GuidesGuide

Back Up Websites to S3 with restic: A Complete Linux Guide

A backup that lives on the same server as your sites dies with that server. The most reliable low-cost way to protect websites and databases is to ship them every night, encrypted, to S3-compatible storage with restic: dump the databases consistently first, keep several versions, and test a restore on a schedule. This guide gives you every command for Ubuntu 24.04, then shows how the same job looks in Koapanel.

What you actually need to back up

On a hosting server the list is short, and every item matters:

  • site files (/var/www), minus caches and temp files;
  • databases, as SQL dumps. Copying /var/lib/mysql while MariaDB is running often gives you files that won't start;
  • configuration: /etc/nginx, /etc/php, /etc/letsencrypt, any cron jobs;
  • mailboxes, if you host mail on the server.

The 3-2-1 rule

Keep 3 copies of your data, on 2 different kinds of storage, with 1 off-site. On a VPS that means:

  1. the live data on the server;
  2. a backup in S3 storage at a different vendor (or at least a different data center);
  3. a second, independent copy: another bucket, a disk in your office, or your VPS provider's snapshots.

Provider snapshots alone are not enough. Lose access to the account, or hit a provider-wide incident, and they're gone along with the server.

Choosing S3 storage

Anything that speaks the S3 API works with restic. What matters is price per TB, the cost of getting data back out (egress) and any minimum retention rules.

Service List price Worth knowing
Backblaze B2 $6.95 per TB/month free egress up to 3x your stored data, no minimum storage duration
Wasabi from $7.99 per TB/month no egress or API fees, but a minimum storage duration that depends on your plan
Cloudflare R2 $0.015 per GB/month free egress, 10 GB/month free, operations billed separately
Hetzner Object Storage monthly base price base price includes 1 TB storage and 1 TB egress, EU data centers
Amazon S3 varies by class and region the default choice, but egress and request fees add up
MinIO free software self-hosted on another server, handy as your second copy

Prices checked on the official pages in September 2026. Two practical tips:

  • if you host EU customers' data, pick an EU region; it keeps the GDPR conversation simple;
  • with providers that enforce a minimum duration, pruning old backups early saves nothing, so set retention with that in mind.

Create a dedicated bucket and an access key scoped to that bucket only, never your account's master key.

Why restic

restic is a free, open-source backup tool built for exactly this:

  • always-on encryption: data leaves the server already encrypted (AES-256); your provider only sees unreadable blobs;
  • deduplication: after the first run only changed chunks are uploaded, so nightly backups are fast and small;
  • snapshots: every run is a full point-in-time view you can restore a single file or everything from.

On Ubuntu 24.04 it's in the standard repositories (version 0.16):

sudo apt update
sudo apt install restic
restic version

1. Password and credentials

Keep settings in a root-only directory:

sudo mkdir -p /etc/restic
sudo chmod 700 /etc/restic
sudo sh -c 'openssl rand -base64 32 > /etc/restic/password'
sudo chmod 600 /etc/restic/password
sudo nano /etc/restic/env

Contents of /etc/restic/env (Backblaze B2 example; the endpoint is shown on your bucket page):

RESTIC_REPOSITORY=s3:https://s3.us-west-004.backblazeb2.com/my-bucket/server1
RESTIC_PASSWORD_FILE=/etc/restic/password
AWS_ACCESS_KEY_ID=your-key-id
AWS_SECRET_ACCESS_KEY=your-secret-key
sudo chmod 600 /etc/restic/env

Store a copy of the repository password off the server (in a password manager). Without it the backups can't be read by anyone, including you.

Initialize the repository:

sudo -i
set -a; . /etc/restic/env; set +a
restic init

2. Consistent database dumps

Ubuntu 24.04 ships MariaDB 10.11 with mariadb-dump (mysqldump still works as an alias). For InnoDB tables, --single-transaction gives you a consistent dump without locking your sites: every table is read at the same logical moment. MyISAM tables get no such guarantee, so either convert them to InnoDB or accept a short lock with --lock-tables.

One dump per database is far easier to restore than a single giant file. Save the full script as /usr/local/sbin/backup-sites.sh:

