Restic encrypted backup flow from a Linux VPS to remote storage
Linux

Restic Backup Tutorial: Encrypted, Automatic Server Backups

Every server owner eventually has a backup story, and the bad ones all start the same way: “I was sure we had backups.” A disk fails, someone runs rm -rf in the wrong directory, a plugin update corrupts the database, or ransomware encrypts everything it can reach. Whether that turns into a bad afternoon or a lost business depends on two questions: does a recent, intact copy of your data exist somewhere else, and have you ever actually restored from it? In this restic backup tutorial we build exactly that on a fresh Linux VPS. We set up encrypted, deduplicated backups of your files and database dumps, send them to remote storage every night with a systemd timer, trim them with retention rules, and check them with a real restore test.

Restic is a free, open source backup program written in Go, published at restic.net , with its source code at github.com/restic/restic under the BSD 2-Clause license. It ships as a single binary, with no daemon and nothing to install on the storage side. It reads your files, encrypts them on your server, and writes them to almost any storage you point it at. It is simple to run and safe by default, and that is why it has become one of the most popular backup tools for Linux servers.

Why restic is a good fit for a VPS

Before installing anything, let’s agree on what a backup is. A backup is a copy of your data that lives on different hardware, ideally in a different location, and that you can restore even if the original server is completely gone. A RAID array is not a backup, because it mirrors your mistakes instantly. A copy in /root/backup on the same disk is not a backup either. The classic rule is 3-2-1: three copies of your data, on two different types of storage, with one copy off-site. Restic handles the “off-site” part with very little effort.

The five ideas you need to know

  • Repository: the place where restic stores your backups. It can be a folder on another server reachable over SFTP, a bucket on S3-compatible object storage, a restic REST server, and a few more. These storage types are called backends. One repository can hold backups from one server or from several.
  • Snapshot: the result of one backup run. Each time restic runs, it records a snapshot of your files exactly as they were at that moment. You can list snapshots, browse them, and restore any of them.
  • Deduplication: restic cuts files into variable-size chunks, recognizes chunks it has already stored, and uploads only the new ones. If a 2 GB folder changes by 20 MB, the next backup sends roughly 20 MB, and you still get a complete snapshot.
  • Compression: repository format version 2, which current restic versions create by default, compresses chunks with zstd before upload. Configs and SQL dumps shrink a lot.
  • Encryption: data is encrypted on your server, before it leaves, with AES-256, and authenticated with Poly1305. The storage provider only ever sees opaque blobs. The key is protected by a password, and you must never lose it: without the password, nobody can decrypt the backup, including you.
How a restic backup travelsYour VPSFiles and configsDatabase dumps (.sql)systemd timer, nightlyresticchunk + deduplicatecompress (zstd)encrypt (AES-256)upload new data onlyRemote repositoryS3 bucket or SFTPEncrypted blobs onlySnapshots + retentionHTTPS/SSHRestore: download, decrypt and copy back only what you need
Restic does all the work on your VPS: the storage provider only receives encrypted chunks and never sees your password.

How it compares to the usual alternatives

Most people start with one of three approaches. rsync to another machine is fast, but you only get a single mirror. If a file gets corrupted on Monday, Tuesday’s sync faithfully copies the corruption, and nothing is encrypted. A nightly tar.gz archive gives you history, but each night stores a full copy, so you pay for the same data again and again. Hypervisor snapshots from your hosting provider are a great extra layer, but they live on the same infrastructure as the server and should not be your only line of defense. Restic gives you history, encryption and small incremental uploads all at once, and that is why the storage difference is so large:

Storage used after 30 daily backups (approximate)Example server: ~10 GB of data, ~1% of it changing per dayFull copy every daytar.gz every dayrestic (dedup)~300 GB~150 GB~8 GB0100200300Repository size in GB (approximate)Backup methodIllustrative values: real numbers depend on how compressible and how volatile your data is.
Deduplication plus compression keeps a month of daily restic snapshots smaller than a single uncompressed copy of the server.

