Files
product-fpol/docs/deployment.md
T
khodowany 78b2c383f1
ci / frontend (push) Successful in 9m13s
ci / backend (push) Failing after 6m40s
Game Features
2026-10-04 23:20:06 -05:00

4.6 KiB

Hosting, backup and recovery

The repository contains a deployment configuration; no host has been selected or deployed. HTTP/browser/container tests must pass on a normal host before using it with friends. Real drafting remains data-gated separately.

Build and bootstrap

Keep product-fpol, modulus, noto, and the updated ligo repositories beside each other. Compose uses the parent directory as its build context, excludes caches/secrets, exports those local Conan recipes, and builds a release C++ image with GCC 12. The frontend uses a separate Node build stage. First build requires internet access for base images and dependencies.

cp .env.example .env
# Set FPOL_DOMAIN to a hostname whose DNS points at your host; no scheme/path.
docker compose build
# Bootstrap migrations, the partial catalog, and an administrator before serving.
docker compose run --rm --no-deps app fpol_admin --database /data/fpol.sqlite \
  import /app/data/real/us-2026-10-04-partial.json
docker compose run --rm --no-deps app fpol_admin --database /data/fpol.sqlite create-user YOUR_USERNAME
docker compose up -d

Caddy owns public ports 80/443 and obtains HTTPS certificates. The app's 8080 port is internal only. Both app and worker use UID 10001 and the persistent fpol_data volume. Caddy certificates/config have their own volumes. The entrypoint uses umask 077. Health checks call /api/health; the worker starts after the app is healthy. No demo mode is enabled. Source activation is explicit; an idle ingestion worker invents no data.

Log in, create a league, and share its private invitation link. To reset a password:

docker compose exec app fpol_admin --database /data/fpol.sqlite reset-link USERNAME

Privately give that user https://YOUR_HOST/#reset/TOKEN. The link expires in 30 minutes and works once. Do not paste passwords, invitations, session tokens or reset links into public logs/issues.

Source setup

Use the ingestion guide. Copy reviewed configuration files into the container or mount an operator-controlled directory. Configure, probe, inspect source status and archived payload/mappings, then activate. Production startup never auto-activates sample mappings. Both coverage and the candidate inventory must be complete before real draft start.

Online backup

Use SQLite's online backup API through Noto while the service is running. Choose a new filename each time; the command refuses to overwrite one.

docker compose exec app fpol_admin --database /data/fpol.sqlite \
  backup /data/backups/fpol-2026-10-04.sqlite
docker compose cp app:/data/backups/fpol-2026-10-04.sqlite ./fpol-2026-10-04.sqlite
chmod 600 ./fpol-2026-10-04.sqlite

Store a protected off-host copy. Include the deployed source revisions and source configuration files. The database backup contains accounts, sessions, invites, frozen league data, picks, raw source payloads and observation history. Do not copy only the live SQLite main file while WAL writes are active.

Restore

Stop both writers. Keep the original database and sidecars as a rollback set. Restore to a stopped app container; adapt the filename to your chosen backup. Do not restore a backup over a running database.

docker compose stop app ingest
# The existing container can receive files while stopped.
docker compose cp ./fpol-2026-10-04.sqlite app:/data/restore.sqlite
docker compose run --rm --no-deps --user root app sh -c 'chown 10001:10001 /data/restore.sqlite && chmod 600 /data/restore.sqlite'
# Use the image's unprivileged account, backed by the same volume.
docker compose run --rm --no-deps app sh -c '
  set -eu
  test -f /data/restore.sqlite
  mkdir /data/before-restore
  for name in fpol.sqlite fpol.sqlite-wal fpol.sqlite-shm; do
    if test -f "/data/$name"; then mv "/data/$name" /data/before-restore/; fi
  done
  cp /data/restore.sqlite /data/fpol.sqlite
  chmod 600 /data/fpol.sqlite
  fpol_admin --database /data/fpol.sqlite rules >/dev/null
'
docker compose up -d app ingest

The ownership step affects only the staged backup, so UID 10001 can read it. Verify SQLite integrity on the offline backup (PRAGMA integrity_check), source health, memberships, and the last draft pick. Restored active drafts pause automatically; commissioners resume with a fresh 120-second turn. Restoring an older backup also restores old session/reset state; consider revoking sessions and reset tokens via trusted local SQLite administration after recovery.

Migrations are forward-only. Back up before upgrades; reverting application binaries after a schema change requires restoring a compatible database backup too. Never use docker compose down -v to restart this app: it deletes persistent volumes.