omiid
homenotebookai usage

Move Docker volume to bind mount

June 12, 2024 · Updated on August 09, 2026

My PostgreSQL data outgrew the disk on a Hetzner box. The largest configuration I could get was 320 GB, and the database lived inside a Docker named volume that Docker placed on that same disk. To put the data on storage I controlled, I had to convert the named volume to a bind mount and copy the existing data across.

I write short, practical notes like this one. Get the next one by email:

Unsubscribe anytime.

Docker named volumes vs bind mounts: Docker owns one path, you own the other

Both give a container storage that survives the container. The difference is who controls the directory on the host.

Docker volumes: Docker manages the path

Docker volumes are stored within the Docker host’s filesystem and are managed by Docker. Here are some benefits of using Docker volumes:

  • Persistence: Even if the Docker container is deleted, the volume persists, which is crucial for non-temporary data.
  • Safety: Volumes are isolated from the core functionality of the host machine, which means there is less risk of a container impacting the host filesystem.
  • Ease of Backup: Docker volumes can be more easily backed up or migrated than bind mounts.

Bind mounts: you manage the path

A bind mount maps a directory or file on the host into the container. Here are some reasons why bind mounts can be advantageous:

  • Performance: Accessing data via bind mounts can be faster than with Docker volumes because there is no extra abstraction.
  • Control: Users have direct access to the file system and files, providing more control over the files and directories.
  • Flexibility: They allow for real-time data sharing and updates between the host and container.

For a large database on a disk you pick yourself, the bind mount wins. The cost is that you now own the directory permissions, and Postgres is strict about them.

Migrate the Docker named volume to a bind mount

Start from the named volume config

My PostgreSQL service was configured as follows in my Docker Compose file, with a named volume:

services:
  postgres:
    image: postgres:latest
    volumes:
      - postgres-data:/var/lib/postgresql/data
volumes:
  postgres-data:

Create the host directory and match its ownership

The first step was to set up the directory that would be used for the bind mount:

mkdir -p /mnt/data/postgres

Ensuring the ownership of the directory matches the user and group expected by the PostgreSQL container is crucial. To confirm the correct ownership, I executed the following commands:

docker exec -it [postgres_container_id] /bin/sh
~ ls -al /var/lib/postgresql/
~ id postgres

This output confirmed the user ID and group ID:

uid=999(postgres) gid=999(postgres) groups=999(postgres),101(ssl-cert)

Then I changed the user and group ownership of the directory to match Docker volume files.

sudo chown -R 999:999 /mnt/data/postgres

Point the Docker Compose volume entry at the host path

After setting the permissions, I updated the Docker Compose file to use a bind mount. The named volume declaration at the bottom of the file goes away with it:

services:
  postgres:
    image: postgres:latest
    volumes:
      - /mnt/data/postgres:/var/lib/postgresql/data

Copy the data with a throwaway container

The final step involved moving the existing data to the new directory. A one-off Alpine container mounts both the old named volume and the new host path, then copies between them:

docker run --rm -v postgres-data:/from -v /mnt/data/postgres:/to alpine cp -a /from/. /to/.

Once the data was successfully transferred, I restarted the services:

docker-compose down
docker-compose up -d

Wrapping up

Although bind mounts require a bit more initial setup and caution, the performance gains and flexibility in managing the data are well worth it. For anyone running into similar storage constraints with Docker, considering bind mounts might provide a suitable solution.

Join My Newsletter

Occasional notes on software, tools, and things I learn. No spam.

Unsubscribe anytime.

Continue Reading

  • Your RAG is confidently wrong without hybrid searchAug 21, 2026
  • Stop tuning everything. pgvector has three knobs that matter.Aug 18, 2026
  • Your agent's knowledge base is lying to you. Run these 14 checks.Aug 18, 2026
  • Your agent's knowledge base is rotting in 14 waysAug 17, 2026
  • Claude watermarks its text now. Here's how, and what it can't do.Aug 16, 2026