1# Config examples
2
3```toml
4# keld config merge order (lowest to highest precedence):
5# [global] -> [global.<command>] -> split preset parts -> full preset -> CLI overrides
6#
7# For a split preset like `home@cloud` and command `backup`, keld checks:
8# [global] -> [global.backup] -> [@cloud] -> [@cloud.backup] ->
9# [home@] -> [home@.backup] -> [home@cloud] -> [home@cloud.backup]
10
11[global]
12# Shared flags for every command and preset.
13password-file = "~/.config/restic/password.txt"
14
15[global.backup]
16# Command-specific defaults.
17exclude-file = "~/.config/restic/excludes.txt"
18exclude-if-present = ".nobackup"
19
20[home.backup]
21# Simple preset (no @): `keld --preset home backup`
22_arguments = ["/home/user"]
23tag = ["home"]
24
25# ── Suffixes: the "where" ───────────────────────────────────
26#
27# Suffix sections define backend-wide settings. Restic supports paths
28# inside rclone remotes and S3 buckets, so one remote or bucket can hold
29# multiple independent repositories. Credentials live here once.
30
31["@nas"]
32# Suffix: applies to any `*@nas` preset.
33repository = "sftp:nas@example.org:/mnt/backups/restic"
34tag = ["nas"]
35
36["@cloud"]
37# Suffix for S3-compatible cloud backups (B2, Wasabi, etc.).
38# Note: no repository here — each full preset sets its own path
39# within the shared bucket.
40tag = ["cloud"]
41
42# Keys ending in _COMMAND are executed by keld; stdout becomes the env
43# var with the suffix removed. RESTIC_PASSWORD_COMMAND and
44# RESTIC_FROM_PASSWORD_COMMAND are passed through to restic as-is.
45["@cloud".environ]
46AWS_ACCESS_KEY_ID_COMMAND = "op read 'op://Vault/Cloud/access-key-id'"
47AWS_SECRET_ACCESS_KEY_COMMAND = "op read 'op://Vault/Cloud/secret-access-key'"
48
49# ── Prefixes: the "what" ────────────────────────────────────
50#
51# Prefix sections define sources, excludes, and the restic encryption
52# password. Each dataset gets its own password so repositories are
53# independently encrypted, even when stored on the same backend.
54
55["home@"]
56# Prefix: applies to any `home@*` preset.
57host = "my-laptop"
58tag = ["home"]
59
60["home@".environ]
61RESTIC_PASSWORD_COMMAND = "pass show backups/restic-home"
62
63["home@".backup]
64# Prefix + command section.
65_arguments = ["/home/user", "/etc"]
66exclude-if-present = ".keld-skip"
67
68["home@".forget]
69# Prefix + different command.
70keep-daily = 7
71keep-weekly = 5
72keep-monthly = 12
73
74# ── Full presets: prefix + suffix with repo path ────────────
75#
76# Full preset sections set the repository path within each backend.
77# This is where the prefix ("what") meets the suffix ("where").
78
79["home@nas"]
80# Uses the @nas suffix's sftp base; path set here.
81repository = "sftp:nas@example.org:/mnt/backups/restic/home"
82
83["home@cloud"]
84# Path within the shared S3 bucket for this dataset.
85repository = "s3:s3.us-east-1.amazonaws.com/my-restic-bucket/home"
86
87# ── BorgBase: per-repo URLs ─────────────────────────────────
88#
89# BorgBase provides per-repository URLs with embedded credentials.
90# Each repo is specific to one dataset — there's no shared @borgbase
91# suffix to reuse. Keep the split form so the prefix's _arguments
92# and password settings still apply through the merge chain.
93
94["home@borgbase_home"]
95repository = "rest:https://ab12cd34:s3cr3tp4ssw0rd@ab12cd34.repo.borgbase.com"
96```
97
98## Systemd timer setup
99
100Example systemd user units for daily backups (with lightweight structural
101check) and monthly full integrity verification, with optional healthchecks.io
102integration via `runitor`. Also assume use via `mise` and that the user has
103set up the `mise` config snippet for `keld`. This might not be the case;
104you'll need to ask or figure out how they've installed `keld`.
105
106### Unit files
107
108Installed to `~/.config/systemd/user/` or `/etc/systemd/user/`.
109
110**keld-backup@.service** — runs backup then a quick structural check. Both
111are wrapped by a single runitor invocation so a failure in either reports
112to the same healthcheck:
113
114```ini
115[Unit]
116Description=keld %I backup
117
118[Service]
119Nice=19
120IOSchedulingClass=idle
121KillSignal=SIGINT
122EnvironmentFile=-%h/.config/keld/timers/%I_backup.env
123ExecStart=/bin/sh -c '%h/.local/bin/mise x github:bdd/runitor -- runitor -- /bin/sh -c "mise x http:keld -- keld --preset %I backup && mise x http:keld -- keld --preset %I check"'
124```
125
126**keld-backup@.timer**:
127
128```ini
129[Unit]
130Description=Daily keld %I backup
131
132[Timer]
133OnCalendar=daily
134AccuracySec=1m
135RandomizedDelaySec=1h
136Persistent=true
137
138[Install]
139WantedBy=timers.target
140```
141
142**keld-integrity@.service** — monthly full data integrity verification.
143Downloads and verifies every pack file (`--read-data`):
144
145```ini
146[Unit]
147Description=keld %I integrity check (read-data)
148
149[Service]
150Nice=19
151IOSchedulingClass=idle
152KillSignal=SIGINT
153EnvironmentFile=-%h/.config/keld/timers/%I_integrity.env
154ExecStart=%h/.local/bin/mise x github:bdd/runitor -- runitor -- mise x http:keld -- keld --preset %I check --read-data
155```
156
157**keld-integrity@.timer**:
158
159```ini
160[Unit]
161Description=Monthly keld %I integrity check
162
163[Timer]
164OnCalendar=monthly
165AccuracySec=1m
166RandomizedDelaySec=1h
167Persistent=true
168
169[Install]
170WantedBy=timers.target
171```
172
173### Enabling timers
174
175```bash
176# For each preset (e.g. media@borgbase_media):
177systemctl --user enable --now keld-backup@media@borgbase_media.timer
178systemctl --user enable --now keld-integrity@media@borgbase_media.timer
179
180# Verify
181systemctl --user list-timers
182```
183
184### Healthchecks env files
185
186Create env files in `~/.config/keld/timers/` with a `CHECK_UUID` variable for each preset:
187
188```
189~/.config/keld/timers/media@borgbase_media_backup.env → backup + check UUID
190~/.config/keld/timers/media@borgbase_media_integrity.env → monthly read-data UUID
191```
192
193The `EnvironmentFile=-` prefix (`-`) makes the env file optional — the timer still works without healthchecks.