Vucense

Restic Backup on Ubuntu 24.04: Encrypted, Incremental Backups (2026)

🟢Beginner

Set up Restic for encrypted incremental backups on Ubuntu 24.04 LTS in 2026. Covers local and remote repositories, scheduled backups with systemd, backup verification, and restoring files.

Restic Backup on Ubuntu 24.04: Encrypted, Incremental Backups (2026)
Article Roadmap

Key Takeaways

  • Encrypted by default: Every restic repository is encrypted with AES-256-CTR + Poly1305-AES. There is no unencrypted restic repository — encryption cannot be disabled.
  • Deduplication saves space: Restic chunks files using content-defined chunking (CDC) — identical content is stored once regardless of filename or path. Incremental backups of databases typically add 5–15% of the original size per backup.
  • restic check is mandatory: A backup you haven’t verified is not a backup. Run restic check weekly and restic check --read-data monthly to verify data integrity.
  • SovereignScore 99/100: Restic is open-source (BSD 2-clause), encryption keys stay local, and all 14 backends support air-gapped operation after initial setup.

Introduction

Direct Answer: How do I set up Restic for encrypted backups on Ubuntu 24.04 in 2026?

Install Restic with sudo apt-get install -y restic. Initialise a repository with restic init --repo /mnt/backup/myserver — you’ll be prompted to set a password that encrypts all backup data. Create the first backup with restic backup --repo /mnt/backup/myserver /etc /home /opt --tag server-backup. Check what was backed up with restic snapshots --repo /mnt/backup/myserver. Restore a specific file with restic restore latest --repo /mnt/backup/myserver --target /tmp/restore --include "/etc/nginx". To automate, create a systemd timer that runs restic backup daily and a weekly restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 to prune old snapshots. The repository password must be stored securely — store it in /etc/restic-password with chmod 600, referenced via --password-file in all restic commands.


Part 1: Installation

sudo apt-get install -y restic
restic version

Expected output:

restic 0.17.1 compiled with go1.22.2 on linux/amd64

Part 2: Create a Repository and First Backup

# ── LOCAL REPOSITORY (on the same server, for quick restores) ─────────────
# Create the repository directory
sudo mkdir -p /mnt/backup/myserver
sudo chown $USER:$USER /mnt/backup/myserver

# Store the repository password securely (do NOT use a weak password)
# Generate a strong password:
openssl rand -base64 32 | sudo tee /etc/restic-password
sudo chmod 600 /etc/restic-password
sudo chown root:root /etc/restic-password
echo "Password file created"

# Initialise the repository
restic init --repo /mnt/backup/myserver --password-file /etc/restic-password

Expected output:

created restic repository abc1234567 at /mnt/backup/myserver

Please note that knowledge of your password is required to access
the repository. Losing your password means that your backed up
data cannot be recovered.
# Create the first backup
restic backup \
  --repo /mnt/backup/myserver \
  --password-file /etc/restic-password \
  --tag server \
  --tag initial \
  /etc \
  /home \
  /var/log \
  /opt \
  --exclude "/home/*/.cache" \
  --exclude "/home/*/.local/share/Trash" \
  --exclude "*.tmp"

Expected output:

repository abc1234567 opened (version 2, compression level auto)
created new cache in /root/.cache/restic

Files:        8,432 new,     0 changed,     0 unmodified
Dirs:         1,247 new,     0 changed,     0 unmodified
Added to the repository: 2.341 GiB (1.123 GiB stored)   ← Deduplication

snapshot 7f3a2b1c saved

“2.341 GiB stored as 1.123 GiB” — 52% space savings from deduplication and compression on first backup.

# List snapshots
restic snapshots --repo /mnt/backup/myserver --password-file /etc/restic-password

Expected output:

repository abc1234567 opened (version 2)
ID        Time                 Host           Tags           Paths
──────────────────────────────────────────────────────────────────
7f3a2b1c  2026-04-28 13:00:00  hetzner-cx22   server,initial /etc, /home, /var/log, /opt

1 snapshots

Part 3: Remote Repository via SFTP

For the offsite backup copy (3-2-1 rule):

# Initialise repository on a remote server via SFTP
# Requires SSH key authentication to the remote server
restic init \
  --repo "sftp:[email protected]:/backups/myserver" \
  --password-file /etc/restic-password

Expected output:

created restic repository def567890a at sftp:[email protected]:/backups/myserver
# Backup to the remote repository (same command, different --repo)
restic backup \
  --repo "sftp:[email protected]:/backups/myserver" \
  --password-file /etc/restic-password \
  --tag server \
  /etc /home /opt

# Verify the remote backup is intact
restic check \
  --repo "sftp:[email protected]:/backups/myserver" \
  --password-file /etc/restic-password

Expected output:

using temporary cache in /tmp/restic-check-cache-123
load indexes
check all packs
check snapshots, trees and blobs
no errors were found

Part 4: Automated Backups with systemd

# Create backup script
sudo tee /usr/local/bin/restic-backup.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail

REPO_LOCAL="/mnt/backup/myserver"
REPO_REMOTE="sftp:[email protected]:/backups/myserver"
PASSWORD_FILE="/etc/restic-password"
LOG="/var/log/restic-backup.log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }

log "=== Backup started ==="

# Local backup
log "Backing up to local repository..."
restic backup \
  --repo "$REPO_LOCAL" \
  --password-file "$PASSWORD_FILE" \
  --tag daily \
  --exclude "/home/*/.cache" \
  --exclude "*.tmp" \
  /etc /home /opt /var/log \
  2>&1 | tee -a "$LOG"

# Prune old snapshots (keep 7 daily, 4 weekly, 12 monthly)
log "Pruning old snapshots..."
restic forget \
  --repo "$REPO_LOCAL" \
  --password-file "$PASSWORD_FILE" \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 12 \
  --prune \
  2>&1 | tee -a "$LOG"

# Remote backup
log "Syncing to remote repository..."
restic backup \
  --repo "$REPO_REMOTE" \
  --password-file "$PASSWORD_FILE" \
  --tag daily \
  --exclude "/home/*/.cache" \
  /etc /home /opt \
  2>&1 | tee -a "$LOG"

log "=== Backup complete ==="
SCRIPT

sudo chmod +x /usr/local/bin/restic-backup.sh

# Create systemd service
sudo tee /etc/systemd/system/restic-backup.service << 'EOF'
[Unit]
Description=Restic Backup
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/restic-backup.sh
StandardOutput=journal
StandardError=journal
EOF

# Create systemd timer (runs at 2:30 AM daily)
sudo tee /etc/systemd/system/restic-backup.timer << 'EOF'
[Unit]
Description=Run Restic backup daily at 2:30 AM

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true          # Run immediately if missed (e.g., server was off)
RandomizedDelaySec=600   # Randomise within 10 minutes to avoid thundering herd

[Install]
WantedBy=timers.target
EOF

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

# Verify timer is scheduled
systemctl list-timers restic-backup.timer

Expected output:

NEXT                         LEFT       LAST  PASSED  UNIT
Tue 2026-04-29 02:30:00 UTC  13h left   n/a   n/a     restic-backup.timer
# Test the backup runs successfully
sudo systemctl start restic-backup.service
sudo journalctl -u restic-backup.service -n 20

Expected output:

Apr 28 13:30:00 server restic-backup.sh[12345]: [2026-04-28 13:30:00] === Backup started ===
Apr 28 13:30:00 server restic-backup.sh[12345]: [2026-04-28 13:30:00] Backing up to local repository...
Apr 28 13:30:02 server restic-backup.sh[12345]: snapshot 8a4b3c2d saved
Apr 28 13:30:02 server restic-backup.sh[12345]: [2026-04-28 13:30:02] === Backup complete ===

Part 5: Restore Files

# List available snapshots
restic snapshots --repo /mnt/backup/myserver --password-file /etc/restic-password

# Browse snapshot contents (without restoring)
restic ls --repo /mnt/backup/myserver --password-file /etc/restic-password latest /etc/nginx

# Restore a single file
restic restore latest \
  --repo /mnt/backup/myserver \
  --password-file /etc/restic-password \
  --target /tmp/restore \
  --include "/etc/nginx/sites-available/mysite.conf"

ls -la /tmp/restore/etc/nginx/sites-available/

# Restore an entire directory
restic restore latest \
  --repo /mnt/backup/myserver \
  --password-file /etc/restic-password \
  --target /tmp/restore-full \
  --include "/home/ubuntu"

# Restore from a specific snapshot (not latest)
SNAPSHOT_ID="7f3a2b1c"
restic restore $SNAPSHOT_ID \
  --repo /mnt/backup/myserver \
  --password-file /etc/restic-password \
  --target /tmp/restore-old

Expected output:

restoring <Snapshot 7f3a2b1c of [/etc/nginx/sites-available/mysite.conf]> to /tmp/restore
Summary: Restored 1 files/dirs (4.212 KiB) in 0:00

Part 6: Weekly Integrity Check

# Add a weekly integrity check timer
sudo tee /etc/systemd/system/restic-check.service << 'EOF'
[Unit]
Description=Restic Repository Integrity Check

