GhostVPSghostvps.shop Open Panel

Back to Blog

Guide

How to back up a VPS with Restic before it fails

June 25, 2026 ~10 min read BackupsResticSelf-hosting

A VPS feels solid until the day it does not: a bad deploy, a deleted volume, a compromised app, a full disk, a failed upgrade, or simply human error. The fix is not heroic incident response. The fix is boring: encrypted offsite backups, automatic schedules, retention, and regular restore tests.

Contents

  1. The backup plan
  2. Install Restic
  3. Choose an offsite repository
  4. Back up files without junk
  5. Docker volumes and databases
  6. Automate with systemd
  7. Test a restore
  8. FAQ

The backup plan

The goal is not "a backup command ran once". The goal is this:

  1. Back up the data that matters. Usually that means app directories, Compose files, config, uploaded files, database dumps, SSH/access config, and notes needed to rebuild.
  2. Store it offsite. A backup on the same VPS is useful for quick mistakes, but useless if the server is gone. Put the real copy somewhere else.
  3. Encrypt before upload. Restic encrypts repositories, so the storage provider should not need to be trusted with plaintext.
  4. Automate it. Manual backups happen exactly until the week you are tired.
  5. Restore-test it. The only proof of a backup is a restore that worked.
A provider snapshot is helpful, but it is not a complete backup strategy. Snapshots often live in the same account, same provider, and sometimes the same failure domain. Keep an independent encrypted copy somewhere else.

Install Restic

On Ubuntu or Debian, the fastest start is the package manager:

sudo apt update
sudo apt install -y restic
restic version

Create a private place for Restic config and a strong repository password:

sudo mkdir -p /etc/restic /var/backups/pre-restic
sudo chmod 700 /etc/restic /var/backups/pre-restic
openssl rand -base64 48 | sudo tee /etc/restic/password >/dev/null
sudo chmod 600 /etc/restic/password
Store a copy of `/etc/restic/password` somewhere safe outside the VPS. If you lose the repository password, encrypted Restic backups cannot be recovered.

Choose an offsite repository

Restic calls the backup destination a repository. It can be a local path, but for a real VPS backup you normally want a remote repository.

Option A: SFTP on another server

SFTP is simple and works well if you have another server or storage box. Create an SSH key for backups, install the public key on the backup host, then configure Restic:

sudo tee /etc/restic/env >/dev/null <<'EOF'
export RESTIC_REPOSITORY="sftp:backupuser@backup.example:/srv/restic/my-vps"
export RESTIC_PASSWORD_FILE="/etc/restic/password"
EOF
sudo chmod 600 /etc/restic/env

Option B: S3-compatible object storage

Many storage providers expose an S3-compatible API. Keep the access key limited to the backup bucket where possible:

sudo tee /etc/restic/env >/dev/null <<'EOF'
export RESTIC_REPOSITORY="s3:https://s3.example.com/my-bucket/my-vps"
export RESTIC_PASSWORD_FILE="/etc/restic/password"
export AWS_ACCESS_KEY_ID="replace-me"
export AWS_SECRET_ACCESS_KEY="replace-me"
EOF
sudo chmod 600 /etc/restic/env

Initialize the repository once:

sudo bash -c 'source /etc/restic/env && restic init'

Back up files without junk

Do not blindly back up the whole filesystem. You want the parts needed to rebuild the machine, not pseudo-filesystems, caches, temporary files, or Docker overlay layers.

Create an exclude file:

sudo tee /etc/restic/excludes >/dev/null <<'EOF'
/dev
/proc
/sys
/run
/tmp
/mnt
/media
/var/cache
/var/tmp
/var/lib/docker/overlay2
/var/lib/docker/tmp
*.sock
EOF
sudo chmod 600 /etc/restic/excludes

Run the first backup manually:

sudo bash -c 'source /etc/restic/env && \
restic backup \
  /etc \
  /home \
  /root \
  /opt \
  /srv \
  /var/backups/pre-restic \
  --exclude-file /etc/restic/excludes \
  --tag vps'

Then list snapshots and check repository metadata:

sudo bash -c 'source /etc/restic/env && restic snapshots'
sudo bash -c 'source /etc/restic/env && restic check'

Docker volumes and databases

For Docker, the rule is simple: containers are replaceable, persistent data is not. Back up your `docker-compose.yml`, `.env.example`, Caddy/Nginx configs, and the bind mounts or named volumes that hold app data.

