Backs up Backrest config.json to an S3-compatible object store.
On each run, the service reads the local config file, computes its SHA-512 hash, and encrypts the content locally with age. It uploads a backup with the hash stored in object metadata, even if the file has not changed, optionally verifies recovery, removes older backups after a successful run, and sends an optional Healthchecks ping.
- A Backrest
config.jsonfile - An S3-compatible bucket
- An
agepassphrase file
Cleanup requires permission to list and delete object versions when the provider supports version listing. For providers without that API, it falls back to listing and deleting objects. Permission and deletion errors are reported as backup failures.
See .env.example for a ready-to-copy example.
Required by the application:
CONFIG_PATHS3_BUCKETS3_ENDPOINTS3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEYAGE_PASSPHRASE_FILECRON_SCHEDULEunlessRUN_ONCE=true
Optional variables:
S3_PREFIXdefaults to empty in the application. The includeddocker-compose.ymlsetsbackrest.AWS_REGIONdefaults tous-east-1TZdefaults toUTCRUN_TIMEOUTdefaults to2mRUN_ONCEdefaults tofalseVERIFY_AFTER_UPLOADdefaults tofalse; when enabled, verifies the restored SHA-512 hash before cleanupS3_SESSION_TOKENif required by your providerHEALTHCHECKS_URLis optional; when unset, healthchecks pings are disabled
- Create
.envfrom.env.exampleand set your values:CONFIG_PATHS3_BUCKETS3_ENDPOINTS3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEYAGE_PASSPHRASE_FILECRON_SCHEDULE(orRUN_ONCE=true)- optional:
HEALTHCHECKS_URL,S3_PREFIX,AWS_REGION,TZ,RUN_TIMEOUT,VERIFY_AFTER_UPLOAD,S3_SESSION_TOKEN
- Put your Backrest config where you want and mount it read-only in
docker-compose.yml.- host path example:
./config.json - container path example:
/data/config.json - set
CONFIG_PATH=/data/config.json
- host path example:
- Create the passphrase secret file (default:
./age_passphrase.txt) and keepAGE_PASSPHRASE_FILEaligned with the secret mount path.- default in compose:
/run/secrets/age_passphrase - if you change secret target path, update
AGE_PASSPHRASE_FILEtoo
- default in compose:
- Start the service:
docker compose up -dThis container runs as root so it can read a config.json produced by the official Backrest container without
permission issues.
Backrest's Docker image runs as the default container user and uses /root paths internally. Its config store writes
config.json with mode 0600, creates timestamped config.json.bak.* copies, then atomically rewrites the live file.
In practice that makes the file application-managed and typically root-owned, so this backup container also runs as
root when mounting the file read-only.
Set VERIFY_AFTER_UPLOAD=true to download each newly uploaded object, fully decrypt it with the configured
passphrase, and compare its SHA-512 hash with the hash of the original configuration snapshot. The original
snapshot hash is used even if the live file changes during the run. Verification keeps plaintext in memory and
leaves the live configuration untouched.
Older backups are deleted only after verification succeeds. A download, decryption, hash mismatch, or timeout
failure preserves older backups and the newly uploaded object, logs the failed stage and object key, and sends
a failure healthcheck when configured. Successful verification is logged as "verified":true.
For a one-time backup and recovery check, with the required environment variables already exported:
RUN_ONCE=true VERIFY_AFTER_UPLOAD=true go run ./cmd/agentThe command exits with a nonzero status on failure. This performs a normal backup, including cleanup after
successful verification. Use a separate S3_PREFIX to isolate a test run from your normal backups.
Verification requires permission to download objects (s3:GetObject) and adds a download and decryption to each
run. These steps share RUN_TIMEOUT with the rest of the backup; increase it if necessary. This checks recovery
of the configuration file; it does not start Backrest with that configuration.
To restore a backup:
- Download the
.json.ageobject from your bucket. - Decrypt it with the same passphrase used for backup.
- Replace your Backrest
config.jsonwith the decrypted file.
To decrypt using the age CLI:
age -d -o config.json config-backup-YYYY-MM-DDTHH-mm-ss-RANDOM.json.ageNew backup names include a random suffix so consecutive runs cannot overwrite a previous backup before verification. Backups using the older timestamp-only names can still be restored with the same command.
This project exists to back up the Backrest configuration, which contains all credentials for restic repositories. Losing that file also means losing access to all backups. Thanks to Backrest for providing an excellent UI and management layer for restic, which provides the amazing backup engine, and age for providing simple, solid file encryption.