How to fix chmod: changing permissions: Operation not permitted in PostgreSQL

PostgreSQLINTERMEDIATEHIGHUpdated Dec 29, 2025· 481 views

The "chmod: changing permissions: Operation not permitted" error occurs when PostgreSQL fails to change file permissions during initialization, typically in Docker containers or when using NFS volumes with root_squash enabled. Fix by adjusting volume permissions, using Docker named volumes instead of bind mounts, or configuring NFS properly.

Quick fix

Fixes EACCES

# If running Docker, check the container logs:
docker logs <container_name>

# Look for the specific path in the error message:
# chmod: changing permissions of '/var/lib/postgresql/data': Operation not permitted
# or
# chmod: changing permissions of '/var/run/postgresql': Operation not permitted

# If running locally, check the PostgreSQL initialization logs:
sudo -u postgres /usr/lib/postgresql/13/bin/initdb /var/lib/postgresql/13/main 2>&1
  1. Verify the error and identify the problematic directory
  2. Understand the root cause: Docker volumes vs bind mounts
  3. Solution 1: Use Docker named volumes instead of bind mounts
  4. Solution 2: Fix file permissions on the host before using bind mounts
postgresql — error
# If running Docker, check the container logs:
docker logs <container_name>
# Look for the specific path in the error message:
# chmod: changing permissions of '/var/lib/postgresql/data': Operation not permitted
# or
# chmod: changing permissions of '/var/run/postgresql': Operation not permitted
# If running locally, check the PostgreSQL initialization logs:
sudo -u postgres /usr/lib/postgresql/13/bin/initdb /var/lib/postgresql/13/main 2>&1
chmod: changing permissions: Operation not permitted

What this error means

The "chmod: changing permissions: Operation not permitted" error is a filesystem permission issue that occurs when PostgreSQL tries to change directory permissions during database initialization. This typically happens when the postgres process lacks sufficient privileges to modify permissions on database files or directories. PostgreSQL initialization scripts run chmod commands to set correct permissions on the data directory and socket directories (/var/lib/postgresql/data, /var/run/postgresql). When these commands fail with EACCES (Operation not permitted), it signals that the current user cannot modify the permission bits on those directories—usually because they do not own the files. This error is extremely common in containerized environments (Docker, Kubernetes) when: 1. PostgreSQL attempts to chmod directories on bind-mounted host volumes, where file ownership mismatches between host and container 2. NFS volumes are mounted with root_squash enabled, which prevents the root (or postgres) user inside the container from changing permissions 3. The data directory is pre-populated with files owned by a different user 4. Running the postgres container with an arbitrary user that differs from the standard postgres user The error is a safety mechanism—PostgreSQL refuses to start if it cannot set proper permissions. The fix is not to bypass chmod, but to ensure the container and volume are configured so that chmod succeeds.

Typical symptoms

  • PostgreSQL container fails during startup with "chmod: changing permissions of '/var/lib/postgresql/data': Operation not permitted"
  • Docker container with postgres image exits immediately with initialization error
  • Kubernetes pod with PostgreSQL crashes with CrashLoopBackOff status
  • Error appears during `initdb` phase: "could not change permissions of directory"
  • Using Docker with a bind mount to a host directory: postgres initialization fails
  • PostgreSQL on Docker with NFS-backed volumes fails to initialize
  • Error specifically mentions "/var/run/postgresql" or "/var/lib/postgresql/data" paths
  • The error occurs on the first run or after recreating the container/pod

Common causes

  1. Using Docker bind mount instead of a named volume, causing file ownership mismatches between host and container
  2. NFS volume mounted with root_squash enabled (default NFS setting), preventing the postgres user from changing permissions
  3. Pre-existing data directory owned by a different user (uid/gid mismatch)
  4. Running PostgreSQL container with an arbitrary user that differs from the standard postgres user
  5. File permissions on host directory too restrictive for container process to modify
  6. Docker volume permission inheritance issues from host filesystem
  7. Running postgres container without sufficient Linux capabilities (CAP_CHOWN, CAP_DAC_OVERRIDE)
  8. Permissions were modified externally (e.g., via host system chown/chmod) while container was running

