👍 What we like
- ✓Client-side encryption ensures provider cannot read data
- ✓Low cost object storage with free tier for testing
- ✓Automatic deduplication saves storage space
- ✓Supports ransomware protection via versioning
- ✓Integrity verification ensures backup reliability
👎 What to watch
- ✕Passphrase loss makes data unrecoverable
- ✕Requires manual setup of environment variables
- ✕Bucket name must match region endpoint exactly
- ✕No built-in GUI for management
📑 Contents ▾
- 01 Prerequisites
- 02 Step 1: Create a bucket and application key on Backblaze B2
- 03 Step 2: Install restic
- 04 Step 3: Configure environment variables
- 05 Step 4: Initialize the restic repository
- 06 Step 5: First manual backup
- · Backing up a database properly
- 08 Step 6: Automate with a script and systemd
- · Create the systemd service and timer
- 10 Step 7: Verify backup integrity
- 11 Step 8: Test restoration (the step always skipped)
- 12 Best practices and security
- 13 FAQ
- · Why restic instead of rsync or simple tar?
- · How much does it really cost on Backblaze B2?
- · What if I forget the repository passphrase?
- · Does the backup slow down my server?
- · Can I back up multiple servers to the same repository?
- 19 Related topics
There are two types of server administrators: those who back up, and those who will. A VPS hosting your Nextcloud, databases, or website is never immune to a failing disk, a rogue rm -rf command, ransomware, or a provider shutting down. The 3-2-1 rule (three copies, two media types, one off-site) remains the gold standard in 2026, and off-site copying is the one aspect most often neglected.
In this guide, we set up a serious, automatic, and encrypted backup strategy using restic, the Swiss Army knife of backups, to Backblaze B2, one of the cheapest object storage options on the market (approximately $6/TB/month). restic encrypts everything client-side before sending: even Backblaze cannot read your data. We cover repository initialization, deduplication, scheduling via systemd, automatic retention, integrity verification, and most importantly, restoration, because a backup that has never been restored is merely a hypothesis.
Prerequisites
-
A Linux server (Ubuntu 24.04 / Debian 12) to back up. Ideally already hardened; see install and secure an Ubuntu VPS.
-
A Backblaze account: create one at backblaze.com. The first 10 GB of B2 are free, which is more than enough to test.
-
sudoaccess on the server. -
A password manager to securely store the repository passphrase and B2 keys. If you lose the passphrase, your backups are unrecoverable by design.
If you are unsure about choosing the tool or object storage, our comparisons restic vs Borg vs Kopia and Backblaze B2 vs Wasabi vs Storj will help you decide.
Step 1: Create a bucket and application key on Backblaze B2
Log in to your Backblaze account, then:
-
Go to B2 Cloud Storage > Buckets > Create a Bucket.
-
Give it a globally unique name (e.g.,
backup-srv-selfhostr-2026), set access to Private, and leave encryption as default. -
Note the bucket Endpoint displayed in its details (e.g.,
s3.eu-central-003.backblazeb2.com). -
Go to App Keys > Add a New Application Key.
-
Restrict the key to your bucket only (principle of least privilege). Check both read and write permissions.
-
Note the displayed
keyIDandapplicationKey. The applicationKey is shown only once, so copy it immediately into your password manager.
Best practice: Enable versioning on the bucket (or object retention) on the Backblaze side. This provides additional protection against ransomware that encrypts/deletes your backups: even if the attacker accesses the key, previous versions remain recoverable.
Step 2: Install restic
restic is available in the repositories, but a recent version is better for the latest optimizations. On Ubuntu/Debian:
sudo apt update && sudo apt install -y restic
Check the version (aim for 0.16 or higher):
restic version
If the repository version is too old, update restic to the latest official release:
sudo restic self-update
Step 3: Configure environment variables
restic is controlled via environment variables for credentials and the passphrase. We store them in a protected file, readable only by root. Create the directory and file:
sudo mkdir -p /etc/restic
sudo nano /etc/restic/b2.env
restic communicates with Backblaze via the S3-compatible API. Fill in:
# S3 Endpoint for your bucket (from Step 1)
export RESTIC_REPOSITORY="s3:https://s3.eu-central-003.backblazeb2.com/backup-srv-selfhostr-2026"
# B2 Application Key credentials
export AWS_ACCESS_KEY_ID="YOUR_keyID"
export AWS_SECRET_ACCESS_KEY="YOUR_applicationKey"
# Repository encryption passphrase (long and unique!)
export RESTIC_PASSWORD="a-very-long-and-random-passphrase"
Lock down permissions; this file contains critical secrets:
sudo chmod 600 /etc/restic/b2.env
sudo chown root:root /etc/restic/b2.env
Common pitfall: The bucket name in
RESTIC_REPOSITORYmust match exactly, and the endpoint must be that of your region (the number aftereu-central-varies). An error here results in an unhelpfulAccess Denied.
Step 4: Initialize the restic repository
Load the variables and initialize the encrypted repository (do this only once):
sudo bash -c 'source /etc/restic/b2.env && restic init'
You will receive a confirmation message with the repository ID. This is when the encrypted structure is created on Backblaze. From now on, all data sent is deduplicated and encrypted client-side with AES-256 before transfer.
Verify that the repository responds correctly:
sudo bash -c 'source /etc/restic/b2.env && restic snapshots'
The list is empty for now, but the absence of errors confirms that the credentials and passphrase are correct.
Step 5: First manual backup
Let’s back up typical server directories: application data, configuration, and Docker volumes. Adjust paths to your case:
sudo bash -c 'source /etc/restic/b2.env && restic backup \
/etc \
/home \
/var/lib/docker/volumes \
--exclude="/var/lib/docker/volumes/*/_data/cache" \
--tag manual'
restic displays progress, the number of files, and the amount of data actually transferred (thanks to deduplication, only new or modified blocks go over the network). At the end, a new snapshot appears:
sudo bash -c 'source /etc/restic/b2.env && restic snapshots'
Backing up a database properly
Never copy database files while they are running: you risk a corrupted backup. Perform a dump and then back up the stream. Example PostgreSQL in a Docker container, piping directly to restic via stdin:
sudo bash -c 'source /etc/restic/b2.env && \
docker exec -t mon-postgres pg_dumpall -U postgres | \
restic backup --stdin --stdin-filename postgres-dump.sql --tag db'
For MySQL/MariaDB, replace with mysqldump --all-databases. This method guarantees a consistent, point-in-time backup.
Step 6: Automate with a script and systemd
We create a backup script that also includes retention cleanup. Create the file:
sudo nano /etc/restic/backup.sh
#!/usr/bin/env bash
set -euo pipefail
source /etc/restic/b2.env
# 1. Database dump
docker exec -t mon-postgres pg_dumpall -U postgres \
| restic backup --stdin --stdin-filename postgres-dump.sql --tag db
# 2. File backup
restic backup /etc /home /var/lib/docker/volumes \
--exclude-caches \
--tag auto
# 3. Apply retention policy
restic forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--prune
Make it executable:
sudo chmod 700 /etc/restic/backup.sh
The retention policy above keeps the last 7 daily backups, 4 weekly, and 6 monthly, then --prune truly frees up space for data no longer referenced. A good balance between cost and history.
Create the systemd service and timer
systemd is more robust than a standard cron job (log management, dependencies, failure handling). Create the service unit:
sudo nano /etc/systemd/system/restic-backup.service
[Unit]
Description=Restic backup to Backblaze B2
After=network-online.target docker.service
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/etc/restic/backup.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
Then the timer that triggers it every night:
sudo nano /etc/systemd/system/restic-backup.timer
[Unit]
Description=Launches daily restic backup
[Timer]
OnCalendar=*-*-* 03:30:00
RandomizedDelaySec=900
Persistent=true
[Install]
WantedBy=timers.target
Persistent=true catches up on a missed backup if the server was off at the scheduled time. Enable the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
Check the schedule:
systemctl list-timers restic-backup.timer
Run an immediate execution to test the full script end-to-end:
sudo systemctl start restic-backup.service
journalctl -u restic-backup.service -n 50 --no-pager
Step 7: Verify backup integrity
A silently corrupted backup is worse than no backup. restic can check the repository integrity. A quick metadata check:
sudo bash -c 'source /etc/restic/b2.env && restic check'
For a thorough check that re-downloads and verifies a sample of actual data (recommended once a month):
sudo bash -c 'source /etc/restic/b2.env && restic check --read-data-subset=10%'
Step 8: Test restoration (the step always skipped)
This is the most important and most neglected step. Restore to a temporary location to validate that your data is recoverable:
# List snapshots and identify the desired ID
sudo bash -c 'source /etc/restic/b2.env && restic snapshots'
# Restore the latest snapshot to /tmp/restore-test
sudo bash -c 'source /etc/restic/b2.env && \
restic restore latest --target /tmp/restore-test'
Inspect /tmp/restore-test and verify that your files are intact. To restore a single file or folder, use --include:
sudo bash -c 'source /etc/restic/b2.env && \
restic restore latest --target /tmp/restore-test --include /etc/caddy/Caddyfile'
To restore a database dump, either mount the repository read-only or use restic dump:
sudo bash -c 'source /etc/restic/b2.env && \
restic dump latest postgres-dump.sql | docker exec -i mon-postgres psql -U postgres'
Schedule a real restoration test quarterly. This is the only way to be certain your strategy holds up.
Best practices and security
-
Keep the passphrase off the server. If the server is compromised, the attacker should not be able to read or destroy your backups. Ideally, the B2 key used by the server has only write (append-only) permission, not delete.
-
Enable object retention on Backblaze to make your backups immutable for a given window: ultimate ransomware protection.
-
Monitor failures. Couple the timer with an alert (email, webhook, or a Uptime Kuma “push” probe) to be notified if a backup fails. See our guide monitor your homelab with Uptime Kuma and Grafana.
-
Document the restoration procedure in a place accessible even if the server is dead (password manager, personal wiki).
-
Never put secrets in the script: the
.envfile with 600/root permissions is the correct place.
FAQ
Why restic instead of rsync or simple tar?
restic provides three things that tar or rsync do not offer natively: deduplication (identical blocks are stored only once, saving massive amounts of space), end-to-end encryption (your data is unreadable on the provider side), and versioning via snapshots with automatic retention. To compare with Borg and Kopia, read restic vs Borg vs Kopia in 2026.
How much does it really cost on Backblaze B2?
B2 charges approximately $6 per TB per month for storage, and outbound traffic (restoration) is free up to 3x the stored size each month. For a server whose deduplicated data fits in 50 GB, you pay a few cents per month. The first 10 GB are free. It is one of the cheapest options, as shown in our B2 vs Wasabi vs Storj comparison.
What if I forget the repository passphrase?
Nothing. This is intentional: restic encrypts everything client-side; without the passphrase, there is no backdoor to decrypt it. Your backups become permanently unusable. This is why this passphrase must be stored in a reliable password manager, separate from the server, right from initialization.
Does the backup slow down my server?
Very little if you follow this guide. The Nice=10 and IOSchedulingClass=best-effort directives in the systemd service give restic low priority, yielding to application processes. By scheduling it at 3:30 AM, the impact is negligible for almost all use cases.
Can I back up multiple servers to the same repository?
Yes, and it is even an advantage: deduplication works across servers, so common files (system binaries, Docker images) are stored only once. Use distinct --tags per machine to keep things organized. However, be aware of concurrency: restic locks the repository during prune, so stagger the schedules.
Related topics
Your data is now protected by an off-site, encrypted, deduplicated, and verifiable copy. All that remains is to test a restoration occasionally and sleep soundly. To follow new self-hosted tools and security best practices, join our Telegram watch bot.