Volumes and Persistent Data

intermediate
11 min

Volumes and Persistent Data

Containers are disposable by design: delete one and its writable layer - including every file it created - vanishes. That is perfect for stateless web servers and a disaster for databases. This lesson explains how Docker persists data using volumes and bind mounts, and when to choose each.

The Problem in Thirty Seconds

Try this experiment:

bash
docker run -d --name db postgres:16 # ... the database writes data inside the container ... docker rm -f db # all of that data is now gone forever

Any data written to the container filesystem lives in its temporary writable layer. Removing the container removes the data. Persistence requires storing data outside the container, and Docker offers two main mechanisms.

Named Volumes

A volume is a piece of storage created and managed by Docker itself, stored in a Docker-controlled area of the host filesystem:

bash
docker volume create pgdata docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16

The syntax is -v volume-name:/path/inside/container. Postgres writes to /var/lib/postgresql/data as usual, but those writes land in the pgdata volume. Now the data survives anything that happens to the container:

bash
docker rm -f db docker run -d --name db2 -v pgdata:/var/lib/postgresql/data postgres:16 # db2 starts with all the previous data intact

Docker even creates the volume automatically if it does not exist yet. Manage volumes with:

bash
docker volume ls docker volume inspect pgdata docker volume rm pgdata docker volume prune # remove volumes no container uses

Bind Mounts

A bind mount maps a specific directory on the host into the container. Instead of a volume name, you give an absolute path:

bash
docker run -d -p 8080:80 -v "$(pwd)/site:/usr/share/nginx/html" nginx

Now Nginx serves whatever is in your local site folder, and edits on the host appear in the container instantly. Bind mounts shine in development: mount your source code into the container, and your app reloads on every save without rebuilding the image.

bash
docker run -it --rm -v "$(pwd):/app" -w /app node:20 npm run dev

Volumes vs Bind Mounts: Choosing

  • Volumes are the right choice for data the application owns: database files, uploaded content, caches. Docker manages the location, they work identically on all platforms, and they are easy to back up.
  • Bind mounts are the right choice when you own the files and want two-way visibility: source code during development, config files, host log directories.
  • Avoid bind mounts for production data - they tie you to specific host paths and permissions.

There is a third option, --tmpfs, which keeps data in memory only - useful for scratch space and secrets that should never touch disk.

Read-Only Mounts

Append :ro to make a mount read-only inside the container - a good habit for configuration:

bash
docker run -d -v "$(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro" nginx

Backing Up a Volume

A classic trick uses a temporary container to tar a volume to the host:

bash
docker run --rm -v pgdata:/data -v "$(pwd):/backup" ubuntu tar czf /backup/pgdata.tar.gz -C /data .

The same pattern in reverse restores it. No downtime tooling required.

Key Takeaways

  • Container filesystems are ephemeral; anything important must live in a volume or mount.
  • Named volumes are Docker-managed and ideal for databases and app-owned data.
  • Bind mounts map host directories in and are ideal for live source code in development.
  • Add :ro for configuration files the container should never modify.
  • docker volume prune cleans up, and a throwaway container can back up any volume.

Next up: Docker Networking, where you will connect containers to each other and to the outside world.

Volumes and Persistent Data - Docker | CodeYourCraft | CodeYourCraft