Skip to content

Configuration reference

zolder reads a single machine-local TOML file, zolder.toml. zolder init writes a working one for you; this page is the full reference for hand-editing it afterwards.

Every check section is optional and config-gated per machine — a machine runs only the checks it declares. The Mac and Windows peers drop [zfs]; a box outside the mesh drops [syncthing]. Unknown keys are rejected (the parser uses deny_unknown_fields), so a typo is an error rather than a silently ignored setting.

Complete example

The annotated zolder.example.toml ships with every release. It is embedded verbatim from that source file at build time, and CI parse-tests it against the config structs (example_config_parses in src/config.rs), so this reference cannot drift from what the binary actually accepts:

toml
# zolder machine-local config. The easy path is `zolder init`, which discovers
# Syncthing and writes a working config into the platform config dir for you;
# this file is the full reference for hand-editing afterwards.
#
# Copy to zolder.toml (gitignored) per machine. Every check section is optional
# — a machine runs only what it declares: the Mac/Windows peers drop [zfs]; a
# box outside the mesh drops [syncthing].

# Point this into a synced folder and the mesh itself carries every device's
# status report to the node (fleet aggregation, post-MVP).
status_path = "zolder.json"

[syncthing]
url = "http://127.0.0.1:8384"
api_key_file = "/home/demian/.config/zolder/syncthing-api-key"
device_max_last_seen_hours = 24

[restic]
binary = "restic"
# What `zolder run backup` backs up — typically the synced tree on the node.
paths = ["/data/sync"]
exclude = ["**/node_modules", "**/target"]

[[restic.repo]]
name = "hetzner"
repository = "s3:https://fsn1.your-objectstorage.example/zolder-backup"
password_file = "/home/demian/.config/zolder/restic-password"
env_file = "/home/demian/.config/zolder/restic-hetzner.env"
max_snapshot_age_hours = 36

[[restic.repo]]
name = "scaleway"
repository = "s3:https://s3.nl-ams.scw.cloud/zolder-backup"
password_file = "/home/demian/.config/zolder/restic-password"
env_file = "/home/demian/.config/zolder/restic-scaleway.env"
max_snapshot_age_hours = 36

[canary]
dir = "/data/sync/.zolder/canary"
max_age_hours = 24

# Home node only — omit on Mac/Windows peers.
[zfs]
pools = ["tank"]

[alert]
# Dead-man's switch: pinged on every completed run, `<url>/fail` when unhealthy.
# heartbeat_url = "https://hc-ping.com/<uuid>"

[alert.ntfy]
server = "https://ntfy.sh"
topic = "zolder-CHANGE-ME"
# Bearer token for a self-hosted / protected ntfy topic.
# token_file = "/home/demian/.config/zolder/ntfy-token"

The sections below are the field-by-field reference — types, defaults, and semantics for every key above.

Top level

KeyTypeDefaultNotes
status_pathpathrequiredWhere zolder check writes its report. zolder init points this at zolder.json in the config dir. Point it into a synced folder and the mesh itself carries every device's status to the node (fleet aggregation, post-MVP).

[syncthing] — mesh health (optional)

Read-only Syncthing REST health: per-folder sync state and backlog, plus whether every configured peer is connected or was seen recently enough.

KeyTypeDefaultNotes
urlstringrequiredREST API base, e.g. http://127.0.0.1:8384.
api_key_filepathrequiredFile holding the API key — never the key itself in config.
device_max_last_seen_hoursinteger24Fail when a disconnected peer hasn't been seen for longer than this.

A folder in error or with scan errors fails; a paused folder or a growing backlog warns; a peer unseen past the threshold fails.

[restic] — backup freshness (optional)

Read-only restic freshness per repo: is the newest snapshot young enough? A repo that can't be queried, one with no snapshots, or one whose newest snapshot is past its age threshold all fail.

KeyTypeDefaultNotes
binarystring"restic"restic binary to invoke; found on PATH by default.
pathsarray of paths[]What zolder run backup backs up. The freshness check doesn't use this.
excludearray of strings[]Extra --exclude globs for the backup run.

At least one repository must be declared as a [[restic.repo]] table:

KeyTypeDefaultNotes
namestringrequiredLabel in reports, e.g. "hetzner".
repositorystringrequiredrestic repository URL, e.g. s3:https://….
password_filepathrequiredrestic password file.
env_filepathoptionalExtra env (credentials) sourced at run time. KEY=VALUE per line; blank lines and # comments are ignored.
max_snapshot_age_hoursinteger36Fail when the newest snapshot is older than this.

[canary] — sync liveness (optional)

Each peer writes its own <host>.canary marker into a shared synced folder on every check run and reads the others' freshness — catching "everything reports healthy but nothing actually moves". Failing to write our own marker fails the check (the synced tree isn't writable); a stale or absent peer marker warns.

KeyTypeDefaultNotes
dirpathrequiredDirectory inside a synced folder.
max_age_hoursinteger24A peer marker older than this is stale.

[zfs] — pool health (optional, node only)

Shells out to zpool list and reports; any pool not ONLINE fails. zolder never scrubs, imports, or resilvers. Omit this section on the Mac/Windows peers.

KeyTypeDefaultNotes
poolsarray of stringsrequiredPools that must be ONLINE.

[alert] — notifications (optional)

See Alerting for the full semantics.

KeyTypeDefaultNotes
heartbeat_urlstringoptionalDead-man's switch: pinged (GET) on every completed run, <url>/fail when unhealthy.

[alert.ntfy] — push channel (optional)

KeyTypeDefaultNotes
serverstring"https://ntfy.sh"ntfy server base.
topicstringrequiredntfy topic to publish alerts to.
token_filepathoptionalBearer token for a self-hosted / protected ntfy topic.