Restic Backups for a Self Hosted Server Step by Step
Your self hosted server has been running for months, and the only copy of its mail, configs and databases sits on the same disk that will one day fail.

Your self hosted server has been running for months, and the only copy of its mail, configs and databases sits on the same disk that will one day fail. Restic backups fix that in about an hour of setup, with encrypted, deduplicated snapshots you can restore from any machine.
This guide walks through a setup I run on a small Debian box: restic installed from the distro package, a repository on an off-site storage bucket, a nightly systemd timer, a retention policy and, most importantly, a restore test. Every command below works on Debian 12 and Ubuntu 24.04 without changes.
Why restic backups suit a small server
Restic is a single binary with no daemon and no server component. It splits files into chunks, stores each unique chunk once, and encrypts everything with a key derived from your repository password. That means three things matter in practice:
- Deduplication is real. A server with 40 GB of data and 30 daily snapshots typically uses 45 to 55 GB of repository space, not 1.2 TB.
- The storage provider sees nothing useful. File names, contents and directory structure are all encrypted before upload.
- Restores do not need the original machine. Any computer with restic, the repository location and the password can pull files back.
The tradeoff is that the password is the whole game. Lose it and the data is gone. Write it into your password manager and on paper before you run a single backup.
Step 1: Install restic and create the repository
Install from the package manager, then let restic update itself to the current release, since distro versions often lag by a year or more:
- Install: sudo apt install restic
- Update the binary: sudo restic self-update
- Check: restic version should report 0.17 or newer
Pick a repository backend. For off-site storage, any S3 compatible bucket works, and a second server you control over SFTP works too. Costs for 50 GB on budget object storage usually land between 0.30 and 1.20 dollars a month, which is cheaper than the coffee you will drink while setting this up.
Create a file at /etc/restic/env with mode 600, owned by root, holding the environment variables restic reads:
- RESTIC_REPOSITORY set to your bucket path, for example s3:your-provider-endpoint/bucket-name/server1
- RESTIC_PASSWORD_FILE pointing to /etc/restic/password
- AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY for the bucket credentials
Generate the password with openssl rand -base64 32, save it into /etc/restic/password with mode 600, then load the environment and run restic init. You should see a line confirming the repository was created. If you keep project secrets in shell files elsewhere, the same discipline from setting up direnv for per project environment variables applies here: one file, tight permissions, never committed to git.
Step 2: Decide what to back up and what to skip
Backing up the whole root filesystem is tempting and mostly wasteful. A fresh OS install takes ten minutes. What takes days to rebuild is your data and configuration. On a typical mail and web box, the list looks like this:
| Path | Why it matters | Typical size |
|---|---|---|
| /etc | Every service config, certificates, cron jobs | 5 to 20 MB |
| /var/vmail or /var/mail | Mailboxes | 2 to 30 GB |
| /srv or /var/www | Site files and uploads | 1 to 10 GB |
| /home | User data and dotfiles | Varies |
| Database dump folder | Consistent copies of live databases | 100 MB to 5 GB |
Put exclusions into /etc/restic/excludes: cache folders, node_modules.cache, temporary upload directories and container image layers. Pass that file to restic with the exclude-file option.
Never back up a running database by copying its data directory. Dump it first. For PostgreSQL, pg_dumpall into a dated file in /var/backups/db. For MariaDB, mariadb-dump with the all-databases and single-transaction options. Run the dump in the same script, just before restic, so the snapshot always holds a fresh, consistent copy.
Step 3: Schedule nightly snapshots with systemd
Create a small script at /usr/local/sbin/restic-backup.sh that does four things in order: dump databases, run restic backup on your path list, run restic forget with a retention policy, and run restic check with a small read sample. Mark it executable.
A retention policy that works for most small servers, passed as options to restic forget:
- keep-daily 7 keeps one snapshot for each of the last 7 days.
- keep-weekly 5 keeps one per week for about a month.
- keep-monthly 12 keeps one per month for a year.
- prune removes chunks no snapshot references anymore.
Then create a service unit that runs the script with EnvironmentFile=/etc/restic/env, and a timer unit with OnCalendar=*-*-* 03:15:00 and RandomizedDelaySec=900 so the job does not collide with other 3 a.m. jobs. Enable and start the timer with systemctl enable, then start restic-backup.timer, and confirm it with systemctl list-timers.
The first run uploads everything and may take an hour or more on a home connection. After that, nightly runs on a server with modest churn finish in two to five minutes.
The advice I disagree with: prune every night
Many guides tell you to run forget with prune after every backup. On a small repository it works, but on object storage it is often the wrong call. Pruning rewrites pack files, which means downloads, uploads and API calls, and some providers charge for early deletion or egress. On a 50 GB repository, a nightly prune can generate more billable traffic than the backup itself.
A better pattern: run forget without the prune option every night, which only removes snapshot references and costs almost nothing, then prune once a week on Sunday. You lose nothing in safety, because the data from forgotten snapshots is simply not reclaimed for a few days. Restic 0.17 also supports a max-unused setting of 10% so prune only repacks when enough dead space has built up.
Step 4: Test a restore before you trust anything
A backup you have never restored is a hope, not a backup. Once a month, put 20 minutes on the calendar and do this:
- Run restic snapshots and confirm last night's snapshot exists with the expected paths.
- Restore a single known file: restic restore latest with /tmp/restore-test as the target and /etc/postfix/main.cf as the only included path, then diff it against the live file.
- Restore last night's database dump into a scratch database and run a quick count on one table.
- Once a quarter, do the full test from a different machine, using only the password from your password manager.
Add failure alerts too. A timer that silently fails for three weeks is common. The simplest fix is a push monitor: the script pings a URL only when it finishes successfully, and you get an alert when the ping stops. If you already followed the guide on uptime monitoring with Uptime Kuma, its push monitor type does exactly this.
For more step by step setups like this one, browse the guides collection.
Frequently asked questions
How much storage do restic backups need?
Plan for roughly 1.2 to 1.4 times your source data size with a daily, weekly and monthly retention policy. Deduplication keeps unchanged files from being stored twice, so growth comes mostly from files that change often, such as mailboxes and database dumps. Compressed repositories, the default since restic 0.14, shrink text heavy data further.
Can I back up to two locations with restic?
Yes. The simplest approach is a second repository and a second environment file, with the script running backup against each one. Restic also has a copy command that duplicates snapshots between repositories, which saves reading the source disk twice.
What happens if I lose the restic password?
The repository becomes unreadable, permanently. There is no recovery service and no back door, because the encryption key is derived from that password. Store it in your password manager and keep a printed copy somewhere physically separate from the server.
More in Guides
Guides
Uptime Monitoring for a Small Website With Uptime Kuma
Your site went down on Saturday night and you found out on Monday from a customer email.
Guides
Expired Domain Recovery Steps Before Your Email Goes Dark
Monday, 08:40. Two customers write to say their invoices bounced, the contact form returns an error, and the website shows a...
Guides
How to Move a Website to a New Host Without Downtime
Most site migrations that go wrong do not fail during the file copy. They fail at 11 p.m.