IAnswerable

The 3-2-1 Backup Rule for a One-Person Team

24 Apr 2026

The 3-2-1 rule: three copies of your data, on two different types of media, with one copy off-site. It sounds like enterprise IT theater. For a one-person team it’s a Saturday afternoon and about $5 a month.

What “copy” actually means here

Your production database counts as one of the three, not all three. If it’s the only place your data exists, you have one copy, not three, and 3-2-1 hasn’t started yet.

“Different media” mattered more when tape and disk were the realistic choices. Today read it as different failure domain: a snapshot on the same host doesn’t count, because a host failure takes both copies with it.

The setup that costs almost nothing

  1. Automate a dump. Cron a pg_dump (or mysqldump, or your DB’s native tool) nightly, compressed, to a local directory on the VPS.
  2. Push it off the VPS immediately. Sync that directory to an S3-compatible bucket — Backblaze B2 is the cheap option, roughly $6/TB/month, no egress fees for the retrieval you’ll actually do in a disaster.
  3. Push it to a second provider. This is the step people skip. If your VPS host and your backup bucket are both, say, on the same cloud account, one compromised login kills both. Use a bucket under a different login, ideally a different provider entirely, or at minimum a hardware key on that account that isn’t reused anywhere else.

That’s copies 2 and 3 done with two rclone targets and a cron job:

#!/bin/bash
# nightly-backup.sh
set -euo pipefail
pg_dump mydb | gzip > /backups/db-$(date +%F).sql.gz
rclone copy /backups/db-$(date +%F).sql.gz b2:my-backups/db/
rclone copy /backups/db-$(date +%F).sql.gz r2:my-backups-secondary/db/
find /backups -name "*.sql.gz" -mtime +7 -delete

Cron it at 3am. Total cost: a few dollars a month in storage, zero in engineering time after the first setup.

flowchart LR
    A["Production DB\n(copy 1)"] -->|"dump + rclone"| B["Backblaze B2\n(copy 2)"]
    A -->|"dump + rclone"| C["Second provider\n(copy 3)"]
  

Copies 2 and 3 only count if they’re a different failure domain — same cloud account for both means one compromised login takes out both.

Test the restore, not just the backup

A backup you haven’t restored is a hypothesis. Once a quarter, pull the latest dump down to a scratch machine and actually restore it:

gunzip -c db-2026-04-01.sql.gz | psql mydb_test

If this fails, you want to find out on a Tuesday afternoon, not during the incident. The most common failure: the dump succeeded but the compressed file is empty because pg_dump errored after the pipe already opened the gzip stream. set -o pipefail in your backup script catches this — without it, a failed dump followed by a successful (empty) upload looks like a green cron job in your logs.

What this doesn’t cover

3-2-1 protects against data loss — disk failure, ransomware, a bad migration, a fat-fingered DROP TABLE. It does not protect against extended downtime, which is a different problem with a different checklist: knowing how to stand a new server up from these backups, not just knowing the backups exist.

The one thing to fix today

If your only backup lives in the same account as your production server, you don’t have 3-2-1. You have 1, with extra steps. Open a second account somewhere else before you do anything else on this list.