A stack is not fixed until it survives being torn down and started again. The last step of any recovery is a full recreate followed by checks you can read, not assumptions.
What each command recreates
| Command | Containers | Image | Named volumes |
|---|---|---|---|
docker compose restart | Same containers, restarted | Not rebuilt | Kept |
docker compose up -d | Recreated where config or image changed | Not rebuilt | Kept |
docker compose up -d --build | Recreated where config or image changed | Rebuilt | Kept |
docker compose down then up -d --build | All new | Rebuilt | Kept |
docker compose down -v | All removed | Not touched | Deleted |
Settings such as restart:, ports: and environment: are fixed when a container is created. Editing compose.yaml changes nothing that is running until the container is recreated.
Record, recreate, compare
text
before recreate after
curl localhost:8000/ ─► docker compose down ─► same count, same rows
docker compose up -d --build new code, new settingsVerification checklist
| Check | Command | Expect |
|---|---|---|
| Both services up, db healthy | docker compose ps | Up … (healthy) for db, 0.0.0.0:8000->5000/tcp for web |
| App answers | curl localhost:8000/health | ok |
| Data survived | curl localhost:8000/signups | The rows from before |
| Writes work | curl -X POST "localhost:8000/signup?name=ops" | Thanks, ops! |
| New code is running | docker compose exec web head -1 app.py | The new version |
| New settings applied | docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' signup-web-1 | The policy in compose.yaml |
| Data is in the named volume | docker inspect -f '{{json .Mounts}}' signup-db-1 | signup_pgdata |
After a full recreate of this stack:
text
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
signup-db-1 postgres:16 "docker-entrypoint.s…" db 4 seconds ago Up 4 seconds (healthy) 5432/tcp
signup-web-1 signup-web "python app.py" web 2 seconds ago Up 2 seconds 0.0.0.0:8000->5000/tcpCREATED a few seconds ago on both rows confirms both containers are new.