Quick start¶
The fastest path from zero to "container is backing up every night and yelling when something breaks".
Do not commit secrets
Real RESTIC_PASSWORD, cloud API keys, SMTP passwords and webhook tokens
must live in a gitignored .env, a Docker secret, or an orchestrator
secret store — never inline in a committed docker-compose.yml.
Minimal Docker run¶
docker run -d \
--name restic-backup-helper \
-e RESTIC_REPOSITORY='s3:https://s3.amazonaws.com/my-bucket/restic' \
-e RESTIC_PASSWORD='use-a-strong-secret' \
-e RESTIC_TAG='daily' \
-e BACKUP_CRON='0 2 * * *' \
-e BACKUP_ROOT_DIR='/data' \
-v /srv/backup-src:/data:ro \
-v restic-config:/config \
marc0janssen/restic-backup-helper:latest
What you just told the container:
- back up
/data(mounted read-only from/srv/backup-src) - at
02:00every day inEurope/Amsterdam(the defaultTZ) - to an S3 bucket
- tagging every snapshot
daily
That's it — no further setup needed for the happy path. restic init is
automatic on first run if the repository does not yet exist (restic cat
config exits with code 10; any other non-zero exit aborts startup so a
transient outage never silently re-initialises a healthy remote).
Minimal Docker Compose¶
A more idiomatic shape with secrets and a named volume for logs:
services:
restic-backup:
image: marc0janssen/restic-backup-helper:latest
container_name: restic-backup-helper
hostname: backup-node
restart: unless-stopped
environment:
RESTIC_REPOSITORY: s3:https://s3.amazonaws.com/my-bucket/restic
RESTIC_PASSWORD_FILE: /run/secrets/restic_password
RESTIC_TAG: daily
BACKUP_CRON: "0 2 * * *"
BACKUP_ROOT_DIR: /data
TZ: Europe/Amsterdam
secrets:
- restic_password
volumes:
- /srv/documents:/data:ro
- ./config:/config:ro
- backup-logs:/var/log
- restic-cache:/.cache/restic
secrets:
restic_password:
file: ./restic.password
volumes:
backup-logs:
restic-cache:
chmod 600 ./restic.password and add it to .gitignore.
Verify the install¶
Once the container is up, run any of the following from your host:
docker logs -f restic-backup-helper
docker exec -ti restic-backup-helper /bin/doctor
docker exec -ti restic-backup-helper /bin/backup
docker exec -ti restic-backup-helper restic snapshots
docker exec -ti restic-backup-helper cat /var/log/last-backup.json
docker logstails/var/log/cron.log— cron ticks, hook output, masked repository URLs and worker exit codes go there./bin/doctoris the read-only support bundle: env, paths, repository probe, replicate validation, hook status, recent JSON summaries./bin/backupruns the backup worker right now, with the same code path as the cron job.last-backup.jsonis the structured per-run summary; see JSON summaries.
Add notifications¶
Pick whichever combination you actually monitor:
environment:
MAILX_RCPT: ops@example.com
MAILX_ON_ERROR: "ON"
volumes:
- ./config/msmtprc:/etc/msmtprc:ro
Mail subjects look like
[OK] Backup backup-node · 5m12s · 1.234 MiB new (snap a1b2c3d4)
or [FAIL 12] Check backup-node · 7s · …. See
Mail notifications.
environment:
WEBHOOK_URL: https://hc-ping.com/00000000-0000-0000-0000-000000000000
WEBHOOK_TIMEOUT: "15"
WEBHOOK_ON_ERROR: "OFF" # POST every run; ON = only on failure
The webhook POST body is the same JSON as last-<job>.json. Works
out of the box with healthchecks.io, Slack, Discord, Gotify, ntfy
and any custom JSON endpoint. See Webhooks.
environment:
METRICS_DIR: /var/log/textfile_collector
volumes:
- ./metrics:/var/log/textfile_collector
Point a node-exporter --collector.textfile.directory at ./metrics.
See Prometheus metrics.
What to read next¶
- Architecture — entrypoint, cron wiring,
worker scripts,
locked_run. - Filesystem layout — which paths matter and what to mount.
- Backup worker — full configuration surface for the default cron job.
- Hardening —
cap_drop,read_only: true+ tmpfs,no-new-privileges, KubernetessecurityContext.