How to fix "chmod: changing permissions: Operation not permitted"

1Verify the error and identify the problematic directory

First, examine the full error message to identify which directory is causing the permission issue.

bash
# If running Docker, check the container logs:
docker logs <container_name>

# Look for the specific path in the error message:
# chmod: changing permissions of '/var/lib/postgresql/data': Operation not permitted
# or
# chmod: changing permissions of '/var/run/postgresql': Operation not permitted

# If running locally, check the PostgreSQL initialization logs:
sudo -u postgres /usr/lib/postgresql/13/bin/initdb /var/lib/postgresql/13/main 2>&1

Identify whether the issue is with /var/lib/postgresql/data (data directory) or /var/run/postgresql (socket directory).

2Understand the root cause: Docker volumes vs bind mounts

The most common cause is using Docker bind mounts instead of named volumes. Docker bind mounts can have permission mismatches because they directly reference the host filesystem.

Bind mount (problematic):

bash
docker run -v /host/path:/var/lib/postgresql/data postgres

Named volume (recommended):

bash
docker volume create pgdata
docker run -v pgdata:/var/lib/postgresql/data postgres

Named volumes are managed by Docker and have proper permission handling. If you currently use bind mounts and experience this error, switch to named volumes.

3Solution 1: Use Docker named volumes instead of bind mounts

Replace bind mounts with Docker named volumes, which properly handle file ownership and permissions.

bash
# Create a named volume:
docker volume create postgresql-data

# Update your docker-compose.yml:
version: '3.9'
services:
  postgres:
    image: postgres:15
    volumes:
      - postgresql-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password

volumes:
  postgresql-data:
    driver: local
bash
# Or run directly with Docker:
docker run -d \
  --name postgres \
  -v postgresql-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=password \
  postgres:15

# Start the container:
docker-compose up -d

Named volumes are persistent and properly managed by Docker, eliminating permission issues.

4Solution 2: Fix file permissions on the host before using bind mounts

If you must use bind mounts, ensure the host directory has correct ownership before starting the container.

bash
# Create the directory on the host with correct ownership:
sudo mkdir -p /data/postgresql
sudo chown -R 999:999 /data/postgresql
sudo chmod -R 700 /data/postgresql

# Note: uid 999 is the postgres user in the official postgres Docker image
# Verify the postgres user's uid in the image:
docker run --rm postgres id postgres

# Now run the container with the bind mount:
docker run -d \
  --name postgres \
  -v /data/postgresql:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=password \
  postgres:15

The postgres user inside the container (uid 999) must own the host directory. If the uid differs, adjust the chown command accordingly.

5Solution 3: Configure NFS volumes without root_squash

If using NFS storage (common in Kubernetes), the root_squash setting prevents permission changes. Update the NFS export configuration.

On the NFS server, edit /etc/exports:

bash
/exported/path *(rw,sync,no_root_squash,no_all_squash)

Or use IP-specific rules:

bash
/exported/path 192.168.1.0/24(rw,sync,no_root_squash)

Then apply the changes:

bash
sudo exportfs -ra

WARNING: no_root_squash allows the root user inside containers to act as root on the NFS share. Use cautiously and only for trusted networks. As an alternative, see the next solution.

6Solution 4: Use initdb pre-creation to avoid runtime chmod

For Kubernetes, use an init container to pre-initialize the data directory with correct permissions before the main PostgreSQL container starts.

yaml
apiVersion: v1
kind: Pod
metadata:
  name: postgres
spec:
  initContainers:
  - name: init-postgres-dir
    image: postgres:15
    command:
    - /bin/sh
    - -c
    - |
      mkdir -p /var/lib/postgresql/data
      chmod 700 /var/lib/postgresql/data
    volumeMounts:
    - name: pgdata
      mountPath: /var/lib/postgresql/data
  containers:
  - name: postgres
    image: postgres:15
    volumeMounts:
    - name: pgdata
      mountPath: /var/lib/postgresql/data
    env:
    - name: POSTGRES_PASSWORD
      value: "password"
  volumes:
  - name: pgdata
    persistentVolumeClaim:
      claimName: pgdata-pvc

