1.3Volumes: persisting data beyond containers
The essentials in 30 seconds
- The container is ephemeral: everything it writes inside itself dies with it (rm = all lost) — for a database, that is unacceptable: store outside the container.
- Named for data, bind for code: -v pgdata:/var/lib/postgresql/data (Docker-managed volume, portable) for data; -v ./site:/usr/share/nginx/html (machine folder) for code in dev.
- The test that saves: delete the container, relaunch on the same volume — the data must be there; docker volume ls inventories, rm deletes (final).
Running mini-case — Idir (almost) loses a database
Idir launched the demo database with no volume « to go fast ». In the evening, he deletes the container to start clean — and realizes the test data entered in the afternoon vanished with it. The next day, he relaunches with -v pgdata:/var/lib/postgresql/data, recreates the data, deletes the container, relaunches on the same volume: everything is there. He turned a scare into a reflex: data = named volume, verified by an rm + relaunch test before every serious start.
Continuation of cases 1.1-1.2 — Idir moves from throwaway containers to surviving data.
1. The tragedy of the ephemeral container
By default, everything a container writes inside itself dies with it (rm = all lost). For a database, that is unacceptable. The solution: store outside the container, in a Docker-managed volume or a mounted folder of the machine.
Beware the false friend: stop erases nothing, rm erases everything not mounted. Believing the reverse is the most classic level-1 data loss.
2. Named volume vs bind mount
The named volume (-v pgdata:/var/lib/postgresql/data) is stored by Docker, portable and backup-friendly: the choice for data. The bind mount (-v ./site:/usr/share/nginx/html) links a folder of your machine: the choice for code in development (edit a file, the container sees it at once).
The trap is the wrong internal path: mounting next to the directory where the application really writes means persisting emptiness. Check the path in the image documentation.
3. The reflex that saves: testing deletion
The test of truth: delete the container, relaunch one on the same volume — the data must be there. docker volume ls inventories, docker volume rm deletes (careful, final). Orphan volumes are purged with prune, with caution.
Idir made it a ritual: no data service goes on demo without passing the rm + relaunch test.
🔎 Concrete example — 🔎 Concrete example — Postgres surviving rm
Idir's database, before and after: with a volume, nothing is lost.
docker run -d --name bdd -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql/data postgres:16.4
# ... create some data ...
docker rm -f bdd
docker run -d --name bdd2 -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql/data postgres:16.4
# the data is still there
Reading: the pgdata volume survived the container deletion.
Lesson: without -v, the same sequence would have erased everything.
Section vocabulary
- Named volume
- Storage space managed by Docker, persistent outside containers, portable and backup-friendly.
Simple example: pgdata keeps Idir's database between bdd and bdd2.
What it is not: a file written in the container with no mount.
Reflex: data = named volume.
- Bind mount
- A machine folder mounted as-is in the container; ideal for code in development.
Simple example: -v ./site:/usr/share/nginx/html: Idir edits, the container sees it at once.
What it is not: portable storage for a production database.
Reflex: dev code = bind, prod data = named volume.
- Mount point
- The internal path where the volume appears in the container; it must be where the application writes.
Simple example: /var/lib/postgresql/data for postgres:16.4.
What it is not: some random folder picked by chance.
Reflex: check the path in the image docs.
- prune
- Cleaning unused resources, orphan volumes included — final deletion.
Simple example: Idir purges volumes from his throwaway tries, never pgdata.
What it is not: a trash bin you can restore from.
Reflex: check the list before any prune.
In practice — In practice — Proving persistence
- Launch postgres WITH a named volume and create a table.
- Delete the container (rm -f) without touching the volume.
- Relaunch a container on the same volume and check the table.
- Redo the try WITHOUT a volume: observe the loss.
Script ready to say — securing a database (30 s)
« My data goes in a named volume: -v pgdata:/var/lib/postgresql/data, at the documented path of the image. I prove it: I delete the container, I relaunch on the same volume, the data is there. Without a volume, the same sequence erases everything. »
Say while running the rm + relaunch live; show the table still present.
Copy-paste template — persistent database
docker run -d --name bdd \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16.4
# Proof: docker rm -f bdd, relaunch the same with --name bdd2, verify.
# Never: docker volume rm pgdata (final) nor prune without checking.
Idir's database on demo
As above: pgdata named like the service, rm + relaunch test passed before showing anything to the manager.
Website in dev
-v ./site:/usr/share/nginx/html: Idir edits his pages on his machine, the Nginx container serves them at once — bind mount, not named volume.
Recovery after a scare
Database launched with no volume by mistake: create the volume, relaunch with -v, re-enter — and engrave the « data = named volume » reflex.
Cleaning up tries
docker volume ls to inventory, targeted rm for test volumes, prune only after checking — never on pgdata.
✓ Best practices
- Data = named volume, dev code = bind mount.
- Test rm + relaunch before believing.
- Name volumes like services (pgdata).
✗ Common pitfalls
- Believing stop erases (no) and rm keeps (no).
- Mounting at the wrong internal path (data next door).
- Purging volumes (prune) without checking.
Key takeaways
- Without a volume, rm = total loss.
- Named volume for data, bind for code.
- The rm + relaunch test proves persistence.
- prune with caution: final deletion.
48h challenge — apply tomorrow
Launch postgres with a named volume, create a table, do rm -f + relaunch and check. Then redo without a volume: observe the loss — and never forget it.
- I mount a named volume for any data that matters.
- I tell named volume (data) from bind mount (dev code).
- I prove persistence via rm + relaunch before serious use.
Frequently asked questions
Where are volumes on my disk?
In Docker's space (docker volume inspect pgdata gives the path). We do not handle them by hand: we go through containers or backups.
Bind mount or volume for a prod database?
Named volume: portable, backup-friendly, independent of the machine's tree. Bind is reserved for dev and config files.
A postgres container with no volume is deleted (rm). What happens to its data?