Docker Volumes and Bind Mounts: Managing Persistent Data

The Problem: Container Layers Are Ephemeral

Every write to a container's filesystem goes into a writeable layer that disappears when the container is removed. Databases, user uploads, and logs must be stored outside this layer.

Docker has two mechanisms: named volumes and bind mounts.

---

Named Volumes

# Create and use a named volume
docker volume create postgres_data
docker run -d -v postgres_data:/var/lib/postgresql/data postgres:16

# In Compose
services:
  postgres:
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Named volumes are managed by Docker. They live at /var/lib/docker/volumes/ on Linux. They survive docker stop and docker rm. They are removed only by docker volume rm or docker compose down -v.

---

Bind Mounts

# Mount a host directory into the container
docker run -d -v /host/path:/container/path nginx

# Read-only bind mount
docker run -d -v /opt/configs:/etc/app:ro myapp

Bind mounts directly expose a host filesystem path. Changes in the container are immediately visible on the host and vice versa. Use for:

---

Inspecting Volumes

docker volume ls                    # list all volumes
docker volume inspect postgres_data # detailed info including mount point
docker inspect <container> | grep -A10 Mounts   # what's mounted where

---

Backing Up a Named Volume

# Run a temp container, tar the volume contents to the host
docker run --rm   -v postgres_data:/data:ro   -v $(pwd)/backup:/backup   alpine tar -czf /backup/postgres_data.tar.gz -C /data .

---

Restoring a Volume from Backup

docker volume create postgres_data_restored

docker run --rm   -v postgres_data_restored:/data   -v $(pwd)/backup:/backup:ro   alpine tar -xzf /backup/postgres_data.tar.gz -C /data

---

tmpfs Mounts (RAM-backed)

For temporary data that must not persist to disk:

docker run --tmpfs /tmp:rw,noexec,nosuid,size=64m myapp

Useful for session data, temp files, or any data that should vanish on restart and should be fast.

---

Common Mistakes

Problem: data not persisting after docker compose down -v Cause: -v removes named volumes. Use docker compose down (no -v) to preserve data.

Problem: permission errors writing to a bind mount Cause: the process inside the container runs as a user whose UID does not match the file owner on the host. Fix: either match UIDs or use chown on the host path.

# Check the UID the process uses inside the container
docker exec <container> id

# Fix ownership on the host
chown -R 1000:1000 /host/path

Problem: writes to a volume are slow on Mac/Windows Cause: Docker Desktop uses a VM; bind mounts cross the VM boundary. Use named volumes for database data on non-Linux hosts.

Practice in the ShellGenius Docker Labs — the volume-persistence challenge puts you in a scenario where data loss is happening and you must fix it.