Testing a backup
A backup nobody has restored is a hypothesis. Testing it does not touch your instance, but it cannot
be a second docker compose up either: the compose file pins its project name, so a second one from
it recreates the stack you are running rather than standing a copy beside it. Build the image under
a name of your own and run the pair by hand.
Standing a copy beside your instance
Section titled “Standing a copy beside your instance”-
A network and a database that are nobody else’s.
Terminal window docker network create badlen-restore-testdocker run -d --name badlen-restore-db --network badlen-restore-test \-e POSTGRES_USER=rt -e POSTGRES_PASSWORD=rt -e POSTGRES_DB=rt \-v badlen-restore-test-pgdata:/var/lib/postgresql postgres:18-alpine -
The application, on a port nothing else uses. It applies the migrations itself before it serves, so there is no separate schema step.
Terminal window docker build -t badlen-restore-test -f Dockerfile.api --target runtime .docker run -d --name badlen-restore-app --network badlen-restore-test -p 8099:3001 \-e DATABASE_URL='postgresql://rt:rt@badlen-restore-db:5432/rt?schema=public' \-e JWT_ACCESS_SECRET="$(openssl rand -hex 32)" \-e SECRET_ENCRYPTION_KEY="$(openssl rand -base64 32)" \-e WEB_ORIGIN=http://localhost:8099 \badlen-restore-test -
Open
http://localhost:8099, sign up with anything, import your file. -
Take it all down. Nothing here outlives the drill, the image included: it is a copy of your instance close to 800 MB, and it is the one piece that stays behind unnoticed because nothing lists it next to the containers.
Terminal window docker rm -f badlen-restore-app badlen-restore-dbdocker volume rm badlen-restore-test-pgdatadocker network rm badlen-restore-testdocker rmi badlen-restore-test
The two secrets on that run
Section titled “The two secrets on that run”Both secrets on that docker run are generated rather than typed, and the access one especially:
the boot check refuses a JWT_ACCESS_SECRET shorter than 16 characters, and a container it refuses
exits at once, after applying the migrations. Nothing then listens on 8099, so the browser you were
told to open answers nothing at all, and the refusal is only in docker logs badlen-restore-app,
where it names the variable.
The encryption key on the line under it is a new one every time, and that is the point of the drill rather than a shortcut: a sealed file opens here, on an instance that has never seen the key of the one that wrote it.
The three figures
Section titled “The three figures”Compare against the real instance: the net worth, the number of operations on your busiest account, and one line’s cost basis. Those three catch a restore that came back short.
The repository does the same thing under assertion rather than by eye:
apps/api/src/data-io/roundtrip.live.test.ts fills an account, exports it, has its volume deleted,
restores into an empty database and compares the two column by column. Its header carries the exact
commands. It needs a database it may destroy, so it runs only when LIVE_DB_URL names one; without
that variable it skips.