Wolkra

A backup strategy for your VPS

Updated

A good VPS backup strategy has three layers: automatic server backups for quick full restores, application-level dumps for databases, and an encrypted offsite copy at a second location. Add a snapshot before every risky change, and test a restore regularly, because an untested backup is only a hope.

This guide explains each layer, with commands for Ubuntu 24.04 and Debian 12.

Start with the 3-2-1 rule

The classic rule still holds:

  • 3 copies of your data (the live one plus two backups),
  • on 2 different kinds of storage or systems,
  • 1 of them offsite, at another location or provider.

On a VPS that usually means: the server itself, the provider's backups, and an encrypted copy somewhere else.

Know what you are protecting against

Risk What helps
A bad update or config change Snapshot before the change
Deleted files, broken app Daily backups with several restore points
Database corruption Logical dumps (pg_dump, mariadb-dump)
Compromised server or ransomware Offsite copies the server cannot delete
Lost account access Copies held outside the account

Different risks need different layers. That is why one method is never enough for important data.

Layer 1: provider backups and snapshots

Automatic backups

On Wolkra, you can turn on daily automatic backups per server. They cost 20% of the server price and keep 7 restore points. A backup captures the whole server, so a restore brings back the OS, configuration and data together. That makes it the fastest way back after a serious mistake.

Snapshots

Snapshots are taken on demand and billed per GB. Use them:

  • before upgrading the OS, a database or a major app version;
  • before experimenting with firewall or network settings;
  • as a template: you can create new servers from a snapshot.

See backups and snapshots and the docs for the steps.

Make disk-level copies consistent

A backup of a running server is like pulling the power plug: files are consistent at the file system level, but a database in the middle of a write may need recovery. Databases are built to handle that, but a clean dump (layer 2) removes the doubt.

Layer 2: application-level dumps

PostgreSQL

sudo mkdir -p /var/backups/db && sudo chown postgres: /var/backups/db
sudo -u postgres sh -c 'pg_dump -Fc mydb > /var/backups/db/mydb-$(date +%F-%H%M).dump'

-Fc is the compressed custom format; restore it with pg_restore.

MariaDB or MySQL

sudo sh -c 'mariadb-dump --single-transaction --routines --all-databases | gzip > /var/backups/db/all-$(date +%F-%H%M).sql.gz'

--single-transaction gives a consistent dump of InnoDB tables without locking them.

In Docker

docker compose exec -T db pg_dump -U app app | gzip > ~/backups/app-$(date +%F).sql.gz

Schedule and prune

Put the dump in a script and run it from a systemd timer or cron, then remove old files:

find /var/backups/db -type f -mtime +14 -delete

Layer 3: encrypted offsite copies with restic

restic encrypts, deduplicates and sends backups to many targets: another server over SFTP, or any S3-compatible object storage.

Install

sudo apt install -y restic

Choose a repository

The examples use SFTP to a backup server you control. An S3-compatible bucket works the same way with s3: repository URLs and credentials in environment variables.

sudo mkdir -p /etc/restic && sudo chmod 700 /etc/restic
openssl rand -base64 32 | sudo tee /etc/restic/password >/dev/null
sudo chmod 600 /etc/restic/password

sudo tee /etc/restic/env >/dev/null <<'EOF'
RESTIC_REPOSITORY=sftp:[email protected]:/srv/restic/web-1
RESTIC_PASSWORD_FILE=/etc/restic/password
EOF
sudo chmod 600 /etc/restic/env

Root needs an SSH key that can log in to backup.example.com (see SSH keys). Store the restic password somewhere outside the server too. Without it the backups cannot be read by anyone, including you.

Initialise and run

sudo bash -c 'set -a; . /etc/restic/env; restic init'
sudo bash -c 'set -a; . /etc/restic/env; restic backup /etc /var/www /var/backups/db /home --exclude-caches'
sudo bash -c 'set -a; . /etc/restic/env; restic snapshots'

Retention

sudo bash -c 'set -a; . /etc/restic/env; restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune'

That keeps a week of dailies, a month of weeklies and half a year of monthlies.

Automate with a systemd timer

/etc/systemd/system/restic-backup.service:

[Unit]
Description=restic backup
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
ExecStart=/usr/bin/restic backup /etc /var/www /var/backups/db /home --exclude-caches
ExecStartPost=/usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

/etc/systemd/system/restic-backup.timer:

[Unit]
Description=Nightly restic backup

[Timer]
OnCalendar=*-*-* 03:30
RandomizedDelaySec=15m
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer

Run the database dump before restic, for example with an ExecStartPre= line that calls your dump script.

Protect against a compromised server

If an attacker gets root, they can delete backups the server can reach. Reduce that risk:

  • give the backup user on the target append-only access where the tool supports it (restic's REST server has an --append-only mode);
  • run pruning from a different machine;
  • keep at least one copy in a place the server has no credentials for.

What to back up

  • /etc for configuration.
  • Application code and uploads: /var/www, /srv, /opt or your app directory.
  • Database dumps, not raw database files.
  • Docker volumes for stateful containers, or their dumps.
  • TLS and app secrets, encrypted.
  • A short README that says how to rebuild the server.

You usually do not need /var/cache, /tmp, package caches or Docker image layers that can be pulled again.

Block storage volumes

If your data lives on a block storage volume, the volume can be detached and attached to a new server, which helps with server failures. It is still one copy, so include it in restic or dumps like any other data.

Test the restore

Pick a schedule, for example monthly, and do one of these:

  1. Full server: create a new server from a backup or snapshot, start it on a test name and check that the app works.
  2. Files: restore to a temporary directory and compare.
sudo bash -c 'set -a; . /etc/restic/env; restic restore latest --target /tmp/restore-test --include /etc/caddy'
sudo bash -c 'set -a; . /etc/restic/env; restic check'
  1. Database: load a dump into a fresh database and run a few queries.

With hourly billing, a restore test server for an hour costs very little. Write down how long the restore took; that is your real recovery time.

A simple plan to copy

What How Retention
Whole server Provider daily backups 7 restore points
Before changes Snapshot Delete after a week
Databases Dump every 6 hours 14 days local
Files and dumps restic, nightly, offsite 7 daily, 4 weekly, 6 monthly
Restore test New server from backup Monthly

Adjust the numbers to how much data you can afford to lose and how fast you need to be back. For the rest of the server setup see first steps on a new VPS and how to secure an Ubuntu server.

Frequently asked questions

What is the difference between a snapshot and a backup?

A snapshot is a point-in-time copy you take by hand, often before a risky change. Backups run automatically on a schedule and keep several restore points. Both protect against mistakes; neither replaces an offsite copy.

How often should I back up a VPS?

At least daily for anything that changes. Databases with important data often need more frequent dumps, sometimes hourly.

Are provider backups enough?

They are a strong first layer and the fastest way to restore a whole server. For important data, add a copy at another provider or location so one account or one site is not a single point of failure.

Should backups be encrypted?

Yes, especially copies stored outside your own servers. Tools like restic encrypt everything before it leaves the server.

How do I know my backups work?

Restore them. Create a new server from a backup or restore files to a test directory on a schedule, and check that the app actually starts.