Disks fail, people run the wrong command, updates break things, and ransomware encrypts whatever it can reach. A backup that lives on the same server, or is reachable with the same credentials, often disappears together with the data it was supposed to protect. The 3-2-1 rule is the simplest reliable framework for avoiding that.

The rule

  • 3 copies of your data: the original plus two backups.
  • 2 different storage types or systems, for example a local disk and object storage.
  • 1 copy off-site, in a different location and with a different provider or account.

A common modern extension is 3-2-1-1-0: plus 1 copy that’s offline or immutable, and 0 errors in restore tests.

Why “backups” on the same server aren’t enough

Copy kept… Survives disk failure? Survives server compromise? Survives account loss / provider issue?
On the same disk No No No
On a second disk in the same server Yes Usually no No
On another server, same provider/account Yes Depends on credentials No
Off-site, different provider, append-only Yes Yes Yes

RAID isn’t a backup either: deletions and corruption are mirrored instantly. See software RAID.

What to back up on a server

  • Databases, as consistent dumps or with database-aware tools. Never copy the raw files of a running database. See database backup automation.
  • Application data: uploads, user files, /var/www, Docker volumes. See Docker volume backups.
  • Configuration: /etc (Nginx, PHP, SSH), crontabs, systemd units, .env files (encrypted!).
  • Code, if it isn’t already in Git.
  • Infrastructure knowledge: the steps or scripts to rebuild the server (Ansible, cloud-init, a README).

Usually skip: OS files, package caches, node_modules, logs (unless you need them), and the cache directories of web apps.

Tools that make 3-2-1 easy

Tool Highlights
restic Encrypted, deduplicated, incremental. Backs up to S3-compatible storage, SFTP, B2 and more. Simple CLI
BorgBackup (+ borgmatic) Encrypted, deduplicated, very efficient over SSH. Append-only mode
rclone Syncs and copies to almost any cloud storage. Pairs well with dumps
rsync Simple mirroring to another server. Add snapshots (rsnapshot) for history
Database tools mysqldump/mariadb-dump, pg_dump, pgBackRest, Percona XtraBackup

Example with restic to S3-compatible object storage:

export RESTIC_REPOSITORY="s3:https://s3.example-provider.com/my-backups/web1"
export RESTIC_PASSWORD_FILE=/root/.restic-pass
export AWS_ACCESS_KEY_ID=… AWS_SECRET_ACCESS_KEY=…

restic init                                            # once
mysqldump --single-transaction --all-databases | gzip > /var/backups/db.sql.gz
restic backup /var/www /etc /var/backups --exclude-caches
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic check

Schedule it with a systemd timer. See systemd timers and rsync.

Protect backups from the server itself

If an attacker gets root on your server, they get its backup credentials too. Make sure they can’t delete or overwrite old backups:

  • Append-only repositories: Borg’s --append-only, or restic’s REST server with --append-only.
  • Object lock / immutability on S3-compatible storage (versioning plus retention), where supported by the provider.
  • Pull-based backups: a separate backup server connects to production and pulls data. Production has no credentials for the backup storage.
  • Separate accounts: backup storage under a different account, with MFA.

Encryption

Backups contain everything: customer data, password hashes, API keys. Encrypt before data leaves the server (restic and Borg do it by default, or use GPG for dumps). Store the encryption keys somewhere else too, because without them the backups are useless. See GPG encryption.

Retention

A typical policy: 7 daily, 4 weekly, 6–12 monthly. Keep enough history to recover from problems noticed late, such as silent corruption, or a compromise discovered weeks afterwards. Legal and contractual requirements may dictate longer.

Test restores (the “0” in 3-2-1-1-0)

A backup that hasn’t been restored is a hope, not a backup.

  • Monthly: restore files and a database to a test server or a temporary directory, and check that they work.
  • Document the restore steps, and time them. How long would a full rebuild take?
  • Monitor backup jobs. Alert when a backup didn’t run or failed, not only on errors. Healthchecks-style “dead man’s switch” pings are ideal.

A complete small-server plan

  1. Nightly: database dumps plus restic backup of data and configs to S3-compatible storage at another provider (off-site, encrypted, with object lock or append-only).
  2. Weekly: an additional copy to a second location (another region or a home NAS), using a different tool or credentials.
  3. Monthly: restore test, then check retention and costs.
  4. Keep rebuild instructions (scripts or Ansible) in Git.

For VPS.DO servers, the second copy can simply be a small storage VPS in another location, receiving backups over SSH (Borg, restic or rsync), plus a third copy at an object storage provider.

Quick reference

Principle Practice
3 copies Original + 2 backups
2 media Local/server + object storage or another server
1 off-site Different provider or region
1 immutable Append-only or object lock
0 errors Regular restore tests

Related: ransomware protection for servers and database backup automation.

  • Backups
  • VPS Guides
  • Best Practices