backups.md RawHistoryFolder

Backups Configuration

Gordon has two backup flows:

  • Database backups: PostgreSQL logical dumps via pg_dump, stored on the local filesystem.
  • Volume backups: best-effort filesystem archives of Gordon-managed named volumes, uploaded to S3. Administrative bind mounts are operator-owned host paths and are never archived.

Backups are identified by app, service, and the declared database or volume (see Apps). Domains are routing addresses and are never a backup identity.

Schedule

Each backup flow runs on one installation-wide schedule. Apps only declare what to back up; the installation decides when.

schedule Runs Default time
hourly every hour at the time minutes 00:00 (on the hour)
daily every day 02:00
weekly every Sunday 03:00
monthly the first day of each month 04:00
  • time is HH:MM (24-hour) in the server's local timezone (TZ, or /etc/localtime). The run keeps the same wall-clock time across daylight-saving changes. A time skipped by the spring-forward change runs when the gap ends. A time repeated by the fall-back change runs once.
  • The next run is computed from the current time when Gordon starts. A run that falls while Gordon is down is skipped, not caught up: no workload runs then, so there is nothing new to back up.
  • Gordon logs the schedule and next_run at startup (backup scheduler enabled, volume backup scheduler enabled).

Database backups

[backups.databases]
enabled = true
schedule = "daily"
time = "03:00"
storage_dir = "~/.gordon/backups"

[backups.databases.retention]
hourly = 24
daily = 7
weekly = 4
monthly = 12
Key Default Description
backups.databases.enabled false Enables PostgreSQL backup service wiring
backups.databases.schedule "daily" Run frequency: hourly, daily, weekly, monthly; databases run when their declared schedule matches
backups.databases.time "" HH:MM server local time; empty uses the preset default
backups.databases.storage_dir "" Root backup directory; defaults to {server.data_dir}/backups
backups.databases.retention.* 0 Number of backups to keep per DB and schedule tier

Volume backups to S3

[backups.volumes]
enabled = true
schedule = "daily"
time = "03:30"
compression = "gzip"
timeout = "2h"
max_concurrency = 2
helper_image = "alpine:3.20"

[backups.volumes.s3]
bucket = "gordon-backups"
region = "eu-west-3"
prefix = "prod/gordon"
# endpoint = "https://s3.example.com"
# path_style = false
# sse_algorithm = "AES256"
# sse_kms_key_id = ""
# storage_class = "ONEZONE_IA"

[backups.volumes.retention]
keep = 14
Key Default Description
backups.volumes.enabled false Enables automatic volume archives
backups.volumes.schedule "daily" Run frequency: hourly, daily, weekly, monthly
backups.volumes.time "" HH:MM server local time; empty uses the preset default
backups.volumes.compression "gzip" gzip or zstd; helper image must provide the tool
backups.volumes.timeout "2h" Per-volume archive/upload timeout
backups.volumes.max_concurrency 2 Volumes archived in parallel during a scheduled run
backups.volumes.helper_image "alpine:3.20" Helper container image used for tar/compression
backups.volumes.s3.bucket "" Required when volume backups are enabled
backups.volumes.s3.region "" Required when volume backups are enabled
backups.volumes.s3.prefix "" Object key prefix
backups.volumes.s3.endpoint "" Optional S3-compatible endpoint
backups.volumes.s3.path_style false Use path-style addressing for S3-compatible storage
backups.volumes.s3.sse_algorithm "" Server-side encryption sent with each upload, such as AES256
backups.volumes.s3.sse_kms_key_id "" KMS key ID for aws:kms encryption
backups.volumes.s3.storage_class "" Storage class sent with each upload, such as STANDARD_IA (AWS) or ONEZONE_IA (Scaleway One Zone); empty uses the bucket default
backups.volumes.retention.keep 14 Completed archives to keep per app + volume

Volume backup objects are stored under the canonical app name:

<prefix>/apps/<app>/volumes/<volume>/<timestamp>-<id>.tar.gz   # gzip
<prefix>/apps/<app>/volumes/<volume>/<timestamp>-<id>.tar.zst  # zstd

