Backups
Three backup paths, each with its own GitHub deploy key (deploy keys are
repo-scoped, not account-wide). Coverage completed 2026-11-01 — all 14 modules'
code and all module databases are now backed up.
1. In-run DB archive
- Source:
/opt/sportstlm/data/sportstlm.db(opened read-only). - Output per run:
inrun-*.sqlite.gz(3 inrun tables, ~1.65 MB) and
fulldb-*.sqlite.gz(whole DB, ~6.9 MB, 37 tables). Row counts + integrity verified
before push. - Schedule:
inrun-db-backup.timer— Mon & Fri 15:00 SGT. - Local keep:
/opt/backups/inrun/<stamp>/, last 12 runs (~100 MB). - GitHub: private
investconfig-spec/sportstlm-db-backups, pushed from
/opt/backups/github/inrun-db. - Cost: ~4 s wall, ~150 MB peak RSS.
2. Code snapshots
/opt/backups/code_backup.py, hourly (code-backup.timer).- Mirror:
/opt/backups/github/code-backups/<module>/— 17 modules:
sportstlm, racing, lottery, ufc, snooker, nba, ops, supervision, backup-scripts, tennis, nfl, nhl, mlb, badminton, livenba, inlive2, local-feeds. - Excludes:
data/,*.db,*.sqlite*,.env*,vps-credentials.txt, media,
node_modules/,.next/,__pycache__/,browser_profile/,{src. - Credential scan every run (OpenAI/Stripe/GitHub/AWS/Slack/Google keys,
private-key blocks, Telegram tokens) — aborts the commit on a hit. - Push target:
investconfig-spec/sportstlm-code-backups.
3. Hub DB snapshots (daily)
/opt/backups/scripts/hub_db_backup.py, daily (hub-db-backup.timer19:30 UTC).- Consistent snapshot of every module DB (SQLite
VACUUM INTOinside the container,
or Pythonsqlite3.backup()read-only for non-node/exited containers), gzipped and
pushed tosportstlm-db-backupsunderhubs/<module>/. - Coverage (as of 2026-11-01):
ufc(staging/archive/ufcstats/previews),snooker
(staging/archive/previews),sportstlm(inlive2 sidecar + aps_staging + logos),
nba(staging/previews/metrics),tennis(lta + tennis),nfl,nhl,mlb
(staging),badminton,livenba,lottery,inlive2. - Retention: 8 snapshots per DB kept locally.
Deliberately not backed up:
racing'sdata/(daily-regenerated scrape JSON,
covered by the staging "observed-only" policy) and the 0-byteapp.db/rsssports.db.
Deploy keys
| Key | Repo |
|---|---|
/root/.ssh/id_ed25519_vps_backup |
sportstlm-db-backups (DB archives) |
/root/.ssh/id_ed25519_vps_code |
sportstlm-code-backups (code snapshots) |
code_backup.py and the supervisor probe both use the _code key for the code repo.
Probing the code repo with the _backup key returns ERROR: Repository not found —
that means wrong key, not a missing repo.
GitHub PAT alternative
/etc/inrun-backup/github.token (0700 dir / 0600 file), installed via
/opt/backups/accept_credential.py --from-file|--from-url.
Retention / deletion policy
Automated deletion only:
- docker image prune -af --filter until=24h (daily 04:22) — images only
- docker builder prune -f (Wed 04:54) — build cache only
- logrotate — docker/caddy/apt logs only
- systemd-tmpfiles — /tmp 10 d, /var/tmp 30 d
- inrun DB archive rotation — keeps 12 archives
Staging datasets (/opt/sportstlm/data, /opt/racing/data, /opt/lottery/data) are
observed only — no timer/heal may purge them (fallback datasets).