The init container runs with appropriate permissions (or with no_root_squash temporarily) to set up directories before PostgreSQL starts.

7Solution 5: Clean up and restart if data directory is corrupted

If the data directory is in a partially initialized state, clean it up and restart.

bash
# Stop the container:
docker stop <container_name>

# Remove the container:
docker rm <container_name>

# Delete the volume (WARNING: This deletes data!):
docker volume rm postgresql-data

# Create a fresh volume:
docker volume create postgresql-data

# Run the container again:
docker run -d \
  --name postgres \
  -v postgresql-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=password \
  postgres:15

For bind mounts:

bash
# Delete the directory content:
sudo rm -rf /data/postgresql/*

# Ensure correct permissions:
sudo chown -R 999:999 /data/postgresql
sudo chmod -R 700 /data/postgresql

# Restart the container:
docker-compose up -d

A fresh initialization avoids corruption from partial/failed attempts.

8Verify PostgreSQL started successfully

After applying a fix, verify the PostgreSQL container is running and accessible.

bash
# Check container status:
docker ps | grep postgres

# View logs for errors:
docker logs <container_name> | tail -20

# Connect to PostgreSQL from the host:
psql -h localhost -U postgres -c "SELECT version();"

# If no host tools available, exec into the container:
docker exec -it <container_name> psql -U postgres -c "SELECT version();"

Verify that initialization completed without "Operation not permitted" errors and the database is accepting connections.

Advanced notes

The chmod operation is critical to PostgreSQL security. The /var/lib/postgresql/data directory must have permissions 700 (rwx------) so only the postgres user can access database files. Similarly, /var/run/postgresql must be 775 (rwxrwxr-x) for socket access. These permissions prevent unauthorized users from reading database files directly from disk.

When using Kubernetes with StatefulSets, use PersistentVolumes backed by local storage or properly configured network storage (NFS, EBS) instead of emptyDir volumes. StatefulSets combined with named volumes provide reliable persistence and permission handling.

Docker Compose simplifies volume management. Always use the volumes: section with named volumes rather than raw host paths in production setups.

For development, you can work around this by running the postgres container as root temporarily (add --user root to the docker run command), but never do this in production—it violates principle of least privilege.

Linux AppArmor or SELinux policies can also interfere with chmod operations. If you have SELinux enabled (check with getenforce), you may need to adjust policies or disable SELinux for the volume mount using the :Z option in Docker: -v pgdata:/var/lib/postgresql/data:Z.

When migrating PostgreSQL from non-containerized to containerized deployments, test volume setup thoroughly on a staging environment first. File permission issues can silently corrupt a database if left unchecked.

Frequently asked questions

What does "EACCES" mean?
The "chmod: changing permissions: Operation not permitted" error is a filesystem permission issue that occurs when PostgreSQL tries to change directory permissions during database initialization. This typically happens when the postgres process lacks sufficient privileges to modify permissions on database files or directories. PostgreSQL initialization scripts run chmod commands to set correct permissions on the data directory and socket directories (/var/lib/postgresql/data, /var/run/postgresql).
How do I fix "EACCES"?
The "chmod: changing permissions: Operation not permitted" error occurs when PostgreSQL fails to change file permissions during initialization, typically in Docker containers or when using NFS volumes with root_squash enabled. Fix by adjusting volume permissions, using Docker named volumes instead of bind mounts, or configuring NFS properly.
What causes "EACCES"?
Using Docker bind mount instead of a named volume, causing file ownership mismatches between host and container NFS volume mounted with root_squash enabled (default NFS setting), preventing the postgres user from changing permissions Pre-existing data directory owned by a different user (uid/gid mismatch)
Is the "EACCES" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading