Two bar charts measured 3 November 2026 with restic 0.16.4 on Ubuntu 24.04 with 2 vCPU and 4 GB, 3.3 GiB of data. Backup time: first run 37 seconds, nothing changed 2.5 seconds, 112 MiB changed 6 seconds. Restore time: one file 2.3 seconds, everything on the same server 35.5 seconds, everything on a brand new server 41 seconds.

A backup you haven't restored is only a hope, so we did what a bad day looks like: we shut the server down, built a new one with nothing on it but restic, two small files and network access to the store, and had 3.44 GiB and 86,641 files back in 41 seconds. Every SHA-256 checksum matched. This guide gives you the same setup: encrypted backups to S3-compatible storage, on a daily timer, with old backups pruned and an alert when a run fails.

We ran every step on 3 November 2026, on a clean Ubuntu 24.04 server with 2 vCPU and 4 GB of memory, using restic 0.16.4 from Ubuntu's own packages. One thing to keep in mind when reading the times: our test store was on a local network at 1.27 Gbit/s. Yours is probably further away, and we work out what that means below.

Step 1: install and get your storage details

Create a bucket and an access key with your storage provider. Any S3-compatible store should work the same way, as the only part that changes is the address.

bash
apt-get update
apt-get install -y restic
restic version     # restic 0.16.4

That took 14 seconds. Put the settings in one private file:

bash
cat > /etc/restic.env <<'E'
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
RESTIC_REPOSITORY=s3:https://your-endpoint/your-bucket/server1
RESTIC_PASSWORD_FILE=/etc/restic.password
RESTIC_COMPRESSION=auto
E
openssl rand -base64 32 > /etc/restic.password
chmod 600 /etc/restic.env /etc/restic.password

Copy both files somewhere that isn't this server, now. Our new-server restore needed nothing else. If you lose the password, restic can't get your data back, and neither can anyone else.

Step 2: first backup

bash
set -a; . /etc/restic.env; set +a
restic init
restic backup /srv/data

Point it at your own folders. Our test data was the Linux 6.12 source (1.6 GB, lots of small files), a 1 GB SQL dump and 1 GB of random data that can't be compressed, standing in for photos.

What we ranTimePeak memoryResult
First backup, 3.34 GiB, 86,639 files37 s229 MB1.48 GiB stored
Again, nothing changed2.5 s73 MB0 B added
After changing 200 files, 1% more dump, 2 new 50 MB files6 s199 MB102 MiB stored
restic check, structure only1.7 s73 MBNo errors
restic check --read-data, reads everything19.5 s155 MBNo errors
forget --prune with the policy below5.4 s72 MB3 snapshots down to 2

Backing up a live database's files doesn't give you a usable copy. Dump it to a file first and back up the dump, as in our PostgreSQL guide.

Step 3: the encryption is real

Two quick checks. With the wrong password, restic stops: Fatal: wrong password or no key found. And a test email address we searched for was in the source dump, but in 0 of the 104 files in the bucket. Whoever runs your storage can't read your data.

Step 4: run it every day

bash
cat > /usr/local/bin/restic-backup <<'E'
#!/bin/bash
set -euo pipefail
set -a; . /etc/restic.env; set +a
restic backup /srv/data --exclude-caches --tag auto
restic forget --tag auto --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check
E
chmod 700 /usr/local/bin/restic-backup

cat > /etc/systemd/system/restic-backup.service <<'E'
[Unit]
Description=restic backup
OnFailure=backup-alert.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/restic-backup
Nice=10
IOSchedulingClass=idle
E

cat > /etc/systemd/system/restic-backup.timer <<'E'
[Unit]
Description=daily restic backup
[Timer]
OnCalendar=*-*-* 03:15
RandomizedDelaySec=20m
Persistent=true
[Install]
WantedBy=timers.target
E

Persistent=true runs a backup at the next boot if the server was off at 03:15. The retention above keeps at most 17 backups: 7 daily, 4 weekly and 6 monthly. A full run of backup, prune and check took 36 seconds on our data.

Step 5: hear about failures

A backup that fails silently is how people find out too late. OnFailure above starts this service when a run fails. Replace the address with your own alert hook:

bash
cat > /etc/systemd/system/backup-alert.service <<'E'
[Service]
Type=oneshot
ExecStart=/usr/bin/curl -fsS -m 10 -X POST -d "backup failed on %H" https://your-alert-hook
E

systemctl daemon-reload
systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer
systemctl start restic-backup.service     # one run now, to prove it works

To test it, we stopped the store and ran the service. It failed within 10 seconds, which was how often we checked, and the alert arrived once: backup failed on ubuntu. With the store back, the next run worked in 29 seconds with no cleanup.

This alert has a gap: a server that's off, or a timer that's been disabled, sends nothing. To catch that, have the backup ping an outside check service on success, one that warns you when the ping stops. We didn't test a particular service for this. Our monitoring guide covers the same blind spot for alerts in general.

Step 6: restore

RestoreTimeCheck
One 50 MB file2.3 scmp says identical
Everything, same server35.5 sdiff -r says identical (the diff took 74 s)
Everything, brand new server41 s, plus 11 s to install resticAll 86,641 SHA-256 checksums match

One file:

bash
set -a; . /etc/restic.env; set +a
restic snapshots
restic restore latest --target /tmp/restore --include /srv/data/uploads/photo3.bin

A new server, with restic.env and restic.password copied back to /etc/ and chmod 600:

bash
apt-get update && apt-get install -y restic
set -a; . /etc/restic.env; set +a
restic restore latest --target /

Restoring to / puts files back in their original paths and overwrites what's there, so use a temporary folder on a server that's still in use. Put a restore on your calendar every few months too: if the steps have gone stale, that's the day to find out.

What a slower link does to these times

Our store was next door, so these figures show restic's own speed. Over the internet, the data transfer becomes the slow part. Our bucket held 1.6 GB, so a restore needs that much downloaded. This is arithmetic, not a measurement: about 2 minutes at 100 Mbit/s, and about 13 seconds at 1 Gbit/s, plus restic's own work. Later backups stay quick on any link, since only changes get sent. Time your first restore on your real store.

One risk to think about

This setup keeps a key that can delete backups on the server, because pruning needs that. Anyone who takes over the server can wipe them too. If that worries you, look at object lock or versioning at your provider, or restic's append-only rest-server with pruning run from another machine. We didn't test those here.

Which of our servers can run this

All of them. On a 2 vCPU, 4 GB server under $20, restic and the timer worked, including the Nice and IOSchedulingClass lines. That test backed up to a local folder rather than a bucket, since it only checked what these servers allow. Backups go outward, so the limit on inbound ports under $20 doesn't matter. Peak memory of 229 MB fits every plan, though a much larger data set will use more.

Sources

Our own runs on Ubuntu 24.04, 3 November 2026, under QEMU/KVM with 2 vCPU, 4 GB and disk capped at 3,000 IOPS and 150 MB/s. restic 0.16.4 to a versitygw 1.10.0 S3 gateway on a second server over a 1.27 Gbit/s link (measured with iperf3), plain HTTP as it was a private lab network; use HTTPS on real stores. We tested the small server limits on 3 November too. Docs: restic: preparing a repository, restic: removing snapshots, systemd.timer.

Order a server and set this up before you have anything to lose.

See the servers