Containers are meant to be replaceable. That is good for deployments, but it creates an important question: where should application data live if containers can be removed and recreated?

Docker has four common storage paths:

Storage TypePersists After Container Removal?Best For
Container writable layerNoTemporary files, throwaway experiments
VolumeYesApplication data managed by Docker
Bind mountYes, because data is on the hostDevelopment code, host-provided config, local files
tmpfs mountNoSensitive or temporary in-memory files on Linux

Mental Model

flowchart TD
    Image["Image layers: read-only"] --> Container["Container writable layer"]
    Container --> Removed["Container removed"]
    Removed --> Lost["Writable layer data is lost"]

    Volume["Docker volume"] --> Mount["Mounted into container"]
    Bind["Host path bind mount"] --> Mount
    Mount --> App["App writes data"]
    App --> Persistent["Data remains outside container lifecycle"]

The image provides the starting filesystem. The container adds a writable layer. Volumes and bind mounts attach storage from outside that writable layer.

container filesystem
 
+----------------------------------+
| /var/lib/postgresql/data         |  -> Docker volume
+----------------------------------+
| /app                             |  -> bind mount from host project
+----------------------------------+
| container writable layer         |  -> removed with container
+----------------------------------+
| image layers                     |  -> read-only base
+----------------------------------+

Key Insight: If data matters, put it in a volume, bind mount, managed database, or external storage. Do not rely on the container writable layer.

Container Writable Layer

Every container gets a writable layer on top of the image.

docker run --name test-box ubuntu:24.04 bash -c "echo hello > /data.txt"
docker rm test-box

After the container is removed, /data.txt is gone because it lived only in that container’s writable layer.

This is fine for:

  • temporary files
  • caches that can be rebuilt
  • short experiments
  • logs that are also shipped elsewhere

It is not fine for:

  • database files
  • user uploads
  • business data
  • anything you need after container replacement

For the image/container relationship, see Docker Image Build Time and Runtime.

Docker Volumes

A Docker volume is storage managed by Docker and mounted into one or more containers.

docker volume create app-data
docker volume ls
docker volume inspect app-data

Run a container with a named volume:

docker run -d \
  --name postgres-db \
  -e POSTGRES_PASSWORD=local_demo_password \
  --mount source=app-data,target=/var/lib/postgresql/data \
  postgres:16

The same idea using -v:

docker run -d \
  --name postgres-db \
  -e POSTGRES_PASSWORD=local_demo_password \
  -v app-data:/var/lib/postgresql/data \
  postgres:16

Both commands mount the app-data volume at /var/lib/postgresql/data inside the container.

The password value above is only a local demo value. For real work, do not hardcode database passwords in commands or Compose files; use an environment file, secret manager, Docker secrets, or the platform’s secret mechanism.

Docker volume: app-data
        |
        v
container path: /var/lib/postgresql/data

If the container is removed, the named volume remains:

docker rm -f postgres-db
docker volume ls

Another container can mount the same volume later.

Tip: Prefer named volumes for container-owned application data. Databases are the common beginner example, but the same idea applies to uploads, queues, and other runtime state.

Bind Mounts

A bind mount connects a specific host file or directory into a container.

docker run --rm \
  --mount type=bind,source="$(pwd)",target=/workspace \
  ubuntu:24.04 \
  ls /workspace

The same idea using -v:

docker run --rm \
  -v "$(pwd)":/workspace \
  ubuntu:24.04 \
  ls /workspace

Read it like this:

host path                 container path
---------                 --------------
current project folder -> /workspace

Bind mounts are useful when the host and container both need to see the same files.

Use CaseWhy Bind Mount Fits
Local developmentContainer sees source code changes immediately
Config filesHost provides a file to the container
One-off file processingContainer works on files from the host
DebuggingInspect or write files directly from the host

Bind mounts are powerful because the container can modify host files. Use read-only mode when the container only needs to read:

docker run --rm \
  --mount type=bind,source="$(pwd)",target=/workspace,readonly \
  ubuntu:24.04 \
  ls /workspace

Volume vs Bind Mount

QuestionVolumeBind Mount
Who manages the storage path?DockerYou choose the host path
Good for app data?YesSometimes, but less portable
Good for source code during development?Usually noYes
Tied to host directory layout?LessYes
Easier to move across environments?UsuallyLess
Can container change host files directly?No direct host path workflowYes

Use this simple rule:

App-owned runtime data -> volume
Host-owned files       -> bind mount
Temporary memory data  -> tmpfs

tmpfs Mounts

A tmpfs mount stores files in host memory and removes them when the container stops.

docker run --rm \
  --mount type=tmpfs,target=/run/cache \
  alpine \
  sh -c "echo temp > /run/cache/file && cat /run/cache/file"

Use tmpfs for temporary data that should not be written into the container writable layer or persisted on disk.

Good UseWhy
Temporary secretsAvoids writing them to image layers or normal container filesystem
Short-lived cacheFaster and disposable
Runtime scratch filesRemoved when the container stops

Warning: tmpfs is not persistent storage. If you need data after the container stops, use a volume, bind mount, or external storage.

Named and Anonymous Volumes

Docker can create named or anonymous volumes.

Named volume:

docker run -d --name app -v app-data:/data nginx

Anonymous volume:

docker run -d --name app -v /data nginx
TypeExampleLifecycle
Named volumeapp-data:/dataEasy to reuse and manage by name
Anonymous volume/dataDocker generates a name; easy to lose track of

For learning and local apps, prefer named volumes unless you deliberately want throwaway storage.

Inspect What Is Mounted

Use docker inspect to see exactly what storage is attached to a container.

docker inspect postgres-db

Look for the Mounts section:

[
  {
    "Type": "volume",
    "Name": "app-data",
    "Destination": "/var/lib/postgresql/data"
  }
]

For a shorter output:

docker inspect --format '{{json .Mounts}}' postgres-db

Removing Volumes

Removing a container does not automatically remove a named volume.

docker rm -f postgres-db
docker volume ls

Remove a specific volume:

docker volume rm app-data

Prune unused volumes:

docker volume prune

Be careful with broader cleanup commands:

docker system prune --volumes

Warning: Volume cleanup can delete application data. Check docker volume ls and docker volume inspect before pruning anything important.

Compose Example

Compose makes named volumes explicit in the application definition.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: local_demo_password
    volumes:
      - db-data:/var/lib/postgresql/data
 
volumes:
  db-data:

Read it like this:

db service
  mounts volume db-data
  at /var/lib/postgresql/data

The container can be recreated, but the db-data volume remains unless you explicitly remove it.

Again, the password value is only for a local example. Do not commit real passwords into Compose files.

TL;DR

  • Data written only to the container writable layer is lost when the container is removed.
  • Use Docker volumes for container-owned persistent data.
  • Use bind mounts when the host needs to provide or edit the files directly.
  • Use tmpfs for temporary in-memory data, not persistence.
  • Prefer named volumes over anonymous volumes for learning and local apps.
  • Removing a container is not the same as removing its volume.
  • Always inspect before pruning volumes.

Resources

Docker storage overview Official overview of volumes, bind mounts, tmpfs mounts, and container writable layers.

Docker volumes Official guide to volume lifecycle, volume mounting, and common volume commands.

Docker bind mounts Official guide to host path mounts and bind mount syntax.

Docker tmpfs mounts Official guide to temporary in-memory mounts.

Compose file volumes Official Compose reference for named volumes.