Automated Off-Site Backups With Restic and Cron
Here is an uncomfortable fact about self-hosting: the moment you run a single service on your own hardware, you become your own backup admin, and the default approach of "I'll copy the folder occasionally" does not survive contact with reality. The reliable, low-effort pattern that has held up for years is restic: an open-source backup tool that produces encrypted, deduplicated snapshots and runs happily on a schedule via cron or a systemd timer.
Restic is not a disk-mirroring tool and it is not a full image clone. It is a snapshot backup tool. That distinction matters: snapshots let you travel back in time, keep many versions, deduplicate so the incremental history stays small, and store everything encrypted so an off-site copy sitting somewhere you do not control is still your data.
Initialize a repository
Everything restic does happens against a repository, which holds all your snapshots. Creating one is a single command, and it needs two things: a location (local path or object storage) and a password that encrypts and unlocks the data.
restic init --repo /mnt/backup-hdd/restic-repo
You will be prompted for a repository password. Set it once, write it down somewhere safe, and never lose it, because the password is what decrypts your data. The cleaner way to automate this (so cron does not prompt you at 2am) is to export the repository and password as environment variables:
export RESTIC_REPOSITORY="/mnt/backup-hdd/restic-repo"
export RESTIC_PASSWORD_FILE="/root/.restic-password"
You can keep the password in a file instead of an environment variable, which avoids it showing up in process listings or shell history. Restic also supports RESTIC_PASSWORD_COMMAND for pulling the password from a secret manager.
You can point the repository at local disk or at object storage such as Amazon S3 or Backblaze B2, which is how you get a genuinely off-site copy. Restic supports many backends, so the command stays identical whether the repository is a local folder or a bucket thousands of miles away.
Back up on a schedule
Restic provides no daemon and no scheduler of its own. It is a command-line tool, and you wire the schedule yourself with cron or a systemd timer. That is the right split: the backup tool stays simple, and your OS handles the timing.
A minimal backup command:
restic backup /home /etc --exclude-file=/root/.restic-excludes
This snapshots /home and /etc, skipping anything listed in the exclude file, which is where you put the predictable junk you do not want to back up, like build directories and cache folders.
To make it a real backup system, wrap it in a small script that does three things in order: back up, prune old snapshots, and verify integrity:
#!/usr/bin/env bash
set -e
export RESTIC_REPOSITORY="/mnt/backup-hdd/restic-repo"
export RESTIC_PASSWORD_FILE="/root/.restic-password"
restic backup /home /etc --exclude-file=/root/.restic-excludes
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
restic check
forget deletes snapshots outside your retention policy, and --prune frees the underlying storage the deleted snapshots occupied. check verifies the repository structure is intact. Put that script at /usr/local/bin/backup.sh, make it executable, and schedule it with cron:
0 2 * * * /usr/local/bin/backup.sh
That runs the backup at 2am every day. The retention policy in the example keeps seven daily, four weekly, and twelve monthly snapshots, which gives you a rolling history without unbounded growth. Adjust the numbers to match how much history you actually need; the syntax is expressive enough to express most policies.
The verification step everyone skips
Here is the part that separates a backup from a ceremony: a backup you have never restored is a guess. Restic makes this easy with its snapshots command and a restore test.
restic snapshots
lists every snapshot in the repository with its date and contents, so you can confirm the automated runs are actually landing. Then, at least once, do a real restore into a scratch directory:
restic restore latest --target /tmp/restore-test
Confirm the files come back intact, read a file you care about, and you have earned the right to trust your backup. Restic's find and restore commands also let you pull a single file or directory out of a snapshot, which is the common real-world need: "I deleted one file yesterday."
What this buys you
- Off-site copies. Point the repository at object storage and your data exists somewhere other than the box it lives on, which is the entire point of a backup.
- Encryption. The repository is encrypted, so the storage provider or a stolen hard drive cannot read your data without the password.
- Deduplication. Repeated runs only store what changed, so nightly snapshots stay small even for large datasets.
- Point-in-time history.
restic snapshotsgives you a time machine back as far as your retention policy reaches.
Backups are the least glamorous part of running your own stack, and the most important. A restic repository plus a cron line is almost all of a serious backup strategy, provided you remember the one step people routinely skip: restore something from it, on purpose, before you need to.
Key takeaways
- restic produces encrypted, deduplicated snapshots and stows them in a repository you can point at local disk or off-site object storage.
restic initcreates the repository; setRESTIC_REPOSITORYandRESTIC_PASSWORD_FILEso cron can run without prompting.- A scheduled script runs
restic backup, thenforget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prunefor retention, thenrestic checkfor integrity. - Verify your setup by listing snapshots and doing a real restore into a scratch directory before you trust it.