build(train): Train's server and BRouter deployable beside the instance (ADR 0030, D46)

deploy/compose.train.yaml adds Train's server at train.halkyon.eu, a
member of Learn's register, with its database halkyon_train in the
instance's PostgreSQL, and BRouter beside it alone (v1.7.10, checked by
SHA-256, no log), its segments of the operator's region kept current
monthly. Nothing changes until deploy/.env names the compose file:
redeploy.sh and backup.sh take Train in only then, and the operator's
steps are in sidecars/README.md. restic now forgets snapshots grouped by
host alone, so adding Train's dump leaves no group behind for good.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-29 00:05:39 +01:00
commit a8f7c26e69
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -4,7 +4,8 @@
# how it was set up and how to restore.
#
# Everything people keep is in the database, their files too (Serverpod's database
# cloud storage), but for the releases of published studysets, which are git
# cloud storage), and in Train's where .env switches Train on (compose.train.yaml,
# Halkyon Train's ADR 0030, D46), but for the releases of published studysets, which are git
# repositories in the server's volume: a dump and a bundle of each repository cover
# the instance; the secrets in deploy/.env are kept in Proton Pass instead. restic stores the dump encrypted in a Hetzner Storage Box
# and keeps 24 hourly, 7 daily, 4 weekly and 3 monthly snapshots (decided 2026-10-03).
@ -35,6 +36,11 @@ cd "$(dirname "$0")"
# the last hour and stores it only once; restic compresses what it stores.
mkdir -p -m 700 "$HOME/backups"
dump="$HOME/backups/latest.dump"
train_dump="$HOME/backups/latest-train.dump"
train=
if docker compose config --services | grep -qx train-server; then
train=yes
fi
(
umask 077
# Never during a deploy (redeploy.sh holds this lock): its migrations would wait
@ -42,8 +48,14 @@ dump="$HOME/backups/latest.dump"
exec 9>"$HOME/.redeploy.lock"
flock 9
docker compose exec -T postgres pg_dump -U halkyon_learn -Fc -Z 0 halkyon_learn >"$dump.partial"
if [ -n "$train" ]; then
docker compose exec -T postgres pg_dump -U halkyon_learn -Fc -Z 0 halkyon_train >"$train_dump.partial"
fi
)
mv "$dump.partial" "$dump"
if [ -n "$train" ]; then
mv "$train_dump.partial" "$train_dump"
fi
# Each repository as a git bundle, after the dump: a release the dump lacks is never
# numbered again (the server numbers past the highest tag), while one the bundles
@ -67,8 +79,15 @@ releases="$HOME/backups/repositories.tar"
)
mv "$releases.partial" "$releases"
restic backup --quiet "$dump" "$releases"
restic forget --quiet --keep-hourly 24 --keep-daily 7 --keep-weekly 4 --keep-monthly 3
if [ -n "$train" ]; then
restic backup --quiet "$dump" "$train_dump" "$releases"
else
restic backup --quiet "$dump" "$releases"
fi
# By host alone: restic groups by the paths backed up too, so a path added (Train's
# dump) would leave the snapshots before it a group of their own, never forgotten, and
# a deleted account in them for good.
restic forget --quiet --group-by host --keep-hourly 24 --keep-daily 7 --keep-weekly 4 --keep-monthly 3
# Once a day, in the run at 03:00: free the space of forgotten snapshots, and read the
# repository's structure; on Sundays also a tenth of the stored data, chosen at random,