Precheck.
Nothing gets wiped until every item ticks and S3 is verified. This is the gate between planning and doing — a forcing function before any irreversible action.
01 · Infisical setup — cloud account, CLI logged in, AWS credentials stored.
02 · Terraform AWS — S3 buckets provisioned, Restic IAM user created.
All aws and restic commands below inject credentials via infisical run --env=prod --.
Every critical artefact is in S3 — and a restore has been spot-checked.
No rebuild work begins until this stage clears. Two buckets must exist (fast + slow). One file must be successfully read back.
- Both S3 buckets accessible and confirmed private
- Vault, runner code, configs, secrets, repos, DBs, compose files all in S3
- Photos in slow bucket (Glacier IR)
- One restore proven by reading a file back
- Total backup size noted in vault
What goes where
12-Item Checklist
- S3 buckets accessible and confirmed private
- Obsidian vault backed up and verified in S3
- Current runner code backed up
- Runner config.yaml and .env backed up (private bucket)
- Gitea repos backed up
- PostgreSQL databases dumped and in S3
- Docker Compose files backed up
- Nginx configs backed up
- Photos in slow bucket (Glacier IR)
- Plex metadata backed up
- Spot-check restore passed (one file read back)
- Total backup size noted in vault
Commands by step
infisical run --env=prod -- aws s3 ls s3://homelab-backup-fast infisical run --env=prod -- aws s3 ls s3://homelab-backup-slow infisical run --env=prod -- aws sts get-caller-identity
# Restic preferred infisical run --env=prod -- restic -r s3:s3.amazonaws.com/homelab-backup-fast \ backup /root/obsidian-vault --tag precheck --tag manual # Or fall back to plain s3 sync infisical run --env=prod -- aws s3 sync /root/obsidian-vault \ s3://homelab-backup-fast/manual-precheck/obsidian-vault/
infisical run --env=prod -- aws s3 sync /opt/runner \ s3://homelab-backup-fast/manual-precheck/runner-current/ infisical run --env=prod -- aws s3 cp /opt/runner/config.yaml \ s3://homelab-backup-fast/manual-precheck/runner-config.yaml infisical run --env=prod -- aws s3 cp /opt/runner/.env \ s3://homelab-backup-fast/manual-precheck/runner-env.txt
# Easiest: Gitea's own dump gitea dump -c /etc/gitea/app.ini --file /tmp/gitea-dump.zip infisical run --env=prod -- aws s3 cp /tmp/gitea-dump.zip \ s3://homelab-backup-fast/manual-precheck/gitea-dump.zip
pg_dumpall -U postgres > /tmp/all-databases.sql infisical run --env=prod -- aws s3 cp /tmp/all-databases.sql \ s3://homelab-backup-fast/manual-precheck/databases/all-databases.sql rm /tmp/all-databases.sql
# Glacier Instant — slow tier infisical run --env=prod -- aws s3 sync /path/to/photos/ \ s3://homelab-backup-slow/manual-precheck/photos/ \ --storage-class GLACIER_IR infisical run --env=prod -- aws s3 sync \ "/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/" \ s3://homelab-backup-fast/manual-precheck/plex-metadata/ \ --exclude "Cache/*" --exclude "Codecs/*"
mkdir /tmp/restore-test infisical run --env=prod -- aws s3 cp \ s3://homelab-backup-fast/manual-precheck/obsidian-vault/HOMELAB-CURRENT-STATE-2026-05-04.md \ /tmp/restore-test/ cat /tmp/restore-test/HOMELAB-CURRENT-STATE-2026-05-04.md rm -rf /tmp/restore-test
# Worst-case rollback to current state infisical run --env=prod -- aws s3 sync s3://homelab-backup-fast/manual-precheck/obsidian-vault/ /root/obsidian-vault/ infisical run --env=prod -- aws s3 sync s3://homelab-backup-fast/manual-precheck/runner-current/ /opt/runner/ infisical run --env=prod -- aws s3 cp s3://homelab-backup-fast/manual-precheck/runner-env.txt /opt/runner/.env systemctl restart obsidian-runner obsidian-runner-web
Things that bite
.env public
Bucket must be private. Verify in console before uploading runner-env.txt.
Photo upload time
Multi-GB libraries take hours. Run in tmux, check size with --summarize.
Restore not tested
A backup with an untested restore is not a backup. Step 11 is non-optional.