Deploy Backrest on a VPS
A production-ready web UI for restic — orchestrate scheduled backups, retention, notifications, and snapshot browsing across every machine you own from one self-hosted dashboard.
At a Glance
| Project | Backrest (web UI for restic) |
| License | GPL v3 |
| Recommended Plan | RamNode Cloud VPS 1–2 GB+ (4 GB+ for large repos) |
| OS | Ubuntu 22.04 / 24.04 or Debian 12 |
| Backends | B2, S3, SFTP, Local, Wasabi, R2 |
| Estimated Setup Time | 30–45 minutes |
Prerequisites
- A RamNode VPS running Ubuntu 22.04/24.04 or Debian 12
- Root or sudo access
- A DNS A record (e.g.
backrest.example.com) pointing at the VPS - An offsite repository target (B2, S3, Wasabi, R2, or SFTP host)
Prepare the System
apt update && apt upgrade -y
apt install -y curl tar bzip2 ufw ca-certificatesuseradd -m -s /bin/bash backrestfallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstabInstall Backrest
cd /tmp
curl -sLO https://github.com/garethgeorge/backrest/releases/latest/download/backrest_Linux_x86_64.tar.gz
mkdir backrest && tar -xzvf backrest_Linux_x86_64.tar.gz -C backrest
cd backrest
sudo ./install.sh
systemctl stop backrest# /etc/systemd/system/backrest.service
[Unit]
Description=Backrest
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=backrest
Group=backrest
ExecStart=/usr/local/bin/backrest
Environment="BACKREST_PORT=127.0.0.1:9898"
Environment="BACKREST_DATA=/home/backrest/.local/share/backrest"
Environment="BACKREST_CONFIG=/home/backrest/.config/backrest/config.json"
Environment="XDG_CACHE_HOME=/home/backrest/.cache"
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=false
ReadWritePaths=/home/backrest
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
[Install]
WantedBy=multi-user.targetmkdir -p /home/backrest/.local/share/backrest \
/home/backrest/.config/backrest /home/backrest/.cache
chown -R backrest:backrest /home/backrest
systemctl daemon-reload
systemctl enable --now backrest
ss -tlnp | grep 9898 # confirm 127.0.0.1 onlyFirst-Run Setup over an SSH Tunnel
From your workstation, open a tunnel and complete the Backrest wizard at http://localhost:9898:
ssh -L 9898:127.0.0.1:9898 user@your-vps-ip- Set an instance ID like
ramnode-backrest-01 - Leave authentication enabled and set a strong username + password
nginx Reverse Proxy with Let's Encrypt
apt install -y nginx certbot python3-certbot-nginx
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw --force enableserver {
listen 80;
listen [::]:80;
server_name backrest.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name backrest.example.com;
client_max_body_size 0;
location / {
proxy_pass http://127.0.0.1:9898;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Pass auth header explicitly (required by Backrest)
proxy_pass_header Authorization;
proxy_set_header Authorization $http_authorization;
proxy_buffering off;
proxy_request_buffering off;
proxy_connect_timeout 60s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}ln -s /etc/nginx/sites-available/backrest /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
certbot --nginx -d backrest.example.comConfigure a Repository
In the Backrest UI, click Add Repository. Pick a backend:
- Backblaze B2: URI
b2:bucket-name:/restic-repo+ env varsB2_ACCOUNT_IDandB2_ACCOUNT_KEY - S3-compatible: URI like
s3:s3.amazonaws.com/bucket/restic-repo+AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY - SFTP:
sftp:user@host:/path/to/repo— Backrest 1.12+ generates SSH keys for you
Important: generate a strong random repo password and store it in your password manager. Restic encrypts everything client-side — there is no recovery if you lose it.
Define a Backup Plan
- Paths: directories to back up (overlap is cheap thanks to deduplication)
- Schedule: cronexpr — e.g. nightly at 3:15 AM
15 3 * * * - Retention: keep 7 latest, 14 daily, 8 weekly, 12 monthly, 3 yearly
**/.cache
**/node_modules
**/__pycache__
**/*.tmp
**/lost+found
/var/cache
/var/tmp
/tmp
/proc
/sys
/dev
/runAt the repository level, schedule prune weekly (e.g. 0 5 * * 0) and check monthly with --read-data-subset=10%.
Notifications
Under Settings → Notifications, add a Discord, Slack, Shoutrrr, Gotify, or Telegram destination. Trigger on error and success.
Test with a deliberately failing plan (point at a non-existent path), confirm the alert fires, then fix the plan.
Hardening Passes
- Add nginx Basic Auth in front of Backrest's own login (defense in depth)
- Install fail2ban with the standard
nginx-http-authjail - Restrict the nginx
location /by source IP if you only access from a few networks - Add
/home/backrest/.config/backrest/config.jsonto one of your plans so the Backrest config itself is recoverable
Verify a Restore
The only step in any backup plan that actually matters:
- Open the repository and pick a recent snapshot
- Browse to a non-trivial file, download or restore to
/tmp/restore-test - Compare with the live file:
sha256sumordiff - Repeat any time you change plans, repos, or excludes
Troubleshooting
- Login loops: add
proxy_pass_header Authorization+proxy_set_header Authorization $http_authorizationto nginx - Failed to acquire repo lock: use Unlock Repo after confirming nothing is running
- fatal: bad password: the repo password is wrong — there is no recovery, verify your password manager entry
- OOM during prune: bump VPS plan, add swap, or use
--max-unused