[Service]
Type=oneshot
ExecStart=restic check \
  --repo /mnt/backup/myserver \
  --password-file /etc/restic-password \
  --read-data-subset 10%%   # Verify 10% of data chunks (fast + thorough)
StandardOutput=journal
StandardError=journal
EOF

sudo tee /etc/systemd/system/restic-check.timer << 'EOF'
[Unit]
Description=Weekly Restic integrity check

[Timer]
OnCalendar=Sun 04:00:00
Persistent=true

[Install]
WantedBy=timers.target
EOF

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

# Run a check now to verify everything is healthy
sudo restic check \
  --repo /mnt/backup/myserver \
  --password-file /etc/restic-password

Expected output:

using temporary cache in /tmp/restic-check-cache
load indexes
check all packs
check snapshots, trees and blobs
no errors were found

Troubleshooting

Fatal: unable to open config file

Cause: Repository not initialised at this path, or wrong path. Fix: restic init --repo /path/to/repo --password-file /etc/restic-password to create it, or check the path is correct.

Backup taking too long — subsequent backups should be fast

Cause: First backup is always slow (full copy). Subsequent backups are incremental — only changed files are uploaded. Normal behaviour: A 10GB initial backup might take 5 minutes. The same directory the next day (with minimal changes) should take 10–30 seconds.

restic check reports errors

Cause: Repository corruption — possibly from interrupted backup, disk failure, or transmission error. Fix:

# Rebuild the repository index
restic rebuild-index --repo /mnt/backup/myserver --password-file /etc/restic-password
# Then re-check
restic check --repo /mnt/backup/myserver --password-file /etc/restic-password

If errors persist, restore from the offsite backup copy.


Conclusion

Restic is running: encrypted incremental backups to a local repository every night at 2:30 AM, automatically pruned to 7 daily + 4 weekly + 12 monthly snapshots, with a weekly integrity check. The identical command syntax against the SFTP remote repository provides the offsite copy for the 3-2-1 backup rule.

This backup infrastructure protects the data on the servers built throughout the Dev Corner guides — see Ubuntu 24.04 LTS Server Setup Checklist for the full server hardening workflow this backup system completes.


People Also Ask

Is Restic better than rsync for backups?

They solve different problems. rsync is a file sync tool that copies files from source to destination — it is fast and simple but the destination is a plain directory that can be accidentally modified or deleted. Restic is a proper backup tool — it stores immutable snapshots, deduplicates data across snapshots, encrypts everything, and supports versioning and retention policies. Use rsync for mirroring files between servers. Use Restic for server backups where you need encryption, versioning, and data integrity verification.

Where should I store the repository password?

The password file (/etc/restic-password) must be: (1) readable by the user/service running restic, (2) not readable by other users (chmod 600), and (3) stored separately from the backup repository. If the password is lost, the backup is permanently unrecoverable — Restic’s AES-256-CTR encryption has no backdoor. Store the password in a password manager (Bitwarden, KeePass) as well as in the file. For team setups, store it in a secrets manager (HashiCorp Vault, Infisical). Never store the password in the same location as the repository.

Can Restic back up a running PostgreSQL or MySQL database?

Restic can back up database data directories, but this is not safe for running databases — the files may be in an inconsistent state mid-transaction. The correct approach: dump the database first, then back up the dump. A pre-backup hook in the restic-backup.sh script handles this:

# In restic-backup.sh, before the restic backup command:
sudo -u postgres pg_dumpall | gzip > /var/backups/postgres-$(date +%Y%m%d).sql.gz

Restic then backs up the compressed dump file, which captures a consistent snapshot.


Further Reading


Tested on: Ubuntu 24.04 LTS (Hetzner CX22). Restic 0.17.1. Last verified: April 28, 2026.

Divya Prakash

About the Author

AI Systems Architect & Founder

Graduate in Computer Science | 12+ Years in Software Architecture | Full-Stack Development Lead | AI Infrastructure Specialist

Divya Prakash is the founder and principal architect at Vucense, leading the vision for sovereign, local-first AI infrastructure. With 12+ years designing complex distributed systems, full-stack development, and AI/ML architecture, Divya specializes in building agentic AI systems that maintain user control and privacy. Her expertise spans language model deployment, multi-agent orchestration, inference optimization, and designing AI systems that operate without cloud dependencies. Divya has architected systems serving millions of requests and leads technical strategy around building sustainable, sovereign AI infrastructure. At Vucense, Divya writes in-depth technical analysis of AI trends, agentic systems, and infrastructure patterns that enable developers to build smarter, more independent AI applications.

View Profile

Further Reading

All Dev Corner

Comments