FATAL: data directory has wrong ownership

PostgreSQL startup fails: FATAL: data directory has wrong ownership

Database Errors Intermediate 👁 0 views 📅 May 27, 2026

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

  1. Use pg_dump/pg_restore for backups instead of copying the raw data directory. pg_dump creates a portable SQL file, and pg_restore constructs the directory with correct ownership on the target system. No ownership mismatches.
  2. 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/
  1. Never manually chown the data directory to your user even temporarily. If you need to edit a config file inside, use sudo -u postgres or edit the file as root.
  2. 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?