Practice brief

Data Persistence Exercises

The Postgres container from Module 8 loses all its data when it is removed. In Module 10 you add a named volume so the database survives container restarts, replacements, and upgrades. After this exercise, stopping and removing the database container no longer means losing your data.

Build from scratch

10.1 Build from scratch
~25-35 min

Add a Named Volume to the Database

Right now, if you run docker rm -f taskflow-db and start a new Postgres container, all the tasks you saved are gone. The data lived inside the container's writable layer, which Docker discards when the container is removed. In this exercise you will create a named volume and attach it to the Postgres data directory. Docker manages the volume separately from the container, so removing and re-creating the container does not touch the data stored in the volume.

Try it yourself first. Open the guided path if you get blocked.

Step 1 — Prove the problem first

docker exec taskflow-db psql -U postgres -d taskflow -c "INSERT INTO tasks (title) VALUES ('test task');"
docker rm -f taskflow-db
docker run -d --name taskflow-db --network taskflow-backend -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=taskflow postgres:16-alpine
docker exec taskflow-db psql -U postgres -d taskflow -c "SELECT * FROM tasks;"

This sequence inserts a row, removes the container, starts a fresh one, then queries for the row. It will not be there. The data lived in the container's writable layer, not in a volume, so it was discarded when the container was removed. If the API does not have a tasks table yet, the INSERT may fail — that is fine, just observe that the SELECT returns nothing after the re-create.

Step 2 — Create the named volume

docker volume create taskflow-data

docker volume create makes a named volume that Docker manages. It is stored on the host at a path Docker controls (usually under /var/lib/docker/volumes) but you never need to know that path. You reference it by name: taskflow-data. Run docker volume ls to confirm it appears.

Step 3 — Remove the database container

docker rm -f taskflow-db

Remove the existing container so you can start a new one with the volume attached. Removing the container does not affect the volume — it exists independently.

Step 4 — Start Postgres with the volume mounted

docker run -d --name taskflow-db --network taskflow-backend -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=taskflow -v taskflow-data:/var/lib/postgresql/data postgres:16-alpine

-v taskflow-data:/var/lib/postgresql/data mounts the named volume at the path where Postgres stores its data files. The format is volumeName:containerPath. Everything Postgres writes to /var/lib/postgresql/data now goes into the volume, not the container's writable layer. This is the exact path Postgres expects — using any other path will not work.

Step 5 — Write data and prove it survives a container removal

docker exec taskflow-db psql -U postgres -d taskflow -c "CREATE TABLE IF NOT EXISTS items (id serial, label text); INSERT INTO items (label) VALUES ('persisted row');"
docker rm -f taskflow-db
docker run -d --name taskflow-db --network taskflow-backend -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=taskflow -v taskflow-data:/var/lib/postgresql/data postgres:16-alpine
docker exec taskflow-db psql -U postgres -d taskflow -c "SELECT * FROM items;"

This time the row survives. The volume was not touched when the container was removed, so the new container picks up the same data files. This is how you upgrade a Postgres container — remove the old one, start a new one with the same -v flag, and the data is still there.

Break-fix

10.2 Break-fix
~15-25 min

The Volume That Does Not Persist Anything

A teammate added a volume to the Postgres container to fix the data loss problem. They are confident it is working because docker volume ls shows the volume. But after removing and re-creating the container, all the data is still gone. They mounted the volume at the wrong path. Reproduce this, figure out what the correct mount path is, and fix it.

docker rm -f taskflow-db
docker volume create taskflow-data
docker run -d --name taskflow-db --network taskflow-backend -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=taskflow -v taskflow-data:/data postgres:16-alpine
docker exec taskflow-db psql -U postgres -d taskflow -c "CREATE TABLE IF NOT EXISTS items (id serial, label text); INSERT INTO items (label) VALUES ('will this survive');"
docker rm -f taskflow-db
docker run -d --name taskflow-db --network taskflow-backend -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=taskflow -v taskflow-data:/data postgres:16-alpine
docker exec taskflow-db psql -U postgres -d taskflow -c "SELECT * FROM items;"
Show investigative hints
  • The volume is working — it is mounted and writable. The problem is that Postgres is not writing its data files to /data. It writes to a specific path that is defined by the Postgres image.
  • Run docker image inspect postgres:16-alpine and look for a PGDATA entry in the Env or Config section. That is the path Postgres uses by default.
  • The volume must be mounted at exactly the path where Postgres stores its data. If it is mounted anywhere else, Postgres ignores it and writes to its own internal path instead.
Show diagnosis and fix rationale

The volume was mounted at /data, but the Postgres image stores database files at its configured PGDATA path, normally /var/lib/postgresql/data. Docker preserved the volume correctly; Postgres simply was not using it. Re-create the database container with taskflow-data mounted at /var/lib/postgresql/data, then repeat the remove and re-create test.