Find named volumes

docker volume ls
docker volume inspect volume_name --format '{{ .Mountpoint }}'

If the volume contains normal app files, export it to a tarball before Restic runs:

sudo docker run --rm \
  -v volume_name:/data:ro \
  -v /var/backups/pre-restic:/backup \
  busybox \
  tar czf /backup/volume_name.tgz -C /data .

For databases, prefer a real database dump. A filesystem copy of a live database can be inconsistent unless the database is stopped or snapshotted correctly.

PostgreSQL example

sudo docker exec postgres \
  pg_dumpall -U postgres \
  | sudo tee /var/backups/pre-restic/postgres.sql >/dev/null

MySQL/MariaDB example

sudo docker exec mysql \
  mysqldump --all-databases --single-transaction -uroot -p'REPLACE_PASSWORD' \
  | sudo tee /var/backups/pre-restic/mysql.sql >/dev/null
Do not pipe a database dump straight into Restic and assume success. If the dump command fails, you can accidentally back up an empty or incomplete stream. Write the dump to a file, check that it is non-empty, then back up that file.

Automate with systemd

Create a small script that prepares dumps, runs Restic, applies retention, and checks the repository.

sudo tee /usr/local/sbin/restic-vps-backup >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

source /etc/restic/env
mkdir -p /var/backups/pre-restic

# Optional: add your database dumps or Docker volume exports here.
# Example:
# docker exec postgres pg_dumpall -U postgres > /var/backups/pre-restic/postgres.sql

restic backup \
  /etc \
  /home \
  /root \
  /opt \
  /srv \
  /var/backups/pre-restic \
  --exclude-file /etc/restic/excludes \
  --tag vps

restic forget \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  --prune

restic check
EOF
sudo chmod 700 /usr/local/sbin/restic-vps-backup

Add a systemd service:

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

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-vps-backup
EOF

Add a daily timer:

sudo tee /etc/systemd/system/restic-vps-backup.timer >/dev/null <<'EOF'
[Unit]
Description=Run Restic VPS backup daily

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

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now restic-vps-backup.timer
systemctl list-timers restic-vps-backup.timer

Run it once and inspect the logs:

sudo systemctl start restic-vps-backup.service
journalctl -u restic-vps-backup.service -n 80 --no-pager

Test a restore

This is the part most people skip, and the part that matters most.

Restore the latest snapshot into a temporary directory:

sudo mkdir -p /tmp/restore-test
sudo bash -c 'source /etc/restic/env && \
restic restore latest --target /tmp/restore-test'

Check that key files are present:

ls -la /tmp/restore-test/etc
ls -la /tmp/restore-test/opt
ls -la /tmp/restore-test/var/backups/pre-restic

For a database, test that the dump can at least be read:

head -n 20 /tmp/restore-test/var/backups/pre-restic/postgres.sql

For a Docker volume tarball, test-list it:

tar tzf /tmp/restore-test/var/backups/pre-restic/volume_name.tgz | head
Write down the restore steps in your project repository. During an outage, a short `RESTORE.md` is worth more than a perfect backup command you cannot remember.

What to keep with every app

For each service you host, keep a small recovery bundle:

Security notes

Need a clean VPS to host your stack?

Deploy a no-KYC VPS, harden it, and set up backups before you put real data on it.

Deploy a VPS

FAQ

Are VPS snapshots enough for backups?
Snapshots are useful for fast rollback, but they should not be your only copy. Keep an encrypted offsite backup outside the VPS provider account.
Should I back up Docker containers or volumes?
Back up Compose files, env templates, configs and persistent volumes or bind mounts. Containers can usually be recreated from images; the data is what matters.
Where should I store Restic backups?
Use storage separate from the VPS: SFTP on another server, S3-compatible storage, Backblaze B2, Wasabi, or another Restic backend. Keep credentials scoped and private.
How often should I test restores?
Test once after setup, then repeat regularly, for example monthly or before major app upgrades. A restore test catches missing paths, empty database dumps and forgotten secrets.

Further reading

Pair this with our new VPS hardening checklist, the Nginx reverse proxy guide, and the pillar guide on anonymous VPS hosting.


GhostVPS is an anonymous, no-KYC VPS host on real DigitalOcean infrastructure. Pay with Bitcoin, Monero or USDT (TRC20); deploy in minutes from $9/mo. See pricing or open the panel.