Lesson 05/11 minutes/2 graded
Volumes and persistence
Where your data goes when a container is removed, and how to keep it.
The writable layer on top of a container's image dies with the container. Write a file, remove the container, and the file is gone. That is deliberate, since it is what makes containers disposable, and it is why your database container lost everything the first time you recreated it.
Anything that must outlive the container goes in a volume.
Named volumes
The image enforces two things here. It exits immediately unless
POSTGRES_PASSWORD is set, and the tag decides where the data lives:
postgres:17 keeps it in /var/lib/postgresql/data, while postgres:18 moved to
/var/lib/postgresql/18/docker with the volume at /var/lib/postgresql. Mount
the path that does not match your tag and Postgres will start normally, write
where nothing is persisted, and lose the lot when the container goes.
Docker creates and manages the volume, so you do not have to pick a host path.
It is independent of any one container and survives docker rm. That said,
moving it to another machine is a separate job: a default volume lives on this Docker host
until you back it up or use a driver backed by shared storage.
docker volume ls
docker volume inspect pgdata
Order matters when you clean up. While any container still references the volume,
docker volume rm refuses and tells you which container is holding it, so remove
the container first. There is no confirmation prompt on the volume removal, and
the data is gone once it returns.
Bind mounts
A bind mount points at a specific directory on the host. The left side of the colon is a path rather than a name:
docker run -v $(pwd)/src:/app/src node:24-slim
Edits on the host appear in the container immediately, which is exactly what you want in development and almost never what you want in production.
| Named volume | Bind mount | |
|---|---|---|
Left side of : | A name | A host path |
| Managed by | Docker | You |
| Portable | Yes | No (the path must exist) |
| Good for | Databases, uploads, caches | Live-reloading source in dev |
| Permissions | Initialised for the image | Host UIDs leak in, and bite |
Compose makes this readable
Once more than one flag is involved, put it in a file:
services:
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: local-only
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "127.0.0.1:5432:5432"
volumes:
pgdata:
docker compose up -d starts it, docker compose down stops it, and (worth
knowing) down leaves the volume alone. docker compose down -v is the one
that deletes your data.