#!/bin/bash
set -euo pipefail
DUMP_DIR=/var/backups/mariadb
mkdir -p "$DUMP_DIR"
chmod 700 "$DUMP_DIR"
rm -f "$DUMP_DIR"/*.sql
# 1. Dump every database (root logs in over the socket, no password)
for db in $(mariadb -N -e "SHOW DATABASES" | grep -Ev '^(information_schema|performance_schema|mysql|sys)$'); do
mariadb-dump --single-transaction --quick --routines --triggers --events "$db" > "$DUMP_DIR/$db.sql"
done
# Users and grants
mariadb-dump --system=users > "$DUMP_DIR/_users.sql"
# 2. Back up files, config and dumps
restic backup --tag sites --exclude-caches \
--exclude '/var/www/*/tmp' \
--exclude '/var/www/*/public_html/wp-content/cache' \
/var/www /etc/nginx /etc/php /etc/letsencrypt "$DUMP_DIR"
# 3. Retention: 7 daily, 4 weekly, 12 monthly
restic forget --tag sites --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
sudo chmod 700 /usr/local/sbin/backup-sites.sh

The dumps are left uncompressed on purpose: restic compresses and deduplicates on its own, while a .gz file changes completely on every run and defeats deduplication.

3. Scheduling with a systemd timer

A systemd timer beats cron here: results land in the journal, and Persistent=true catches up on a missed run if the server was off. Create /etc/systemd/system/backup-sites.service:

[Unit]
Description=Website backup with restic
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
ExecStart=/usr/local/sbin/backup-sites.sh
Nice=19
IOSchedulingClass=idle

and /etc/systemd/system/backup-sites.timer:

[Unit]
Description=Nightly website backup
[Timer]
OnCalendar=*-*-* 03:15:00
RandomizedDelaySec=15m
Persistent=true
[Install]
WantedBy=timers.target

Enable it and run it once right away:

sudo systemctl daemon-reload
sudo systemctl enable --now backup-sites.timer
sudo systemctl start backup-sites.service
sudo journalctl -u backup-sites.service -n 50
systemctl list-timers backup-sites.timer

If you prefer cron, the equivalent line in /etc/cron.d/backup-sites is:

15 3 * * * root set -a; . /etc/restic/env; set +a; /usr/local/sbin/backup-sites.sh >> /var/log/backup-sites.log 2>&1

4. Checking the repository

You always find out a repository is damaged at the worst possible moment. Once a week:

restic check
restic check --read-data-subset=5%

The first verifies the structure; the second actually downloads and verifies a slice of the data (watch egress costs on providers that charge for it).

5. Testing restores

A backup you have never restored is a hope, not a backup. At least once a month, restore a site into a scratch directory, never over production:

sudo -i
set -a; . /etc/restic/env; set +a
restic snapshots --tag sites
restic restore latest --tag sites --target /root/restore-test --include /var/www/example.com
ls -la /root/restore-test/var/www/example.com

And a database into a throwaway database:

mariadb -e "CREATE DATABASE restore_test"
restic dump --tag sites latest /var/backups/mariadb/shop.sql | mariadb restore_test
mariadb -e "SELECT COUNT(*) FROM restore_test.wp_posts"
mariadb -e "DROP DATABASE restore_test"

Time the whole thing. That's your real recovery time, and the number you should be quoting to clients.

6. Protecting backups from whoever breaks into the server

If an attacker gets root, they also get your S3 credentials and can delete your backups. To limit the damage:

  • use a key scoped to the backup bucket only;
  • turn on bucket versioning with a lifecycle rule that expires old versions after a few weeks, so deleted objects stay recoverable for that window;
  • keep the second 3-2-1 copy under different credentials the server never sees, for example another machine that pulls or replicates the bucket.

Backups in Koapanel

Everything above is what Koapanel does from its Backup page, with no scripts. According to the manual:

  • it uses restic and saves site files and their linked databases, encrypted;
  • the destination is S3-compatible storage (Amazon S3, Wasabi, Backblaze B2, MinIO…), the recommended option, or a local folder, ideally on a separate disk: from the Disks page you can prepare a new disk and mount it at, say, /mnt/backup;
  • on the first save it generates the repository password and shows it to you, to be stored away from the server;
  • the daily automatic backup runs at the time you choose, with daily, weekly and monthly retention; older copies are removed automatically;
  • Back up all sites now runs everything immediately, or you can back up a single site from its tab;
  • admins get an email when a backup fails;
  • restores happen from the site's Backup tab: pick a copy by date and restore files, database or both;
  • S3 keys are stored encrypted on the server.

Users and resellers can see and restore backups of their own sites, without access to the rest of the server.

What's left to you: the second copy in the 3-2-1 rule (bucket replication at your provider, for instance) and restore testing. The manual doesn't describe automatic restore tests, so every so often restore a dedicated test site and check it works. A restore overwrites the current files and databases; if in doubt, take a fresh backup first.

Backups are included in every plan, including the free one: see pricing.

FAQ

How often should I back up?

Daily is enough for most sites. For a busy online store, consider several database runs a day; after the first backup, restic runs are small and quick.

Can several servers share one bucket?

Yes, but give each server its own path (/server1, /server2) and its own password, so a problem with one repository can't touch the others.

How much space will backups use?

Far less than the sum of all copies. Thanks to deduplication and compression, keeping 7 daily, 4 weekly and 12 monthly snapshots usually costs little more than your sites' size plus what changes over time. Check with restic stats.

What if I lose the restic password?

The backups become permanently unreadable. Nobody can recover them, not even your storage provider. Keep the password in at least two places off the server.

Can I restore onto a different server?

Yes. Install restic, copy /etc/restic/env and the password file, then run restic restore. It's also the best way to rehearse a full disaster recovery.

Get your sites backed up tonight

Follow this guide by hand, or install Koapanel on a fresh Ubuntu 24.04 server and switch on S3 backups in minutes, together with the firewall, antimalware and site isolation covered in our guide to securing a VPS for web hosting:

curl -fsSL https://get.koapanel.app | sudo bash

Want a look first? Try the public demo or browse the documentation.

Try Koapanel on your server

One command on Ubuntu 24.04, free up to 3 sites. Are you a provider or an agency? Let's talk wholesale pricing and migrations.

More guides