The 3-2-1 backup rule

3 min read07 November 2026

The 3-2-1 rule: three copies, two different media, one off site. It covers the three scenarios that destroy data: hardware failure, human error and ransomware. A provider snapshot is one copy, not a complete strategy.

The three dimensions

Rule What it protects against Concrete example
3 copies accidental deletion, corruption production, local archive, remote copy
2 media failure of one disk or storage VPS SSD and separate object storage
1 off site loss of the provider or site another machine, region or account

A snapshot on the same storage as the VPS is not a second medium: if the storage disappears, both copies disappear. Backups included in the plan are useful, but do not replace a copy whose destination and retention you control.

Produce a usable copy

For files, a dated archive often suffices. For a database, the dump must be consistent: --single-transaction avoids an intermediate state.

Bash
sudo tar --one-file-system -czf /var/backups/www-$(date +%F).tar.gz /var/www /etc/nginx
mysqldump --single-transaction --routines --triggers -u backup -p base > /var/backups/base-$(date +%F).sql

Both files stay on the VPS: only the first copy. Send them elsewhere with a tool that handles increments and integrity checks, such as restic:

Bash
export RESTIC_REPOSITORY=sftp:backup@backup-host:/srv/restic
restic init
restic backup /var/www /etc/nginx
restic check

The restic password must not be stored in Git or in the cron script. Keep it in a root-only file (chmod 600) and back it up separately: without it, the repository is unreadable.

Encrypt and set retention

Encrypt before the copy leaves the server. age or gpg --symmetric --cipher-algo AES256 produce a file protected by a passphrase. Keep the decryption key apart from the backup, otherwise theft of the media gives access to both.

Set retention, for example 7 daily, 4 weekly and 6 monthly copies. Ensure purging never removes every copy from one day. If supported, enable immutability or append-only mode: ransomware that reaches the server must not encrypt mounted backups.

Test the restore

An untested backup is an assumption. Restore into a temporary directory without overwriting production:

Bash
restic restore latest --target /tmp/restore-test
restic check --read-data-subset=5%

Open a random file, check the tree size, import the dump into a test database and start the application on a temporary port. Record the duration: that is your real recovery time, often much longer than estimated.

Mistakes that break the rule

  • Backing up a database by copying MySQL files instead of a consistent dump.
  • Keeping the encryption key on the same server as the copies.
  • Letting a cron job fail silently for months.
  • Restoring a backup without checking it predates the incident.
  • Deleting old copies before validating the new one.

A cron job must log, return an explicit error code and alert on failure. A backup is measured by its restore, not its file size.

Advertisement

Was this article helpful?

Read next