Objects written by earlier versions under a domain prefix are not read or migrated: back them up and re-run the app's volume backups if you need them in the current layout.

Monitoring

With telemetry metrics enabled, Gordon exports the result, duration, and size of every backup run, the last success of each target, and the next scheduled run. See Backups metrics for the metric names and example alerts.

Notes

  • Volume backups archive the app's declared volumes that its backup declaration references.
  • Bind mounts, tmpfs mounts, anonymous volumes, and undeclared volumes are never backed up.
  • Database backups archive the app's declared PostgreSQL databases that its backup declaration references.
  • Only volumes listed in a service's backup.volume are archived. Leave caches and rebuildable data (CI runner state, build caches) out of that list.
  • Stored size is roughly retention.keep × the compressed size of each backed-up volume.
  • Live volume archives are best-effort; consistency requires application quiesce, pause, or stop.
  • Automated volume restore is not part of the MVP.
  • Snapshots are not generic across Docker/Podman volumes and require backend-specific support such as ZFS, Btrfs, LVM, EBS, or a snapshot-capable volume driver.
  • S3 credentials should come from the environment, shared config, instance role, or Gordon secret conventions. Use least-privilege IAM for the configured prefix.
source
1# Backups Configuration
2
3Gordon has two backup flows:
4
5- **Database backups**: PostgreSQL logical dumps via `pg_dump`, stored on the local filesystem.
6- **Volume backups**: best-effort filesystem archives of Gordon-managed named volumes, uploaded to S3. Administrative bind mounts are operator-owned host paths and are never archived.
7
8Backups are identified by app, service, and the declared database or volume
9(see [Apps](../config/apps.md)). Domains are routing addresses and are never a
10backup identity.
11
12## Schedule
13
14Each backup flow runs on one installation-wide schedule. Apps only declare
15*what* to back up; the installation decides *when*.
16
17| `schedule` | Runs | Default `time` |
18|---|---|---|
19| `hourly` | every hour at the `time` minutes | `00:00` (on the hour) |
20| `daily` | every day | `02:00` |
21| `weekly` | every Sunday | `03:00` |
22| `monthly` | the first day of each month | `04:00` |
23
24- `time` is `HH:MM` (24-hour) in the **server's local timezone** (`TZ`, or `/etc/localtime`). The run keeps the same wall-clock time across daylight-saving changes. A time skipped by the spring-forward change runs when the gap ends. A time repeated by the fall-back change runs once.
25- The next run is computed from the current time when Gordon starts. A run that falls while Gordon is down is skipped, not caught up: no workload runs then, so there is nothing new to back up.
26- Gordon logs the schedule and `next_run` at startup (`backup scheduler enabled`, `volume backup scheduler enabled`).
27
28## Database backups
29
30```toml
31[backups.databases]
32enabled = true
33schedule = "daily"
34time = "03:00"
35storage_dir = "~/.gordon/backups"
36
37[backups.databases.retention]
38hourly = 24
39daily = 7
40weekly = 4
41monthly = 12
42```
43
44| Key | Default | Description |
45|---|---:|---|
46| `backups.databases.enabled` | `false` | Enables PostgreSQL backup service wiring |
47| `backups.databases.schedule` | `"daily"` | Run frequency: `hourly`, `daily`, `weekly`, `monthly`; databases run when their declared `schedule` matches |
48| `backups.databases.time` | `""` | `HH:MM` server local time; empty uses the preset default |
49| `backups.databases.storage_dir` | `""` | Root backup directory; defaults to `{server.data_dir}/backups` |
50| `backups.databases.retention.*` | `0` | Number of backups to keep per DB and schedule tier |
51
52## Volume backups to S3
53
54```toml
55[backups.volumes]
56enabled = true
57schedule = "daily"
58time = "03:30"
59compression = "gzip"
60timeout = "2h"
61max_concurrency = 2
62helper_image = "alpine:3.20"
63
64[backups.volumes.s3]
65bucket = "gordon-backups"
66region = "eu-west-3"
67prefix = "prod/gordon"
68# endpoint = "https://s3.example.com"
69# path_style = false
70# sse_algorithm = "AES256"
71# sse_kms_key_id = ""
72# storage_class = "ONEZONE_IA"
73
74[backups.volumes.retention]
75keep = 14
76```
77
78| Key | Default | Description |
79|---|---:|---|
80| `backups.volumes.enabled` | `false` | Enables automatic volume archives |
81| `backups.volumes.schedule` | `"daily"` | Run frequency: `hourly`, `daily`, `weekly`, `monthly` |
82| `backups.volumes.time` | `""` | `HH:MM` server local time; empty uses the preset default |
83| `backups.volumes.compression` | `"gzip"` | `gzip` or `zstd`; helper image must provide the tool |
84| `backups.volumes.timeout` | `"2h"` | Per-volume archive/upload timeout |
85| `backups.volumes.max_concurrency` | `2` | Volumes archived in parallel during a scheduled run |
86| `backups.volumes.helper_image` | `"alpine:3.20"` | Helper container image used for tar/compression |
87| `backups.volumes.s3.bucket` | `""` | Required when volume backups are enabled |
88| `backups.volumes.s3.region` | `""` | Required when volume backups are enabled |
89| `backups.volumes.s3.prefix` | `""` | Object key prefix |
90| `backups.volumes.s3.endpoint` | `""` | Optional S3-compatible endpoint |
91| `backups.volumes.s3.path_style` | `false` | Use path-style addressing for S3-compatible storage |
92| `backups.volumes.s3.sse_algorithm` | `""` | Server-side encryption sent with each upload, such as `AES256` |
93| `backups.volumes.s3.sse_kms_key_id` | `""` | KMS key ID for `aws:kms` encryption |
94| `backups.volumes.s3.storage_class` | `""` | Storage class sent with each upload, such as `STANDARD_IA` (AWS) or `ONEZONE_IA` (Scaleway One Zone); empty uses the bucket default |
95| `backups.volumes.retention.keep` | `14` | Completed archives to keep per app + volume |
96
97Volume backup objects are stored under the canonical app name:
98
99```text
100<prefix>/apps/<app>/volumes/<volume>/<timestamp>-<id>.tar.gz   # gzip
101<prefix>/apps/<app>/volumes/<volume>/<timestamp>-<id>.tar.zst  # zstd
102```
103
104Objects written by earlier versions under a domain prefix are not read or
105migrated: back them up and re-run the app's volume backups if you need them in
106the current layout.
107
108## Monitoring
109
110With [telemetry](./telemetry.md) metrics enabled, Gordon exports the result, duration, and size of every backup run, the last success of each target, and the next scheduled run. See [Backups metrics](./telemetry.md#backups) for the metric names and example alerts.
111
112## Notes
113
114- Volume backups archive the app's declared volumes that its backup declaration references.
115- Bind mounts, tmpfs mounts, anonymous volumes, and undeclared volumes are never backed up.
116- Database backups archive the app's declared PostgreSQL databases that its backup declaration references.
117- Only volumes listed in a service's `backup.volume` are archived. Leave caches and rebuildable data (CI runner state, build caches) out of that list.
118- Stored size is roughly `retention.keep` × the compressed size of each backed-up volume.
119- Live volume archives are best-effort; consistency requires application quiesce, pause, or stop.
120- Automated volume restore is not part of the MVP.
121- Snapshots are not generic across Docker/Podman volumes and require backend-specific support such as ZFS, Btrfs, LVM, EBS, or a snapshot-capable volume driver.
122- S3 credentials should come from the environment, shared config, instance role, or Gordon secret conventions. Use least-privilege IAM for the configured prefix.
123
124## Related
125
126- [App Manifest](./apps.md)
127- [CLI Backup Command](../cli/backup.md)
128- [Configuration Reference](./reference.md)
129- [Telemetry](./telemetry.md)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close