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 Type | Persists After Container Removal? | Best For |
|---|---|---|
| Container writable layer | No | Temporary files, throwaway experiments |
| Volume | Yes | Application data managed by Docker |
| Bind mount | Yes, because data is on the host | Development code, host-provided config, local files |
tmpfs mount | No | Sensitive 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-boxAfter 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-dataRun 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:16The same idea using -v:
docker run -d \
--name postgres-db \
-e POSTGRES_PASSWORD=local_demo_password \
-v app-data:/var/lib/postgresql/data \
postgres:16Both 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/dataIf the container is removed, the named volume remains:
docker rm -f postgres-db
docker volume lsAnother 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 /workspaceThe same idea using -v:
docker run --rm \
-v "$(pwd)":/workspace \
ubuntu:24.04 \
ls /workspaceRead it like this:
host path container path
--------- --------------
current project folder -> /workspaceBind mounts are useful when the host and container both need to see the same files.
| Use Case | Why Bind Mount Fits |
|---|---|
| Local development | Container sees source code changes immediately |
| Config files | Host provides a file to the container |
| One-off file processing | Container works on files from the host |
| Debugging | Inspect 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 /workspaceVolume vs Bind Mount
| Question | Volume | Bind Mount |
|---|---|---|
| Who manages the storage path? | Docker | You choose the host path |
| Good for app data? | Yes | Sometimes, but less portable |
| Good for source code during development? | Usually no | Yes |
| Tied to host directory layout? | Less | Yes |
| Easier to move across environments? | Usually | Less |
| Can container change host files directly? | No direct host path workflow | Yes |
Use this simple rule:
App-owned runtime data -> volume
Host-owned files -> bind mount
Temporary memory data -> tmpfstmpfs 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 Use | Why |
|---|---|
| Temporary secrets | Avoids writing them to image layers or normal container filesystem |
| Short-lived cache | Faster and disposable |
| Runtime scratch files | Removed when the container stops |
Warning:
tmpfsis 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 nginxAnonymous volume:
docker run -d --name app -v /data nginx| Type | Example | Lifecycle |
|---|---|---|
| Named volume | app-data:/data | Easy to reuse and manage by name |
| Anonymous volume | /data | Docker 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-dbLook for the Mounts section:
[
{
"Type": "volume",
"Name": "app-data",
"Destination": "/var/lib/postgresql/data"
}
]For a shorter output:
docker inspect --format '{{json .Mounts}}' postgres-dbRemoving Volumes
Removing a container does not automatically remove a named volume.
docker rm -f postgres-db
docker volume lsRemove a specific volume:
docker volume rm app-dataPrune unused volumes:
docker volume pruneBe careful with broader cleanup commands:
docker system prune --volumesWarning: Volume cleanup can delete application data. Check
docker volume lsanddocker volume inspectbefore 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/dataThe 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
tmpfsfor 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.