What you need

  • A VPS running Debian 13 or Ubuntu 24.04, and a user that can run sudo. Everything also works in an LXC container. We point out the one feature that usually does not.
  • Remote storage that is not the same server. This can be an S3-compatible bucket from any object storage provider, or another server you can reach over SSH, such as a second VPS or a storage server. Ideally it sits in a different data center from the server you are protecting.
  • A password manager, or another safe place outside the server, for the repository password.
  • About 30 minutes, plus however long your first upload takes.

Here is the whole procedure at a glance. The numbers match the step headings below.

The 7 steps of this restic backup tutorial1234567InstallresticPreparestorageInitrepositoryFirstbackupRetentionrulessystemdtimerTestrestoreOne-time setupNightly routineVerifySteps 1 to 3 run once, steps 4 to 6 repeat every night, step 7 you repeat on a schedule
Steps 1 to 3 prepare the repository, steps 4 to 6 become the automated nightly job, and step 7 proves the backups can be restored.

Step 1: Install restic

Debian and Ubuntu both ship restic in their official repositories, so installation takes one command:

sudo apt update
sudo apt install -y restic
restic version

The last command prints the installed version. At the time of writing, Debian 13 ships 0.18.x and Ubuntu 24.04 ships 0.16.x. Both support compression, and every command in this tutorial works on both.

If you want the newest release (recent versions restore faster and add a few options), you can install the official binary instead. It goes into /usr/local/bin, which comes before /usr/bin in the PATH, so it takes precedence over the distribution package:

RESTIC_VERSION=0.18.0   # check github.com/restic/restic/releases for the newest
ARCH=$(dpkg --print-architecture)   # amd64 or arm64
sudo apt install -y curl bzip2
cd /tmp
curl -fLO "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_${ARCH}.bz2"
bunzip2 "restic_${RESTIC_VERSION}_linux_${ARCH}.bz2"
sudo install -m 0755 "restic_${RESTIC_VERSION}_linux_${ARCH}" /usr/local/bin/restic
hash -r
restic version

One practical difference: the official binary can update itself later with sudo restic self-update. The Debian and Ubuntu packages have that feature disabled, and you update them with apt like any other package.

Step 2: Prepare remote storage and credentials

Every time it runs, restic needs to know three things: where the repository is, what the password is, and how to log in to the storage. We keep all three in one directory, /etc/restic, readable only by root, so ordinary users on the server cannot see them.

First, create the directory and generate a strong random password:

sudo mkdir -p /etc/restic
sudo chmod 700 /etc/restic
sudo sh -c 'umask 077; head -c 32 /dev/urandom | base64 > /etc/restic/password'
sudo cat /etc/restic/password

The umask 077 part makes sure only root can read the new file. The last command shows the password, a 44-character string. Copy it into your password manager right now. If the server dies, this file dies with it, and without it the backup is useless.

Now choose a storage option.

Option A: an S3-compatible bucket

Create a bucket at your object storage provider, plus an access key that can only reach that bucket. Then write the environment file, replacing the endpoint, bucket name and keys with your own:

sudo tee /etc/restic/restic.env > /dev/null << 'EOF'
RESTIC_REPOSITORY=s3:https://s3.example-provider.com/company-backups/web01
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY
AWS_SECRET_ACCESS_KEY=YOUR_SECRET_KEY
# Only if your provider requires a region:
# AWS_DEFAULT_REGION=eu-central-1
EOF
sudo chmod 600 /etc/restic/restic.env

The repository address has the form s3:https://ENDPOINT/BUCKET/PREFIX. The prefix (web01 here) lets several servers share one bucket, each with its own repository. The variables keep their AWS_ names even if your provider is not Amazon, because that is what S3 client libraries expect. RESTIC_CACHE_DIR puts restic’s local metadata cache in a fixed place, so manual runs and scheduled runs share the same cache.

Option B: an SFTP server you control

If you have a second machine (a storage VPS, a dedicated server, a box at the office), restic can use plain SSH. On that machine, create a user (we call it backup) and a directory it owns, for example /srv/restic. Then, on the server you are backing up, create an SSH key for root, since the backup will run as root:

sudo install -d -m 700 /root/.ssh
sudo ssh-keygen -t ed25519 -f /root/.ssh/restic_backup -N "" -C "restic@$(hostname)"
sudo ssh-copy-id -i /root/.ssh/restic_backup.pub backup@backup.example.com

ssh-copy-id asks you once to confirm the host fingerprint and to type the backup user’s password. Next, give the connection a short name in root’s SSH config:

sudo tee -a /root/.ssh/config > /dev/null << 'EOF'
Host restic-target
    HostName backup.example.com
    User backup
    IdentityFile /root/.ssh/restic_backup
    ServerAliveInterval 60
    ServerAliveCountMax 240
EOF
sudo chmod 600 /root/.ssh/config
sudo ssh restic-target exit && echo "SSH OK"

If you see SSH OK and no password prompt, write the environment file:

sudo tee /etc/restic/restic.env > /dev/null << 'EOF'
RESTIC_REPOSITORY=sftp:restic-target:/srv/restic/web01
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
EOF
sudo chmod 600 /etc/restic/restic.env

A small wrapper so commands stay short

Loading the environment by hand every time gets old fast. We create a tiny helper that reads /etc/restic/restic.env and passes everything else straight to restic:

sudo tee /usr/local/sbin/restic-run > /dev/null << 'EOF'
#!/bin/sh
# Run restic with this server's repository settings loaded
set -a
. /etc/restic/restic.env
set +a
exec restic "$@"
EOF
sudo chmod 700 /usr/local/sbin/restic-run

set -a exports every variable read from the file, so restic can see them. From now on, sudo restic-run snapshots runs restic with all settings in place. On both Debian and Ubuntu, /usr/local/sbin is in sudo’s search path, so the short name works.

Step 3: Initialize the encrypted repository

Initializing creates the repository structure on your storage and writes the master key there, encrypted with your password:

sudo restic-run init

Expected output:

created restic repository 5b7d8e9f01 at s3:https://s3.example-provider.com/company-backups/web01

Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.

Take that warning literally. Next, run sudo restic-run snapshots. It should connect and print an empty list, which proves that the address, credentials and password all work. If you get an error instead, check the troubleshooting section before going further.

Step 4: Run your first backup of files and database dumps

Decide what to back up

You don’t need to back up the whole disk. You can reinstall the operating system in minutes. Your data and configuration are what you cannot replace. On a typical VPS, that means:

  • /etc: system and service configuration (web server, firewall, cron, SSH).
  • /home and /root: user files, scripts and SSH keys.
  • /var/www, /srv, /opt: websites and applications, depending on where you installed them.
  • Database dumps, which we create in a moment.

If you run Docker, add the folders that hold your compose files and bind-mounted data, for example /opt/stacks (already covered by /opt).

Skip caches, node_modules folders and temporary files by listing them in an exclude file:

sudo tee /etc/restic/excludes > /dev/null << 'EOF'
/root/.cache
/home/*/.cache
**/node_modules
**/*.tmp
EOF

Why database dumps instead of raw database files

A database server keeps data in memory and writes it to disk in pieces. If you copy the files under /var/lib/mysql or /var/lib/postgresql while the server is running, you can capture a half-written state that will not start after a restore. The safe approach is to ask the database for a consistent export, called a dump, and back up that file. mariadb-dump and mysqldump with --single-transaction produce a consistent copy of InnoDB tables without locking your site, and pg_dumpall does the same for PostgreSQL.

One detail matters for restic: do not gzip the dumps. Compressing a file changes all of its bytes even when a single row changed, and that defeats deduplication. A plain .sql file deduplicates well from one night to the next, and restic compresses it anyway.

Debian and Ubuntu differ here. On Debian, the default MySQL-compatible server is MariaDB (service mariadb, dump tool mariadb-dump). On Ubuntu you may have MariaDB or Oracle MySQL 8 (service mysql, dump tool mysqldump). The script below handles both. In the default setup on both systems, the database root account logs in through the Unix socket, so a script running as root needs no database password.

The backup script

This script creates the dumps first, then backs up the folders from our list that exist on the server:

sudo tee /usr/local/sbin/restic-backup.sh > /dev/null << 'EOF'
#!/bin/bash
# Nightly restic backup: database dumps first, then files
set -euo pipefail
cd /

# Load repository location, password file and storage credentials
set -a
. /etc/restic/restic.env
set +a

DUMP_DIR=/var/backups/db
mkdir -p "$DUMP_DIR"
chmod 700 "$DUMP_DIR"

# MariaDB or MySQL, only if a server is running
if systemctl is-active --quiet mariadb || systemctl is-active --quiet mysql; then
    if command -v mariadb-dump > /dev/null 2>&1; then
        DUMP_BIN=mariadb-dump
    else
        DUMP_BIN=mysqldump
    fi
    "$DUMP_BIN" --all-databases --single-transaction --quick \
        --routines --events > "$DUMP_DIR/mysql-all.sql.tmp"
    mv "$DUMP_DIR/mysql-all.sql.tmp" "$DUMP_DIR/mysql-all.sql"
fi

# PostgreSQL, only if it is running
if systemctl is-active --quiet postgresql; then
    runuser -u postgres -- pg_dumpall > "$DUMP_DIR/postgres-all.sql.tmp"
    mv "$DUMP_DIR/postgres-all.sql.tmp" "$DUMP_DIR/postgres-all.sql"
fi

# Back up only the folders that exist on this server
SOURCES=(/etc /home /root /var/www /srv /opt "$DUMP_DIR")
EXISTING=()
for p in "${SOURCES[@]}"; do
    if [ -e "$p" ]; then EXISTING+=("$p"); fi
done

restic backup \
    --exclude-file /etc/restic/excludes \
    --exclude-caches \
    --tag scheduled \
    "${EXISTING[@]}"
EOF
sudo chmod 700 /usr/local/sbin/restic-backup.sh

Here is what the important parts do:

  • set -euo pipefail stops the script at the first error. If a dump fails, the whole run fails visibly, and you never silently back up last week’s dump.
  • Each dump is written to a .tmp file and renamed only when it is complete, so a crash never leaves a half-written file under the real name.
  • --tag scheduled labels automatic snapshots. It matters for the retention rules in Step 5.
  • --exclude-caches skips any folder that contains a standard CACHEDIR.TAG file, which many tools create for their caches.

If your database runs in a Docker container, replace the dump section with a command like docker exec my-postgres pg_dumpall -U postgres > "$DUMP_DIR/postgres-all.sql.tmp". With restic 0.17 or newer (the Debian 13 package or the official binary), you can also stream a dump straight into the repository with restic backup --stdin-from-command. The Ubuntu 24.04 package is too old for that option, which is why we use dump files here: they work everywhere.

Run it by hand

sudo /usr/local/sbin/restic-backup.sh

The first run uploads everything, so it takes the longest. The end of the output looks like this (your numbers will differ):

repository 5b7d8e9f opened (version 2, compression level auto)
created new cache in /var/cache/restic
no parent snapshot found, will read all files

Files:       18734 new,     0 changed,     0 unmodified
Dirs:         2311 new,     0 changed,     0 unmodified
Added to the repository: 1.912 GiB (812.304 MiB stored)

processed 18734 files, 2.104 GiB in 1:47
snapshot 7c1e4d2a saved

Look at the “Added to the repository” line. The first number is the amount of new data, and the number in parentheses is what was actually stored after compression. Run the script a second time and check the same line again. You should see a few megabytes at most, because only changed chunks get uploaded. That’s deduplication at work.

Step 5: Set retention rules with forget and prune

Without cleanup, a repository grows forever. Restic splits cleanup into two actions:

  • forget removes snapshots from the list, following rules you define. The data they used is still in the repository.
  • prune deletes the data that no remaining snapshot needs, and that is what actually frees space.

A sensible policy for a small server: one snapshot per day for the last 7 days, one per week for 4 weeks, and one per month for 6 months. That gives you at most 17 restore points, going back about half a year. Always preview with --dry-run first:

sudo restic-run forget --tag scheduled --group-by host,tags \
    --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --dry-run

The output lists the snapshots that would be kept and those that would be removed, with a reason for each one kept. Two flags need explaining:

  • --tag scheduled applies the rules only to automatic snapshots. A manual snapshot you take before a risky upgrade will never be deleted by the nightly job.
  • --group-by host,tags applies the policy per server and per tag. By default, restic also groups by the list of backed-up paths. With that default, adding a new folder like /srv later would start a separate group, and the old group would stop being cleaned up properly.

Once you are happy with the preview, append retention and a weekly integrity check to the backup script:

sudo tee -a /usr/local/sbin/restic-backup.sh > /dev/null << 'EOF'

# Retention: 7 daily, 4 weekly, 6 monthly snapshots
restic forget --tag scheduled --group-by host,tags \
    --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

# On Sundays, verify the structure and re-read 5% of the stored data
if [ "$(date +%u)" = "7" ]; then
    restic check --read-data-subset=5%
fi
EOF

--prune runs the prune right after forget. Prune only rewrites storage when it can recover enough space (by default it tolerates up to 5% unused data), so running it every night on a small server is fine. restic check --read-data-subset=5% downloads and verifies a random 5% of the data each week, which catches silent corruption on the storage side without downloading the whole repository.

Step 6: Automate backups with a systemd timer

A systemd timer is the modern replacement for a cron entry. It starts a service on a schedule, logs everything to the system journal, catches up on runs missed while the server was off, and makes failures easy to spot with systemctl. We need two files: a service that says what to run, and a timer that says when.

sudo tee /etc/systemd/system/restic-backup.service > /dev/null << 'EOF'
[Unit]
Description=Restic backup to remote repository
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
Environment=HOME=/root
ExecStart=/usr/local/sbin/restic-backup.sh
Nice=10
IOSchedulingClass=idle
TimeoutStartSec=6h
# Optional: ping a push monitor only when the backup succeeded
# ExecStartPost=-/usr/bin/curl -fsS -m 10 --retry 3 https://status.example.com/api/push/YOUR_TOKEN
EOF

sudo tee /etc/systemd/system/restic-backup.timer > /dev/null << 'EOF'
[Unit]
Description=Nightly restic backup

[Timer]
OnCalendar=*-*-* 03:15:00
RandomizedDelaySec=20min
Persistent=true

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer

What each setting does:

  • Type=oneshot tells systemd the job runs to completion and then exits, unlike a long-running daemon.
  • Environment=HOME=/root helps tools that look for config files in the home folder, because systemd does not set HOME for system services by default.
  • Nice=10 and IOSchedulingClass=idle lower the backup’s CPU and disk priority, so your website stays responsive while it runs.
  • TimeoutStartSec=6h kills a run that hangs, for example on a dead network connection, so it doesn’t block the next one.
  • OnCalendar sets the time, RandomizedDelaySec adds up to 20 random minutes (useful when many servers back up to the same storage), and Persistent=true runs a missed backup right after boot if the server was off at 03:15.
  • The commented ExecStartPost line is a “dead man’s switch”: it pings a push-type monitor (for example in Uptime Kuma) only when the backup succeeded. If the ping stops arriving, the monitor alerts you. The leading - means a failed ping does not mark the backup as failed. On Debian, install curl first with sudo apt install -y curl if it is missing.

OnCalendar uses the server’s local time zone, and many VPS images default to UTC. Check with timedatectl and change it if you like, for example with sudo timedatectl set-timezone Europe/Bucharest.

Confirm the timer is scheduled:

systemctl list-timers restic-backup.timer
NEXT                         LEFT      LAST PASSED UNIT                ACTIVATES
Wed 2026-11-11 03:24:51 EET  15h left  -    -      restic-backup.timer restic-backup.service

Then run the service once now, so you know the scheduled version works exactly like the manual one:

sudo systemctl start restic-backup.service
systemctl status restic-backup.service --no-pager
sudo journalctl -u restic-backup.service -n 30 --no-pager

For a oneshot service, systemctl start waits until the backup finishes. The status should then show inactive (dead) with status=0/SUCCESS, and the journal shows the full restic output. We use sudo for journalctl because the first user on Ubuntu is in the adm group and can read system logs, but on Debian it usually is not. Timers work the same in KVM and LXC VPS instances.

Step 7: Test a restore

A backup you have never restored is a hope, not a backup. Run this test now, and then again every month or two. Put a reminder in your calendar.

Restore a single file

List the snapshots, look inside the latest one, and restore one file to a temporary folder:

sudo restic-run snapshots
sudo restic-run ls latest /etc/ssh
sudo restic-run restore latest --target /tmp/restore-test --include /etc/ssh/sshd_config
sudo diff /etc/ssh/sshd_config /tmp/restore-test/etc/ssh/sshd_config && echo "identical"

Restic recreates the full path under the target folder, so the file lands at /tmp/restore-test/etc/ssh/sshd_config. If diff prints nothing and you see identical, the restore works end to end: download, decryption and reassembly. Clean up with sudo rm -rf /tmp/restore-test.

Check the database dumps

restic dump prints a file from a snapshot straight to the terminal, so you can check a dump without restoring it to disk:

sudo restic-run dump latest /var/backups/db/mysql-all.sql | tail -n 1
sudo restic-run dump latest /var/backups/db/postgres-all.sql | tail -n 3

A complete MariaDB or MySQL dump ends with a line like -- Dump completed on 2026-11-10 3:15:42, and a complete PostgreSQL dump ends with -- PostgreSQL database cluster dump complete. If that last line is missing, the dump was cut short. The best test goes one step further: load the dump on a scratch database server (a small second VPS works well) and open your application against it.

Browse snapshots like a folder

To look around comfortably, you can mount the repository as a read-only folder:

sudo apt install -y fuse3
sudo mkdir -p /mnt/restic
sudo restic-run mount /mnt/restic

The command keeps running in the foreground. In a second SSH session, browse with sudo ls /mnt/restic/snapshots/latest/etc, then press Ctrl+C in the first session to unmount. This relies on FUSE and usually does not work in an unprivileged LXC container, because /dev/fuse is not available there unless the host enables it. Everything else in this tutorial works fine in LXC, and restore and dump cover the same needs.

Full disaster recovery on a new server

If the server is gone, here is the recovery path:

  1. Install a fresh VPS with the same distribution and install restic.
  2. Recreate /etc/restic/password (from your password manager), /etc/restic/restic.env and the restic-run wrapper, exactly as in Step 2.
  3. Restore the latest snapshot to a staging folder: sudo restic-run restore latest --target /srv/restore. If several servers share the repository, add --host web01.
  4. Reinstall your packages, then copy configs and site files from /srv/restore into place.
  5. Import the databases: sudo cat /srv/restore/var/backups/db/mysql-all.sql | sudo mariadb (or sudo mysql on MySQL), and sudo cat /srv/restore/var/backups/db/postgres-all.sql | sudo -u postgres psql.

We restore to a staging folder rather than straight over / on purpose. Restoring onto a new system’s /etc would overwrite files like network configuration that belong to the new machine.

Verify the repository

Finally, check the repository structure at any time:

sudo restic-run check

It should end with no errors were found. The weekly partial data check from Step 5 adds a deeper test on top of that.

Troubleshooting and common mistakes

  • Access Denied or bucket does not exist on init: the keys, bucket name, endpoint URL or region are wrong. Double-check /etc/restic/restic.env, and set AWS_DEFAULT_REGION if your provider requires it.
  • wrong password or no key found: the password file does not match the repository. Did you regenerate it after init? Restore the original from your password manager.
  • repository is already locked: another restic process is running, or a run was killed and left a stale lock. Check systemctl status restic-backup.service. If nothing is running, remove the lock with sudo restic-run unlock.
  • Backup ends with exit code 3: the snapshot was saved, but some files could not be read (usually permissions or files deleted mid-run). The journal lists them. Because of set -e, the script stops there and the run shows as failed, which is exactly what you want to notice.
  • Access denied for user 'root'@'localhost' from the dump: your database root has a password instead of socket login. Create /etc/restic/my.cnf with a [client] section containing user and password, run chmod 600 on it, and add --defaults-extra-file=/etc/restic/my.cnf as the first option of the dump command.
  • The timer never runs: you probably enabled the .service instead of the .timer. systemctl list-timers must list restic-backup.timer.
  • Out of memory on a small VPS: restic’s RAM use grows with the number of files. Add GOGC=20 to restic.env to trade a little CPU for less memory.
  • Local disk filling up: the cache in /var/cache/restic and the dumps in /var/backups/db take space on the server. sudo restic-run cache --cleanup removes old caches, and the dumps take roughly as much space as your databases.

Security notes

  • The password is the backup. Keep it in a password manager, outside the server. With sudo restic-run key add you can add a second password, for example one held by a colleague, and key list shows all of them.
  • Lock down the files. /etc/restic must be 700 and the files in it 600, owned by root. The dumps in /var/backups/db contain your entire database, so that folder stays 700 too.
  • Assume the server can be compromised. The credentials on the server can also delete backups, so an attacker with root could wipe your repository. Protect at least one copy against that: turn on object lock or versioning on the bucket if your provider supports it, use a restic REST server started with --append-only, or run prune from a separate trusted machine and give the server write-only access.
  • Know what is encrypted. File contents, names and folder structure are encrypted. The storage provider can still see how much data you store and when you upload.
  • Keep backups somewhere else. A repository in the same data center as the server protects against mistakes, but not against losing the whole site. If your VPS runs with us in Romania, keep the repository with another provider, or at least on separate hardware.
  • Keep restic updated with apt or restic self-update, like any other software that handles your data.

So, is your server actually backed up?

If you followed all seven steps, yes. Every night your server dumps its databases, sends only the changed chunks to remote storage, encrypted with a key that only you hold, and tidies up old snapshots on its own. More importantly, you have already proved you can get your data back. What’s left is discipline: check that the timer keeps succeeding (a push monitor makes that automatic), and repeat the restore test from time to time.

If you need a place to run this, a VPS from our own data center is ready in a few minutes, and restic runs comfortably even on the smallest plans. If you would rather have someone design the backup policy, set up off-site storage and run the restore tests for you, our server administration team does exactly that.

FAQ

How much storage will restic need?

Plan for roughly your data size after compression, plus the daily changes kept by your retention policy. For a typical web server with 10 GB of data and modest daily changes, a month of daily snapshots often fits in 8 to 15 GB. Run sudo restic-run stats --mode raw-data to see the real number for your repository.

Is restic better than rsync or BorgBackup?

Restic beats rsync for backups because it keeps history and encrypts your data. BorgBackup is an excellent alternative with similar deduplication. Restic’s advantage is native support for S3-compatible storage and many other backends without anything installed on the remote side.

What happens if a backup is interrupted?

Nothing breaks. Restic only records a snapshot after all its data has been uploaded, so an interrupted run leaves some unused chunks, and a later prune cleans them up. If a stale lock remains, restic unlock removes it, and the next run continues normally.

Can I back up Docker volumes with restic?

Yes. Back up bind-mounted folders like any other directory, and use database dumps (through docker exec) for database containers rather than copying their live data files. Named volumes live under /var/lib/docker/volumes, and you can add that path to the list, ideally for volumes that do not hold running databases.

Trusted By & Member Of

We are proud members of leading internet infrastructure organizations.

RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX