Hooks¶
Hooks are operator-controlled extension points that run before and after
each worker. Mount scripts into /hooks in the container, make them
executable, and the workers will run them automatically.
Available hooks¶
| Hook | Worker | First argument |
|---|---|---|
/hooks/pre-backup.sh |
/bin/backup |
none |
/hooks/post-backup.sh |
/bin/backup |
Backup exit code |
/hooks/pre-check.sh |
/bin/check |
none |
/hooks/post-check.sh |
/bin/check |
Check exit code |
/hooks/pre-prune.sh |
/bin/prune |
none |
/hooks/post-prune.sh |
/bin/prune |
Prune exit code |
/hooks/pre-replicate.sh |
/bin/replicate |
none |
/hooks/post-replicate.sh |
/bin/replicate |
Aggregate exit code |
/hooks/pre-restore.sh |
/bin/restore |
none |
/hooks/post-restore.sh |
/bin/restore |
Restore exit code |
/hooks/pre-snapshot-export.sh |
/bin/snapshot-export |
none |
/hooks/post-snapshot-export.sh |
/bin/snapshot-export |
Snapshot-export exit code |
/hooks/pre-forget-preview.sh |
/bin/forget-preview |
none |
/hooks/post-forget-preview.sh |
/bin/forget-preview |
Forget-preview exit code |
/hooks/pre-mount-snapshot.sh |
/bin/mount-snapshot |
none |
/hooks/post-mount-snapshot.sh |
/bin/mount-snapshot |
Mount-snapshot exit code (post-unmount) |
Hooks must be executable inside the container (chmod +x). A hook
present but not executable is reported as an error in cron.log, not
silently skipped.
Lifecycle¶
sequenceDiagram
participant Cron
participant LockedRun as /bin/locked_run
participant Worker as /bin/<worker>
participant PreHook as /hooks/pre-<worker>.sh
participant PostHook as /hooks/post-<worker>.sh
Cron->>LockedRun: tick
LockedRun->>Worker: flock acquired
Worker->>PreHook: invoke (if executable)
PreHook-->>Worker: stdout/stderr + exit code (logged, not propagated)
Worker->>Worker: do the actual job
Worker->>PostHook: invoke "$exit_code"
PostHook-->>Worker: stdout/stderr + exit code (logged, not propagated)
Worker-->>LockedRun: exit with worker's own exit code
Key rules:
- Pre-hooks do not abort the worker. A non-zero pre-hook is logged prominently but the worker still runs. Use the pre-hook for informational side-effects (warming a cache, taking an upstream lock, …) — never for "if this fails, do not back up".
- Post-hooks receive the worker's exit code as
$1. They can fan out to escalation channels (PagerDuty, Slack-different), unmount source filesystems, run an external integrity check, etc. - Hook output is logged but does not contribute to the worker's exit code. A failed hook never turns a successful backup into a failed one.
Timeouts¶
Set HOOK_TIMEOUT to a positive integer to wrap each hook in
timeout ${HOOK_TIMEOUT}s:
When a hook times out:
- The runner kills the hook process.
- Exit code
124is logged as a timeout (clearly distinguished from a hook that exited with124on its own). - Subsequent steps still run; the timeout does not propagate to the worker.
A HOOK_TIMEOUT=0 (default) imposes no enforced timeout.
Mounting hooks¶
Example hooks¶
Slack notification on backup failure¶
#!/usr/bin/env bash
set -euo pipefail
rc="${1:-0}"
[ "${rc}" -eq 0 ] && exit 0
webhook="${SLACK_WEBHOOK_URL:-}"
[ -n "${webhook}" ] || exit 0
payload="$(jq -nc \
--arg host "$(hostname)" \
--arg rc "${rc}" \
--arg link "$(cat /var/log/last-backup.json | jq -c .)" \
'{text: "Backup failed on \($host) with rc=\($rc)", attachments: [{text: $link}]}')"
curl -fsS -m 10 -H 'Content-Type: application/json' -d "${payload}" "${webhook}"
Save as /hooks/post-backup.sh, chmod +x, mount.
Filesystem freeze around the backup¶
#!/usr/bin/env bash
# /hooks/post-backup.sh
set -euo pipefail
fsfreeze --unfreeze /data || true
exit 0
fsfreeze requires privileges
fsfreeze needs CAP_SYS_ADMIN and either a host bind mount or a
privileged container. Consider freezing upstream (LVM snapshots,
qemu-guest-agent quiesce) rather than inside this container.
Cleanup after restore¶
#!/usr/bin/env bash
# /hooks/post-restore.sh — chown to the application user.
set -euo pipefail
rc="${1:-0}"
[ "${rc}" -eq 0 ] || exit 0
chown -R 1000:1000 /restore
This pairs with /bin/restore --owner 1000:1000 if you prefer the flag.
The hook is useful when ownership rules depend on which path inside
the restore tree (different UIDs for different subdirectories).
Inspecting hook status¶
/bin/doctor reports the presence and executable bit of every supported
hook:
== Hooks ==
HOOK_TIMEOUT: 300
hooks/pre-backup.sh: executable
hooks/post-backup.sh: executable
hooks/pre-check.sh: not found
…
So if a hook is mounted but silently ignored, doctor tells you why.
See also¶
- Diagnostics — what
/bin/doctorreports. - Backup worker — hook ordering relative to forget / unlock.
- Restore — hooks during operator-driven restore.