[E00-S02-T04] DB volume persists across restart/recreate #385
No Reviewers
Labels
Clear labels
agent/analyst-drafted
agent/analyst-drafted
needs/human-decision
needs/human-decision
needs/security-review
needs/security-review
tier/t0
tier/t1
tier/t2
tier/t3
kind
bug
kind
bug
kind
epic
kind
epic
kind
initiative
EPPP programme initiative
kind
story
kind
story
kind
task
EPPP engineering card/task decomposed from a story
kind
toil
kind
toil
loop
1
loop
1
loop
2
loop
2
loop
3
loop
3
risk
agent-full
risk
agent-full
risk
human-gated
risk
human-gated
risk
human-only
risk
human-only
size
l
size
l
size
m
size
m
size
s
size
s
status
blocked
status
blocked
status
done
Workflow: Done
status
in-progress
status
in-progress
status
proposed
status
proposed
status
ready
status
ready
status
review
status
review
stream
checkout
stream
checkout
stream
onboarding
stream
onboarding
stream
platform
stream
platform
trivial — implementer only, auto-merge
standard — implementer + reviewer + tester
complex — security if triggered, human merge
critical — full chain + security, human merge
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Fabrika/PersonalBlog#385
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What changed
Implements the [E00-S02-T04] DB volume persistence (#171) on top of the [E00-S02-T01/T02/T03] Compose baseline (#59): the database now survives
docker compose restartanddocker compose down+docker compose up -d(recreate).compose.yaml— thedbservice now mounts the named volumedb-dataat PostgreSQL's data directory (/var/lib/postgresql/data), and the file declares the volume in a top-levelvolumes:map. Named volumes survivedocker compose down(which only removes containers and anonymous volumes), so the database files persist across container recreate;docker compose down -vresets the data (the issue's rollback note). Header comment updated: T04 is in scope, T05 (non-root) / T06 (read-only root fs) remain out of scope.apps/server/Dockerfile— header comment doc-accuracy only: T04 is a Compose-level concern (the image itself is unchanged); T05/T06 remain later tasks.tests/compose-config.test.mjs— locks in both acceptance criteria: a static assertion that thedbservice mountsdb-data:/var/lib/postgresql/dataand the top-levelvolumes:map declaresdb-data(both halves are required — removing either breaks the criterion), non-vacuous mutation probes, and a Docker-gated real-stack probe that writes a fixture row through the runningdbservice, then asserts it is still queryable afterdocker compose restart(acceptance 1) and afterdocker compose down→docker compose up -d(acceptance 2, containers recreated from scratch — only the named volume can carry the data across). The probe cleans up withdocker compose down -vso runs stay isolated.Explicitly out of scope per the brief, not touched: app health endpoint (E00-S02-T03 — unchanged behavior), non-root execution (E00-S02-T05), read-only root filesystem (E00-S02-T06).
Criterion → test table
tests/compose-config.test.mjs— "a database fixture survives docker compose restart and recreate (named db-data volume)" (Docker-gated): afterdocker compose up -d+ fixture row,docker compose restartrestarts the containers in place and the probe asserts the fixture row is still queryable (SELECT count(*)→ 1). Static: "the database volume persists across restart and recreate (db mounts the named db-data volume)" requires thedbservice volume mount; mutation probes removing the mount / thedb-datadeclaration / changing the mount target all failtests/compose-config.test.mjs— same real-stack probe:docker compose down(without-v, so named volumes survive) removes the containers,docker compose up -drecreates them from scratch, and the probe asserts the fixture row is still queryable — the container filesystem is discarded on recreate, so only the nameddb-datavolume can carry the data. Static: the top-levelvolumes:map must declaredb-data(the mount target must exist and survive recreate)Test plan executed
node --test tests/compose-config.test.mjs→ 17/17 pass, 3 skipped (the three Docker-gated probes —docker compose config, the up/down smoke probe, and the new persistence probe — skip cleanly where no Docker daemon exists; this sandbox has no Docker) ✓node --test tests/*.test.mjs(full suite) → 63 pass / 3 skipped / 12 fail, and the identical 12 failures exist on cleanmainin this sandbox (frozen-install / root-commands / strict-tsconfig / node-engine suites needpnpm install+ Node 24, which this environment lacks — nonode_modules, Node 22.23.2 here). My branch adds +4 passing tests and +1 Docker-gated skip, zero new failures (verified viagit stashbaseline comparison) ✓compose.yamlparses with the suite's committed YAML subset parser and passes the static + mutation probes; the real-stack probe is exactly the acceptance sequence (fixture → restart → verify → down → up → verify) and is designed to run on CI/dev machines with Docker.Risks / notes
eppp/eppp), the same ones the app'sDATABASE_URLalready hardcodes incompose.yaml— consistent with the existing T01/T02/T03 probes./var/lib/postgresql/datais the officialpostgres:18-bookwormimage'sPGDATA(it declaresVOLUMEthere), so the named volume replaces the image's anonymous volume and actually holds the database files.skipwithout a daemon, so the suite stays green everywhere while giving real container-level validation where Docker exists (same pattern as T01/T02/T03).Refs #171