A lightweight, composable backup system built around restic and tag-driven workflows.
This repository provides shell scripts for defining, running, and retaining backups using a simple JSON configuration model. It is designed for operators who want predictable, scriptable backups without introducing heavy orchestration tools.
-
Tag-driven backups Each backup job is grouped under a tag (e.g.,
docker,system,media). -
Declarative configuration Backup jobs and retention policies are defined in a single JSON file.
-
Separation of concerns Backup, retention (
forget), and pruning are executed independently. -
Pre/Post hooks Optional scripts allow coordination with running systems (e.g., stopping services, creating snapshots).
- Tag-based backup execution
- Per-tag retention policies
- JSON-based configuration
- Pre/post execution hooks
- Safe shell practices (
set -euo pipefail) - Compatible with any restic backend (S3, B2, local, etc.)
- Designed for cron/systemd/monit usage
- monit check script included
.
├── backup-tags.json # Backup job definitions
├── backup_common.sh # Shared validation and helper functions
├── tag_backup.sh # Run backup for a single tag
├── tag-forget.sh # Apply retention policy for a tag
├── backup-all.sh # Run all backups
├── forget-all.sh # Apply retention across all tags
├── backup-prune.sh # Run restic prune
└── check-recent-backups.sh # Monit script for alerting when backups have not occurred
resticjq- Bash (GNU bash recommended)
Create an environment file (example: /etc/restic-env):
export RESTIC_REPOSITORY="s3:s3.us-west-002.backblazeb2.com/your-bucket"
export RESTIC_PASSWORD_FILE="/etc/restic-password"
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."Ensure it is readable by the user running the scripts.
Edit backup-tags.json:
{
"docker": {
"job_description": "Docker volumes",
"backup_path": "/volume1/docker",
"pre_script": "/path/to/pre-script.sh",
"post_script": "/path/to/post-script.sh",
"keep": {
"daily": 3,
"weekly": 1,
"monthly": 6
}
}
}| Field | Required | Description |
|---|---|---|
job_description |
No | Human-readable description |
backup_path |
Yes | Path to back up |
pre_script |
No | Script to run before backup |
post_script |
No | Script to run after backup |
keep |
No | Retention policy (restic forget) |
If keep is omitted, no retention policy will be applied for that tag.
./tag_backup.sh <tag>./tag-forget.sh <tag>./backup-all.sh./forget-all.sh./backup-prune.sh# Nightly backups
0 1 * * * flock -n /var/lock/restic.lock ./backup-all.sh
# Retention cleanup
0 3 * * * flock -w 1200 /var/lock/restic.lock ./forget-all.sh
# Weekly prune
30 3 * * 0 flock -w 1200 /var/lock/restic.lock ./backup-prune.shUsing flock is recommended to prevent overlapping runs.
Copy the file check-recent-backups.sh to /usr/local/bin or wherever your custom monit scripts run from.
Example monit config entry:
## Check for recent successful backups
check program backup-health with path /usr/local/bin/check_recent_backups.sh every 180 cycles
if status !=0 then alert-
Explicit over implicit All backup behavior is defined in configuration, not hidden in scripts.
-
Composable operations Backup, retention, and pruning can be run independently or combined.
-
Operational safety Pre/post hooks allow safe handling of stateful systems.
-
Minimal dependencies Only
restic,jq, and bash are required.
- Scripts are intended to be run with sufficient permissions to access backup paths.
- Ensure pre/post scripts are idempotent and executable.
- Logging should be handled externally (cron, systemd, or monit).