Which tool
Only restic writes straight to S3 storage, which is why the steps below use it.
| restic | BorgBackup | rsync | |
|---|---|---|---|
| Sends to | S3, SFTP, local disk and more | A local disk or a host over SSH. Its quickstart does not mention S3 | A folder on another host over SSH |
| Encrypted | always | yes, you pick the mode at borg init | no, it copies files as they are |
| Take it when | You have an S3 bucket | You have a second server with SSH | You need a plain mirror of a folder |
Steps
-
List what to save
Save what you cannot rebuild: configs in
/etc, your project folders, uploads and a dump of each database. Skip caches,node_modulesand anything a package manager can download again. Dump a running database instead of copying its files.$ sudo mkdir -p /var/backups/db $ sudo -u postgres pg_dump mydb | gzip > /var/backups/db/mydb.sql.gz
-
Make a bucket and a key
Create a bucket for backups only, and a key that can reach that bucket and nothing else. If the server is ever broken into, the thief gets that one bucket, not your account. Any S3 provider works: Amazon S3, Cloudflare R2, Backblaze B2 and others. Uno cloud storage does not give S3 access keys yet, so restic cannot write to it; on Uno, the daily disk copies below are the layer Uno keeps for you.
-
Install restic
$ sudo apt-get install restic $ restic version
-
Keep the keys and the password in one root-only file
restic encrypts the repository with a password. Make a long one and keep a second copy in a password manager, outside the server: without it the snapshots cannot be read. The names below are the ones restic reads from the environment.
$ sudo mkdir -m 700 /etc/restic $ openssl rand -base64 32 | sudo tee /etc/restic/password $ sudo nano /etc/restic/env RESTIC_REPOSITORY=s3:https://s3.example.com/my-backups RESTIC_PASSWORD_FILE=/etc/restic/password AWS_ACCESS_KEY_ID=<your access key> AWS_SECRET_ACCESS_KEY=<your secret key> $ sudo chmod 600 /etc/restic/env /etc/restic/password
Replace
s3.example.comwith your provider's S3 address andmy-backupswith your bucket. Write it as above: the bucket comes after the host, not inside the host name. -
Create the repository and run the first backup
The first run uploads everything. Later runs send only what changed.
$ sudo bash -c 'set -a; . /etc/restic/env; set +a; restic init' $ sudo bash -c 'set -a; . /etc/restic/env; set +a; restic backup /etc /home/dev/app /var/backups/db'
-
Put it in a script and on a timer
The script dumps the database, backs up, then keeps seven daily, four weekly and six monthly snapshots and removes the rest. Name your own numbers with
--keep-daily,--keep-weeklyand--keep-monthly. While restic prunes, the repository is locked and no backup can finish, so run it at night.$ sudo nano /usr/local/bin/backup.sh #!/usr/bin/env bash set -euo pipefail set -a; . /etc/restic/env; set +a sudo -u postgres pg_dump mydb | gzip > /var/backups/db/mydb.sql.gz restic backup /etc /home/dev/app /var/backups/db restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune $ sudo chmod 700 /usr/local/bin/backup.sh
A systemd timer runs the script every night.
Persistent=truemakes systemd run a missed backup after the machine was off.$ sudo nano /etc/systemd/system/backup.service [Unit] Description=Back up to S3 [Service] Type=oneshot ExecStart=/usr/local/bin/backup.sh $ sudo nano /etc/systemd/system/backup.timer [Unit] Description=Nightly backup [Timer] OnCalendar=*-*-* 03:00:00 RandomizedDelaySec=15min Persistent=true [Install] WantedBy=timers.target $ sudo systemctl daemon-reload $ sudo systemctl enable --now backup.timer
-
Restore something, today
Restore the latest snapshot into an empty folder and open a file from it. This is the only step that proves the backup. Then ask restic to check the repository.
$ sudo bash -c 'set -a; . /etc/restic/env; set +a; restic restore latest --target /tmp/restore-test' $ ls /tmp/restore-test/etc $ sudo bash -c 'set -a; . /etc/restic/env; set +a; restic check'
Check that it works
Next morning, check when the timer ran and list the snapshots. You should see one new snapshot a day.
$ systemctl list-timers backup.timer $ journalctl -u backup.service -n 20 $ sudo bash -c 'set -a; . /etc/restic/env; set +a; restic snapshots'
The short way: Uno copies the disk for you
On every paid plan, Uno copies your computer's disk once a day and keeps each copy 7 days, off the server. A sleeping or stopped computer gets its copy too. You turn nothing on. Restore as new makes a new computer from a copy and leaves the old one as it is. See how copies work.
Small: $5 a month
Why keep both layers
| Uno daily copy | Your restic snapshots | |
|---|---|---|
| What it holds | The whole disk | The files and dumps you chose |
| Made | Once a day, by itself | When your timer runs |
| Kept | 7 days | As long as your rule says |
| Get one file back | Restore as new, then copy the file out | yes, one path or one folder |
| Computer deleted | Its daily copies can no longer be restored | Snapshots stay in your S3 bucket |
- The daily copy is a copy of the disk, not a dump of one database: it was made while the computer ran. Keep the database dump from step 1 as well.
- There are no hourly copies, so you can lose up to a day of changes. Run your own timer more often if that is too much.
Common problems
restic cannot connect to the bucket
s3:https://host/bucket with the bucket after the host, and the keys must belong to that bucket."wrong password or no key found"
/etc/restic/password is not the one the repository was created with. There is no way around it: restic encrypts everything. Keep a second copy of the password outside the server.The timer did not run
systemctl list-timers backup.timer shows nothing, run sudo systemctl enable --now backup.timer. If it ran and failed, journalctl -u backup.service shows why.Backups take a long time or lock each other
restic check: it alerts you if the repository's internal data is damaged.Can I use BorgBackup or rsync?
borg init --encryption=repokey /path/to/repo, then borg create --stats /path/to/repo::Tuesday ~/src ~/Documents. rsync: rsync -a --delete /srv/app/ backup@host:/backups/app/; a trailing slash on the source means copy the contents of the directory, and --dry-run first shows what --delete would remove.Daily copies on every paid plan
Pick the size. Each plan copies its computers once a day and keeps the copies off the server.
Small
A Telegram bot, a VPN, a small backend
$5/ month
- 1 vCPU · 1 GB
- 15 GB disk
- 25 GB cloud storage
- 5 boosts a month
Plus
Agents that work with your laptop closed
$20/ month
- 4 vCPU · 4 GB
- 40 GB disk
- 100 GB cloud storage
- Uno Work in the cloud
Pro
A product in production, a second MacBook
$70/ month
- 16 vCPU · 16 GB
- 120 GB disk
- 300 GB cloud storage
- Speed of a MacBook Air M4
Max
A team and many agents at once
$200/ month
- 32 vCPU · 48 GB
- 300 GB disk
- 1,024 GB cloud storage
- 5 public IPv4
Add Uno AI to any plan: 40 h of AI work a month on Small, 100 h on Plus, unlimited on Max. Today: GLM-5.3 Flash and MiMo v2.6 Pro. Or sign in with your own Claude or ChatGPT.