Skip to content
C Codeloom
DevOps

Docker Volumes Explained: Persisting Data the Right Way

Learn how Docker volumes keep your data alive between container restarts. Compare bind mounts, named volumes, and tmpfs, and see when to use each in real projects.

·4 min read · By Codeloom
Beginner 10 min read

What you'll learn

  • Why containers lose data on restart by default
  • The difference between bind mounts and named volumes
  • How to create, inspect, and remove Docker volumes
  • When to use tmpfs mounts for sensitive in-memory data
  • How volumes interact with Docker Compose services

Prerequisites

  • Comfortable with the Linux command line

Containers are designed to be ephemeral. When you stop and remove a container, anything written to its writable layer disappears with it. That is fine for stateless services, but databases, uploaded files, and logs cannot afford that. Docker offers three mechanisms to keep data alive across container lifecycles: bind mounts, named volumes, and tmpfs mounts. Each fits a different job, and choosing well is one of the small skills that separates a tidy Docker setup from a fragile one.

The problem volumes solve

By default, every change a container makes to its filesystem happens inside a thin writable layer on top of the immutable image. The moment you run docker rm, that layer is gone. Even simple things like a Postgres database storing rows in /var/lib/postgresql/data would vanish on every redeploy. Volumes give the container a path that is backed by storage outside the container, so data survives.

Bind mounts: map a host folder

A bind mount points a path inside the container at a specific path on the host. It is the most direct option and the one you reach for during local development.

docker run -d \
  --name web \
  -v "$(pwd)/site:/usr/share/nginx/html:ro" \
  nginx:alpine

The :ro suffix mounts the folder read-only. Edits you make to files in ./site are visible instantly inside the container, which is why bind mounts shine for hot-reload workflows.

The downside is portability. The path /Users/alice/project/site does not exist on a teammate’s Linux box, and it does not exist on a production server. Bind mounts also bypass Docker’s own management, so you cannot list them with docker volume ls.

Named volumes: managed by Docker

A named volume is created and tracked by Docker itself. You give it a name and Docker decides where to put the bytes (usually under /var/lib/docker/volumes). This is what you want for stateful services in production.

docker volume create pgdata
docker run -d \
  --name db \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

You can inspect, back up, or move the volume without caring about its physical location:

docker volume ls
docker volume inspect pgdata
docker volume rm pgdata

Named volumes also work cleanly across machines because the name is the contract, not a host path.

tmpfs mounts: memory only

A tmpfs mount lives entirely in RAM and never touches disk. Use it for short-lived secrets or scratch data you do not want persisted.

docker run --rm \
  --tmpfs /run/secrets:rw,size=64m \
  alpine sh -c "echo hello > /run/secrets/token && cat /run/secrets/token"

When the container stops, the data is gone for good. That property is sometimes exactly what you want.

Using volumes in Docker Compose

Compose makes volumes feel natural because they live next to the services that use them. The top-level volumes: key declares named volumes, and each service mounts what it needs.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data
  api:
    image: my-api:latest
    volumes:
      - ./uploads:/app/uploads

volumes:
  pgdata:

Notice how pgdata is a named volume while ./uploads is a bind mount. Both are valid in the same file.

Backups and migration

Because a named volume is just a directory Docker manages, you can back it up by mounting it into a throwaway container alongside a destination folder:

docker run --rm \
  -v pgdata:/data \
  -v "$(pwd):/backup" \
  alpine tar czf /backup/pgdata.tgz -C /data .

Restoring is the same idea in reverse. This pattern is useful for moving volumes between hosts or snapshotting before a risky upgrade.

Picking the right tool

A simple rule covers most cases. Use bind mounts when you want the host filesystem and the container to share a folder during development. Use named volumes for production state that Docker should manage. Use tmpfs when the data must never hit disk. If you find yourself reaching for bind mounts in production, stop and ask whether a named volume would be safer and more portable.

Cleaning up

Unused volumes can pile up after you delete containers. Prune them carefully, since this is destructive.

docker volume prune

That command removes only volumes not referenced by any container, but it does not ask twice. Read the list it prints before confirming.

Volumes are one of those topics that look small until you lose data the first time. Spend a few minutes on the difference now and your future self will thank you.