PostgreSQL startup fails: FATAL: data directory has wrong ownership
PostgreSQL refuses to start when it can't write to its data directory. The fix: check ownership and permissions. This usually happens after restoring backups or moving data between systems.
Quick answer for pros
Run chown -R postgres:postgres /var/lib/postgresql/16/main/ as root, then verify with ls -ld /var/lib/postgresql/16/main/ — it should show drwx------ 3 postgres postgres. Restart the service.
What's actually happening here
PostgreSQL is picky about its data directory. When you install it, the postgres system user owns the data directory. The postmaster process runs as that user and needs exclusive write access. If you restore a backup with tar or cp -a from another machine, or migrate data by copying the directory manually, you often bring along the original owner's UID/GID. That UID might not match the postgres user on the new system. Or you might have accidentally chown'd the directory to your own user while debugging. Either way, PostgreSQL checks ownership at startup and bails immediately if it's wrong — no fallback, no warning, just a hard stop.
The error message itself tells you exactly which directory is wrong. It'll say something like: FATAL: data directory "/var/lib/postgresql/16/main" has wrong ownership. The path is key. I've seen people waste time checking PostgreSQL configuration files when the fix is literally one chown command.
How to fix it
Step 1: Identify your PostgreSQL data directory
If you don't know it from the error message, run this as root or via sudo:
sudo -u postgres psql -c 'SHOW data_directory;'
That'll fail if PostgreSQL isn't running. Instead, check the systemd service file or pg_lsclusters output:
pg_lsclusters
That shows you cluster name, version, port, status, and data directory. The status column will show "down" with our error.
Step 2: Check current ownership
ls -ld /var/lib/postgresql/16/main/
Look at the third and fourth columns. They should be postgres postgres. If you see anything else — your username, root, nobody — that's the problem.
Step 3: Fix ownership recursively
sudo chown -R postgres:postgres /var/lib/postgresql/16/main/
The -R flag is crucial because subdirectories inside (like base/, pg_wal/, global/) inherit the wrong owner too. Without recursion, PostgreSQL will hit a permission error on the first subdirectory it tries to access.
Step 4: Verify permissions, not just ownership
PostgreSQL also requires the data directory to have 0700 permissions — owner-only read/write/execute. Check with:
ls -ld /var/lib/postgresql/16/main/
If the permissions aren't drwx------, fix them:
sudo chmod 0700 /var/lib/postgresql/16/main/
Don't use chmod -R on the whole directory. Internal files have specific permissions (some are 0600, some are 0640). Only the top-level directory needs 0700.
Step 5: Restart PostgreSQL
sudo systemctl restart postgresql@16-main
Replace 16-main with your actual version and cluster name. Check status:
sudo systemctl status postgresql@16-main
If you still see the same error, read on.
Alternative fixes when chown doesn't work
Scenario 1: The data directory is a symlink
Sometimes people symlink the data directory to a different mount point. chown -R on the symlink doesn't change the target's ownership. You need to follow the link:
readlink -f /var/lib/postgresql/16/main/
Then run chown on the real path.
Scenario 2: AppArmor or SELinux blocking access
On Ubuntu with AppArmor, or CentOS/RHEL with SELinux, even correct ownership won't help if the security context is wrong. Check dmesg or journalctl -xe for denials. Quick test: temporarily disable SELinux with setenforce 0. If PostgreSQL starts, you need to fix the context:
sudo restorecon -R /var/lib/postgresql/16/main/
Scenario 3: Wrong PostgreSQL version path
If you upgraded PostgreSQL but kept the old data directory, the path might be different. Check /etc/postgresql/*/main/postgresql.conf and look for data_directory. If it points to a non-existent path, create it with correct ownership:
sudo mkdir -p /var/lib/postgresql/16/main/
sudo chown postgres:postgres /var/lib/postgresql/16/main/
sudo chmod 0700 /var/lib/postgresql/16/main/
Preventing this from happening again
- Use pg_dump/pg_restore for backups instead of copying the raw data directory.
pg_dumpcreates a portable SQL file, andpg_restoreconstructs the directory with correct ownership on the target system. No ownership mismatches. - If you must copy the data directory, use rsync with
--chown=postgres:postgres. That forces ownership on the destination:
rsync -av --chown=postgres:postgres /old/path/ /var/lib/postgresql/16/main/
- Never manually chown the data directory to your user even temporarily. If you need to edit a config file inside, use
sudo -u postgresor edit the file as root. - Set up a cron job that checks ownership if you're paranoid:
@daily test $(stat -c '%U:%G' /var/lib/postgresql/16/main/) = "postgres:postgres" || echo "Data directory ownership wrong"
The real lesson here: PostgreSQL's ownership check isn't arbitrary. It's a security measure preventing unprivileged users from reading or corrupting the database files. Don't fight it — respect the check and fix the ownership.
Was this solution helpful?