cfgPath:=fs.String("config","/etc/felis/felis.toml","path to felis.toml")
label:=fs.String("label",dbbackup.LabelManual,"bundle label; daily/pre-migrate/pre-restore bundles are pruned, manual ones never")
keep:=fs.Int("keep",-1,"bundles of this label to keep (default: daily 14, pre-migrate 10, pre-restore 5, manual all)")
keep:=fs.Int("keep",-1,"bundles of this label to keep (default: daily 14, pre-migrate 10, pre-restore 5, offsite 1, manual all)")
stateDir:=fs.String("state-dir",dbbackup.DefaultStateDir,`host state directory to bundle ("" for none)`)
noServers:=fs.Bool("no-servers",false,"leave the MinecraftServer objects out of the bundle")
metrics:=fs.String("metrics-file","","node-exporter textfile to rewrite on success (e.g. /var/lib/node_exporter/textfile_collector/felis_db_backup.prom)")
archiveDir:=fs.String("archive-dir","","host directory of the world archive volume (default: resolved from the backup PVC through the cluster)")
backupPVC:=fs.String("backup-pvc","felis-backups",`the world archive PVC, in the [k8s] namespace ("" when backups are off)`)
dbDir:=fs.String("db-dir",dbbackup.DefaultDir,`database bundle directory ("" copies no bundles)`)
stateDir:=fs.String("state-dir",dbbackup.DefaultStateDir,`host state directory bundled into the database bundle taken after archives are copied ("" for none)`)
registry:=fs.String("registry","",`host[:port] of the registry whose user images are copied (default: the in-cluster registry's loopback hostPort; "off" copies none)`)
uploadsDir:=fs.String("uploads-dir","","host directory of the submission uploads volume (default: resolved from the uploads PVC through the cluster)")
uploadsPVC:=fs.String("uploads-pvc",platform.UploadsPVCName,`the submission uploads PVC, in the control-plane namespace ("" copies no uploads)`)
`FELIS_DB_BACKUP_TIME`, `FELIS_DB_BACKUP_METRICS` and
@@ -2175,10 +2175,10 @@ off-site bucket (next sections) plus a fresh install. What the host holds:
| Data | On the host | In the bucket | Brought back by | Lost at most |
|---|---|---|---|---|
| Control-plane database (accounts, passkeys, ownership, quotas, audit, submissions, the `world_backups` index) | felis-postgres, `/var/lib/felis/postgres` | every bundle, copied within the hour of being written | `fetch-db`, `db restore` | changes since the newest bundle: up to a day plus an hour with the daily timer |
| Control-plane database (accounts, passkeys, ownership, quotas, audit, submissions, the `world_backups` index) | felis-postgres, `/var/lib/felis/postgres` | every bundle, copied within the hour of being written, plus a fresh one after every pass that copied a world archive | `fetch-db`, `db restore` | changes since the newest bundle: up to a day plus an hour with the daily timer |
| Host state (`/etc/felis`: secrets, both `felis.toml` copies, `offsite.env`, the mail relay password and uploads bucket keys, panel TLS pair) | `/etc/felis` | inside every bundle | `tar -x` of the bundle's `state/` | as the database |
| MinecraftServer objects | k3s | inside every bundle (`k8s/minecraftservers.json`) | `kubectl apply` | as the database |
| World archives (reaper, "Back up now", pre-restore snapshots) | `felis-backups` volume | each one within the hour | `fetch-worlds` | archives written in the last hour |
| World archives (reaper, "Back up now", pre-restore snapshots) | `felis-backups` volume | each one within the hour, followed by a bundle listing it | `fetch-worlds` | archives written in the last hour |
| Live worlds | `world-*` volumes under `/var/lib/rancher/k3s/storage` | **only as their archives** | a restore from the newest archive (§10) | everything since that world's newest archive |
| User images | `registry` volume | hourly; image lists kept 14 days | `fetch-images` | images pushed in the last hour |
| Submission uploads (modpacks awaiting or past review) | `felis-uploads` volume | hourly; upload lists kept 14 days | `fetch-uploads` | uploads of the last hour |
@@ -2313,7 +2313,9 @@ host yourself, plus the off-site encryption key if the copy is in the bucket.
and the volume lacks, provisioning the `felis-backups` volume first if
nothing has used it yet (a short-lived `felis-bind-felis-backups-*` pod). It
lists any it could not find in the bucket. Restore a world from its archive
as usual (§10, §13).
as usual (§10, §13). With the newest bundle restored, every archive in the
bucket is listed; an older bundle leaves the archives copied after it
unlisted, and the sync removes those once they pass the longest retention.
8. Make this host the one that writes the bucket, and send its first copy:
```
@@ -2418,6 +2420,24 @@ What runs:
its retention (`expires_at`) has passed. An object already in the bucket at
the right size is recorded without being sent again, so a run cut short
resumes. [GO-TESTED: `internal/offsite`]
- A run that copied a world archive then takes a fresh `offsite` database
bundle (`felis-db-<stamp>-offsite.tar`, the same layout as a daily one, host
state from `-state-dir`, default `/etc/felis`) and sends it, so the newest
bundle in the bucket lists every archive there and a restore from it fetches
them all. The host keeps one such bundle locally, and the bucket keeps it
only while it is the newest; the `db_keep` count covers the other labels, so
a busy day of snapshots never pushes the dailies out. A run that copied
nothing, or whose newest bundle already postdates the last copy, takes none.
The panel's backup card keeps watching `felis-db-backup.timer` alone.