Skip to content

Alerts and automation

Extracted from the master document on 2026-09-04. §N numbering preserved so as not to break cross-references.

The three scripts (pve-backup.sh, borg-backup.sh, borg-maintenance.sh) report success and failure to the same Discord webhook Chivatiente that Uptime Kuma uses. Why only one: one more channel to watch is a channel that stops being watched.

Generating the JSON by hand produced HTTP 400 when the message contained special characters. It was replaced with jq:

Terminal window
payload=$(jq -n \
--arg content "${tag} borg-backup on ${HOSTNAME_TAG}: ${msg}" \
'{content: $content}')

The webhook returns HTTP 204 when it accepts the notification.

Script Machine Schedule Why that schedule
pve-backup.timer Proxmox Sunday 02:00 (+up to 15 min of jitter) full image = heavy; weekly is enough for total reconstruction; early Sunday morning = minimum usage
borg-backup.timer docker-host daily 03:00 granular needs a daily cadence for a low RPO
borg-maintenance.timer VM 101 daily 04:00 one hour later on purpose: it guarantees that the night’s archive finished before pruning/compacting/mirroring
00:00 02:00 03:00 04:00 24:00
│ │ │ │ │
│ vzdump borg-backup borg-maintenance │
│ (Sunday) (daily) (daily) │
│ └──► R2 └──► VM101 └──► prune+compact+sync R2 │
│ │
└───────────── the 03:00→04:00 window avoids the race ─────────┘

All three use Persistent=true: if the machine was powered off at the scheduled time, the timer fires as soon as it comes back, instead of waiting for the next cycle. It was verified that each timer exists only on the machine it belongs to, with no cross-duplicates.