Scheduled Restic backups with checks, prune, rclone replication, alerts, metrics and restore helpers
6.3K
Scheduled Restic backups, optional restic check, optional Rclone replicate jobs (bisync / sync / copy), cron automation, logs under /var/log, optional mail via msmtp + mailx. Includes read-only operator helpers such as /bin/status (/bin/health-summary), /bin/doctor, /bin/cron-list, /bin/support-bundle, /bin/sources-report, /bin/notify-test and an audited /bin/init-repo bootstrap wrapper. Based on restic/restic Alpine.
Documentation: marc0janssen.github.io/restic-backup-helper · GitHub (full manual, Compose, hooks, env matrix): github.com/marc0janssen/restic-backup-helper
release: 3.2.3-0.18.1
Stable
docker pull marc0janssen/restic-backup-helper:latest
docker pull marc0janssen/restic-backup-helper:3.2.3-0.18.1
Development (experimental)
docker pull marc0janssen/restic-backup-helper:develop
docker pull marc0janssen/restic-backup-helper:3.2.3-0.18.1-dev
Upgrading?
- 3.1.0 → 3.2.0: additive plus safer defaults. New
/bin/support-bundlecreates a redacted diagnostics tarball. Workerlast-*.jsonfiles now include documentedstarted_epoch/finished_epochintegers. Compose/Kubernetes/Helm liveness examples now probe local scheduler health (cron.log+crond) instead of remote repository reachability. Trackedconfig/rclone.confandconfig/msmtprcare now.exampletemplates; real local files are gitignored.- 2.13.0 → 2.14.0: purely additive. New
/bin/statushelper (alias/bin/health-summary) gives a fast local OK/WARN/FAIL summary from release metadata, rendered crontab (or env preview), recent/var/log/last-*.jsonfiles and backup/check/forget/prune/replicate ages + exit codes. It deliberately does not runrestic,rclone, hooks, mail, webhooks or a repository probe; use/bin/doctorfor deeper diagnostics.--jsonemits stdout-only schemarestic-backup-helper.status/1and does not write/var/log/last-status.json. No new environment variables.- 2.10.1 → 2.11.0: purely additive.
RESTIC_REPOSITORY_FILEis now first-class. The entrypoint reads the first non-blank, non-comment line of the file and promotes it intoRESTIC_REPOSITORYbefore the banner, the repository probe, the cron-driven workers and restic itself see the env, then unsetsRESTIC_REPOSITORY_FILEso restic never fails withOptions --repo and --repository-file are mutually exclusive(the image bakes aRESTIC_REPOSITORY=/mnt/resticdefault inDockerfile, so the two would otherwise both be set). The "Assuming repository '…' is online" banner now shows the masked resolved URL, andconfig-check//bin/doctorsurface clear errors when the file is unreadable or empty/comments-only. No behaviour change whenRESTIC_REPOSITORY_FILEis unset.- 2.10.0 → 2.10.1: patch release. Prometheus textfile metrics now escape the
hostnamelabel and emit the documentedrestic_<job>_last_started_timestampgauge, the webhook helper contract is documented accurately, and docs clarify thatRESTIC_*_ARGS/REPLICATE_*_ARGSvalues are whitespace-split strings rather than full shell syntax. Keep paths/values free of spaces, or use file-based inputs such as--files-from,--exclude-fileand rclone config files.- 2.9.0 → 2.10.0: purely additive. New
/bin/notify-testhelper sends clearly-labelled test mail and/or webhook notifications through the samenotify_mail/notify_webhookhelpers used by real jobs, so operators can validatemsmtprc,MAILX_RCPT,WEBHOOK_URL,WEBHOOK_HEADER_AUTHandWEBHOOK_TIMEOUTbefore waiting for a real failure. Default mode sends to every configured target;--webhookand--allselect target scope;--dry-runprints what would be sent without invokingcurl;--subject/--messagelabel the test. Unlike real workers, delivery failures affect the helper exit code (1) so CI can catch notification drift. Writes/var/log/last-notify-test.json,restic_notify_test.prom, and runspre-notify-test/post-notify-testhooks. No new environment variables.- 2.8.0 → 2.9.0: purely additive. New audited operator helper
/bin/init-repois the operator-driven counterpart to the entrypoint auto-init probe. The recommended deployment pattern on shared remotes is nowRESTIC_CHECK_REPOSITORY_STATUS=OFF(no auto-init on a transient TLS / DNS / auth hiccup) plus a one-shot/bin/init-repo --yes(CI) or interactive/bin/init-repo(operator) for the first bootstrap.--dry-runruns the samerestic cat configprobe and prints the plannedrestic initcommand + verdict (would CREATE/would REFUSE — already exists/ probe error) without mutation. Without--dry-runa typedinitconfirmation (interactive TTY) or explicit--yesis required. Adds theRESTIC_INIT_ARGSenv-var (e.g.--repository-version=2,--copy-chunker-params=…); CLI passthrough after--works too. Writes/var/log/last-init-repo.json(withdry_run,assume_yes,confirmed,repo_existed,probe_exit_code,init_args),restic_init_repo.prom, and runspre-init-repo/post-init-repohooks, mail and webhook the same way as the other workers. Reachable viadocker exec … /bin/init-repoordocker run … init-repo. Idempotent: exits3when the repo already exists. No behaviour change for the entrypoint auto-init probe;RESTIC_CHECK_REPOSITORY_STATUS=ONkeeps its existing semantics.- 2.7.0 → 2.8.0: purely additive. New read-only operator helper
/bin/sources-reportis a pre-flight inventory of the paths your next backup will actually read: re-uses the sameBACKUP_ROOT_DIR+RESTIC_JOB_ARGSparsing as/bin/backup, reports readability, type, file count and (optional) size per source, plus pattern counts and missing-entry counts for every--files-from/--exclude-filereference.--no-sizeskipsdu -skon slow / remote sources;--depth Ncapsfinddepth; repeatable--source PATH/--files-from FILEadd ad-hoc entries. Writes/var/log/last-sources-report.json(flat aggregates plus nestedsources,files_from,exclude_filesarrays),restic_sources_report.prom, and runspre-sources-report/post-sources-reporthooks, mail and webhook the same way as the other workers. Reachable viadocker exec … /bin/sources-reportordocker run … sources-report. The size figure is unfiltered (exclude rules are not applied); the exclude-file inventory is reported separately. No env-var changes.- 2.6.0 → 2.7.0: purely additive. New audited operator helper
/bin/unlockcomplements the saferRESTIC_AUTO_UNLOCK=OFFdefault (workers still never auto-clear locks on failure). Removes stale exclusive locks by default;--remove-allwidens to non-exclusive locks;--dry-runonly lists current locks. Writes/var/log/last-unlock.json(withremove_all,dry_run,locks_before,locks_after),restic_unlock.prom, and runspre-unlock/post-unlockhooks, mail and webhook the same way as the other workers. Reachable viadocker exec … /bin/unlockordocker run … unlock. No env-var changes;RESTIC_AUTO_UNLOCKkeeps its existing semantics.- 2.5.0 → 2.6.0: purely additive. New read-only
/bin/cron-listinspector printsTZ, the rendered crontab and a per-job summary (run viadocker exec … /bin/cron-listordocker run … cron-list). Build scripts gain a--base <restic-tag>CLI flag withnewest/latestandprerelease/rc/betasentinels resolved against Docker Hub before the tag is computed; the resolved tag is verified to exist on Docker Hub before any files are mutated, so a non-existent--base 0.19.0aborts cleanly instead of producing an image whose tag suffix does not match the base actually used../build-testing-local.shnow also patchesDockerfile FROMto match--base, and pushes:developinstead of:testing(versioned:<release>tag unchanged) — update anyimage: …:testingreferences in your private-registry manifests to…:developor pin to…:2.6.0-0.18.1-dev. No runtime change inside the container beyond the new cron-list helper.- 2.4.0 → 2.5.0: multi-host retention hardening. New standalone
/bin/forgetworker scheduled viaFORGET_CRON(own JSON/Prometheus/mail/webhook/hooks like/bin/prune); when set,/bin/backupskips its inline forget so the exclusive lock is only taken in the dedicated window — eliminates the exit-11 race. Inline post-backup forget exit 11 is also downgraded to⏭ Forget skipped …(backupexit_codestays0); the forget result is recorded separately asforget_exit_codeinlast-backup.jsonand as arestic_backup_last_forget_exit_codePrometheus gauge.restic unlockis never auto-run on exit 11 regardless ofRESTIC_AUTO_UNLOCK(the lock we lost is another host's legitimate lock). Drop-in:FORGET_CRONempty (= default) keeps legacy behaviour. Adding--retry-lock=DURATIONtoRESTIC_FORGET_ARGSis recommended either way.- 2.3.x → 2.4.0: additive. New
/bin/mount-snapshothelper wrapsrestic mount(FUSE) read-only under/fusemount(container-internal by design, never collides with/bin/restoreoutput or host bind-mounts), scoped to this container's host/tag by default, with safe target validation, opt-in--allow-other, repeatable--path, an explicit--repo-wideoverride and anEXITtrap so SIGINT / SIGTERM / crash always unmounts cleanly. FUSE still requires--cap-add SYS_ADMIN --device /dev/fuseand, on hosts that ship AppArmor'sdocker-defaultprofile,--security-opt apparmor=unconfined.- 2.2.x → 2.3.0: additive. New
/bin/forget-previewhelper runsrestic forget --dry-runwithRESTIC_FORGET_ARGS, host/tag-scoped by default and repository-wide only with--repo-wide.- 2.2.1 → 2.2.2: docs/patch. Adds a Material for MkDocs documentation site (hosted at https://marc0janssen.github.io/restic-backup-helper/) plus a GitHub Pages deploy workflow. No runtime change.
- 2.2.0 → 2.2.1: patch. CI-only shellcheck cleanup in
app/snapshot_export.sh(SC2317/SC2119); no runtime change.- 2.1.x → 2.2.0: purely additive. New
/bin/snapshot-exporthelper restores a selected snapshot or subtree into a temporary workdir and packages it as.tar.gzunder/restoreby default. Supports--id,--include,--exclude,--output,--dry-run, hooks, JSON, webhook, mail and metrics.- 2.0.x → 2.1.0: purely additive. New
/bin/doctorread-only diagnostics command for support/triage: masked effective env, path checks, repository probe, replicate job-file validation, hook executable status and recent/var/logsummaries.docker run … doctorruns it without starting cron.- 2.14.x → 3.0.0: Breaking. Removes legacy
SYNC_*env vars and/bin/bisync— useREPLICATE_*and/bin/replicateonly. See GitHub README / Upgrading.- 1.18.x → 2.0.0: the old "sync/bisync" surface is renamed to replicate. Use
/bin/replicate,REPLICATE_*env vars,/config/replicate_jobs.txt,/hooks/pre-replicate.sh//hooks/post-replicate.sh,/var/log/last-replicate.json,/var/log/replicate-last.logandrestic_replicate.prom. Through 2.x only, legacySYNC_*and/bin/bisyncwere accepted with warnings (removed in 3.0.0). Rename any mountedconfig/sync_jobs.txttoconfig/replicate_jobs.txtor setREPLICATE_JOB_FILEexplicitly. Monitoring and hook paths must be updated.- 1.17.x → 1.18.0: polish on top of the 1.17.0
/bin/restorewrapper. Three operator-visible additions:--yes/-yruns the wrapper fully non-interactively (skips picker + target + dry-run + Proceed prompts, fills missing answers with cron/CI defaults — useful from insidedocker exec -ti …);--verbose/-vnow actually streams progress (passes--verbose=2to restic for per-file lines AND wraps restic inscript(1)so the native in-place progress bar renders); interactive mode is TTY-driven only, so modifier flags like--verboseand--forceno longer skip the prompts. Image grows ~6 MB to shiputil-linux(forscript(1)).--includezero-match now exits3instead of silently succeeding. Pure polish, no breaking changes for existing scripted callers.- 1.16.x → 1.17.0: purely additive. New
/bin/restorewrapper (interactive on a TTY, flag-driven otherwise) with mail/webhook on by default and/var/log/last-restore.jsonsummary; optional/hooks/{pre,post}-restore.sh. Refuses to restore into/dataor a non-empty--targetunless--force(or--dry-run). See the GitHub README "Restore (operator-friendly)" section.- 1.15.x → 1.16.0: purely additive (no env-var rename, no behaviour change). New surfaces: opt-in image SBOM via
SBOM=ON ./build.sh(requiressyft); source-tree SBOM uploaded by the release CI;scripts/docker-compose.ymlships Compose profilesmetrics(node-exporter sidecar) anddev(mailhog); new multi-job example atexamples/compose/multi-job.yml; new README "Hardening" section with theread_only: true+ tmpfs recipe.- 1.14.x → 1.15.0: purely additive. New opt-in env vars
METRICS_DIR(Prometheus textfile collector) andREPLICATE_BISYNC_CHECK_ACCESS(bisync--check-accessopt-in; wasSYNC_BISYNC_CHECK_ACCESSin 1.x only). Mail subjects gain[OK|FAIL N] Job host · duration · detailsprefix. Replicate URL credentials are masked in logs.- 1.13.x → 1.14.0: explicitly empty
RESTIC_TAGis now a hard error. Replicate job files accept optionalMODE/EXTRA_ARGScolumns.rcloneis now installed once with SHA256 verification.- From 1.11.x: automatic
restic unlockafter backup / check failures is opt-in (RESTIC_AUTO_UNLOCK=ON, since 1.12.0). 1.13.0 adds standalonePRUNE_CRON+RESTIC_PRUNE_ARGS. See the GitHub README env table.
| Tag | Meaning |
|---|---|
latest | Current stable |
<semver>-<restic> | Pinned stable (helper version + Restic base), e.g. 3.2.3-0.18.1 |
develop | Latest testing build |
<semver>-<restic>-dev | Pinned testing image |
Full documentation: https://marc0janssen.github.io/restic-backup-helper/
| Component | Trigger |
|---|---|
| Backup | BACKUP_CRON → /bin/backup |
| Check | CHECK_CRON (if set) → /bin/check |
| Prune | PRUNE_CRON (if set) → /bin/prune (standalone restic prune on its own cadence) |
| Replicate | REPLICATE_CRON (if set) → /bin/replicate reading REPLICATE_JOB_FILE |
| Log rotate | ROTATE_LOG_CRON → /bin/rotate_log for cron.log |
| Config check | One-shot docker run … config-check (same env as prod) validates settings without cron |
| Status | /bin/status / /bin/health-summary gives a fast local OK/WARN/FAIL summary from last-*.json, crontab and release metadata |
| Doctor | One-shot /bin/doctor or docker run … doctor read-only diagnostics for support/triage |
| Cron list | /bin/cron-list or docker run … cron-list prints timezone, rendered crontab and schedule summary |
| Snapshot export | One-shot /bin/snapshot-export or docker run … snapshot-export archives a selected snapshot/subtree as .tar.gz |
| Forget preview | One-shot /bin/forget-preview or docker run … forget-preview previews RESTIC_FORGET_ARGS with restic forget --dry-run |
| Mount snapshot | One-shot /bin/mount-snapshot or docker run … mount-snapshot mounts the repo read-only over FUSE under /fusemount (container-internal; needs --cap-add SYS_ADMIN --device /dev/fuse + --security-opt apparmor=unconfined on Ubuntu/Debian hosts) |
| Restore | One-shot /bin/restore; interactive with a TTY, flag-driven otherwise |
Startup (/entry.sh) can verify/init the repo when RESTIC_CHECK_REPOSITORY_STATUS=ON. Jobs use flock locks (/var/run/*.lock).
| Path | Use |
|---|---|
/data | Backup source (BACKUP_ROOT_DIR often /data) |
/config | local rclone.conf, excludes, msmtprc, replicate job file |
/hooks | Optional pre-* / post-* scripts |
/var/log | Persist logs on the host |
/restore | Common restore target volume |
Always configure: RESTIC_REPOSITORY, repository auth (RESTIC_PASSWORD or RESTIC_PASSWORD_FILE), RESTIC_TAG (required by backup), BACKUP_CRON, and either BACKUP_ROOT_DIR and/or paths via RESTIC_JOB_ARGS.
Defaults from the image (see GitHub README for full table): RESTIC_CACHE_DIR=/.cache/restic, RESTIC_CHECK_REPOSITORY_STATUS=ON, RCLONE_CONFIG=/config/rclone.conf, REPLICATE_JOB_FILE=/config/replicate_jobs.txt, REPLICATE_VERBOSE=ON, ROTATE_LOG_CRON=0 0 * * 6, CRON_LOG_MAX_SIZE=1048576, MAX_CRON_LOG_ARCHIVES=5, TZ=Europe/Amsterdam.
Forget policy: set RESTIC_FORGET_ARGS (example: --retry-lock=5m --keep-daily 7 --keep-weekly 5 --keep-monthly 12) to run restic forget after a successful backup. Add --prune only if you do not run PRUNE_CRON separately. Run /bin/forget-preview first to preview the policy safely (--dry-run, host/tag-scoped by default).
Mail: MAILX_RCPT + mounted /etc/msmtprc; MAILX_ON_ERROR=ON limits backup/check mail to failures. Replicate mails only when errors occurred.
Replicate file format: SOURCE;DESTINATION[;MODE[;EXTRA_ARGS]] per line (MODE ∈ bisync (default) / sync / copy; EXTRA_ARGS are per-job rclone flags). See config/replicate_jobs.txt. Bisync recovery hardening: set REPLICATE_BISYNC_CHECK_ACCESS=ON to require the RCLONE_TEST marker on both endpoints.
Metrics: set METRICS_DIR=/var/log/textfile_collector to write Prometheus textfile-collector *.prom files alongside last-*.json (point node-exporter at it).
/hooks)pre-backup.sh, post-backup.sh (backup exit code), pre-check.sh, post-check.sh (check exit code), pre-prune.sh, post-prune.sh (prune exit code), pre-replicate.sh, post-replicate.sh (aggregate replicate exit code), pre-restore.sh, post-restore.sh (restore exit code), pre-snapshot-export.sh, post-snapshot-export.sh (snapshot export exit code), pre-forget-preview.sh, post-forget-preview.sh (forget preview exit code), pre-mount-snapshot.sh, post-mount-snapshot.sh (mount snapshot exit code, called after unmount).
Do not embed secrets in image tags or public Hub descriptions. Use env files, secrets, or mounts excluded from git. Treat rclone.conf and msmtprc as sensitive; the repository ships only .example templates for those files.
Image lineage: derived from lobaro/restic-backup-docker.
Content type
Image
Digest
sha256:a16b34143…
Size
50.5 MB
Last updated
4 months ago
docker pull marc0janssen/restic-backup-helper