4.7 KiB
Scripting and command contract
Use this reference when writing an unattended restic job, parsing output, or deciding how automation reacts to failures.
Keep the wrapper small and honest
A safe wrapper supplies credentials through a protected mechanism, calls one operation, preserves its exit code, and emits bounded evidence. It must not silently initialize a missing repository, turn every error into success, or chain destructive maintenance after a failed backup.
#!/usr/bin/env bash
set -euo pipefail
: "${RESTIC_REPOSITORY:?repository must be configured}"
: "${RESTIC_PASSWORD_FILE:?protected password file must be configured}"
restic backup /data --tag files --tag production
restic snapshots --json > /var/lib/restic/latest-snapshots.json
Use --stdin-from-command for database dumps or other generated content when possible. It preserves the producer command's exit status. If using a pipe to backup --stdin, enable set -o pipefail; otherwise a failed producer can leave a misleading successful backup process.
The documented shape is one producer command after --:
restic backup --stdin-filename production.sql --stdin-from-command -- \
mysqldump --host example production
For compression or another transformation, pass a tested wrapper program as that
producer command. If the wrapper uses a shell pipeline, it must enable pipefail
and return a failing producer's status. Do not append | gzip outside the command after
--stdin-from-command: that pipe is handled by the invoking shell, not by
restic's child process, and can transform restic's own output rather than the
database export.
Exit codes are a contract
Restic documents these general meanings. A command can define more specific behavior, and future versions can add codes. Treat every unrecognized nonzero code as a failure.
| Exit code | Meaning | Automation response |
|---|---|---|
| 0 | Success | Record snapshot freshness and duration, not merely process success |
| 1 | Fatal error | Alert and preserve redacted stderr |
| 2 | Go runtime error | Alert as execution failure |
| 3 | Partial backup because some source files were unreadable; or some forget removals failed | Treat backup as incomplete; inspect affected paths before declaring protection healthy |
| 10 | Repository does not exist (0.17.0+) | Do not auto-initialize without an explicit new-repository directive |
| 11 | Failed to lock repository (0.17.0+) | Determine whether an operation is active; retry/wait or investigate stale lock |
| 12 | Wrong password (0.17.1+) | Inspect secret retrieval and intended repository, without logging a secret |
| 130 | Interrupted by SIGINT/SIGTERM (0.19.0+) | Treat as interrupted; rerun deliberately and inspect snapshots |
JSON contract
Use --json only for commands that support it. Main JSON data goes to stdout; fatal errors may yield a final exit_error JSON object on stderr. Long-running commands such as backup, check, restore, and diff can emit JSON Lines distinguished by message_type.
Do not assume JSON schemas are closed. Restic documents that fields and message types can be added. Parsers must ignore unknown fields/message types, retain the exit code, and avoid treating an absence of a known optional field as an error. prune does not support JSON, so forget --prune --json mixes JSON and text: keep prune in a separate operational path if a machine needs clean structured output.
Locks and retries
A repository lock is protection, not a routine error to erase. For expected short contention, --retry-lock can wait. For a persistent lock, inspect scheduler state and live restic processes before considering unlock. Use --no-lock only for read-only operations when the operational risk of an unlocked read is understood; it is not an escape hatch for mutations.
Configuration safeguards
- Use absolute source and exclude-file paths in scheduled jobs.
- Keep
TMPDIRcapacity in the job's preflight; temporary pack creation requires space. - Pin an intended restic version for critical automation and run
restic <command> --helpduring upgrades. - Keep backup, retention/prune, sampled/full checks, and restore drills as separately observable jobs.
- Do not make
restic cat config || restic initan unattended default. An authentication or backend error can be mistaken for a missing repository.
Sources
- Scripting, exit codes, JSON output, and compatibility guidance: https://restic.readthedocs.io/en/stable/075_scripting.html (accessed 2026-07-15)
- Backup stdin behavior and source errors: https://restic.readthedocs.io/en/stable/040_backup.html (accessed 2026-07-15)
- Locks and recovery diagnosis: https://restic.readthedocs.io/en/stable/077_troubleshooting.html (accessed 2026-07-15)