Learning objectives
After reading this article you will be able to:
- Apply the 3-2-1 rule to an app and its database
- Tell backups, snapshots and replicas apart
- Set up a scheduled backup with an offsite copy and a restore test
On this page
- What is the 3-2-1 backup rule?
- How do you apply it to an app and its database?
- What is the difference between a backup, a snapshot and a replica?
- How do you set up an automated backup with an offsite copy?
- How do you know the backup works?
- What are common backup mistakes?
- How to do this on Square Cloud
- FAQs
Almost everyone who hosts an app has lost data, or knows someone who has: a DROP TABLE in the wrong environment, a dying disk, a locked account, ransomware. What separates a scare from a disaster is having a recent copy, somewhere else, that you already know how to restore.
The 3-2-1 rule is the simplest way to organize that. For an app with a database, it tells you how many copies to keep, where each one goes and what role platform snapshots play.
What is the 3-2-1 backup rule?#
The paper "Data Backup Options", written for US-CERT (now part of CISA), sums it up like this:
- 3 copies of any important file: 1 primary and 2 backups.
- 2 different media types, to protect against different hazards.
- 1 copy stored offsite.
The idea is that each kind of failure takes down at most one copy. A broken disk hits one medium. A fire, an account mistake or a break-in hits everything in the same place, which is why one copy must be far away. The paper cites Peter Krogh's book The DAM Book as the source of the rule.
Today "different media" is read more broadly: a local disk plus object storage, or two independent providers. The principle stays the same: pick places that don't fail together.
How do you apply it to an app and its database?#
Start by listing what you would lose and couldn't recreate. For a bot, API or website, that's usually four things:
| What | Where it lives | How to protect it |
|---|---|---|
| Code | Git repository | The remote repository is already an offsite copy. Clone it on another machine for one more. |
| Database | Database server | Scheduled dump (pg_dump, mysqldump, mongodump) sent offsite. See how to back up a database. |
| Uploaded files | Disk or object storage | Periodic copy to another destination. |
| Environment variables | Platform dashboard | Keep the .env in a password vault, never in Git. See what is a .env file. |
A typical setup looks like this:
- Copy 1: the production database.
- Copy 2: the daily dump on the same server, or a platform snapshot. Cheap and fast to restore.
- Copy 3: the encrypted dump in object storage at another provider. This is the offsite copy.
Copy 3 is the only one that survives if the whole account is lost, which is why it matters more than the other two.
What is the difference between a backup, a snapshot and a replica?#
- Backup: a point-in-time copy you control, stored apart from the source and restorable anywhere. This is what the 3-2-1 rule counts.
- Snapshot: a copy taken by the platform your app or database runs on, restored from its dashboard. It's quick and convenient, but it stays inside the same platform. For the rule, it counts as a good layer, but only one.
- Replica: a second instance that follows the first in real time. It helps availability, but it copies mistakes too. A
DELETEwithoutWHEREreaches the replica within seconds, so it doesn't protect you from human error.
Snapshots and dumps complement each other. A snapshot handles "undo yesterday's mistake" in minutes. A dump at another provider handles "I lost the account".
How do you set up an automated backup with an offsite copy?#
The example uses PostgreSQL and restic, an open source tool that encrypts your data, deduplicates it and uploads it to S3 destinations. Swap the dump for mysqldump or mongodump if you use another database.
Prerequisites: pg_dump and restic installed, an S3 bucket at another provider, and credentials in environment variables. Create the repository once:
export AWS_ACCESS_KEY_ID="..." # keys for the destination bucket
export AWS_SECRET_ACCESS_KEY="..."
export RESTIC_PASSWORD="a-long-passphrase" # encrypts the repository
export RESTIC_REPOSITORY="s3:https://endpoint.example.com/my-bucket"
restic initIf you lose RESTIC_PASSWORD, the repository can no longer be opened. Keep it in a password vault, off the server. Then the daily script:
#!/usr/bin/env bash
set -euo pipefail
set -a; source /etc/backup.env; set +a # load and export DB_*, PGPASSWORD, AWS_* and RESTIC_*
DIR=/var/backups/app
mkdir -p "$DIR"
pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -Fc -f "$DIR/db.dump" # 1. dump the database (custom format, already compressed)
restic backup "$DIR" /srv/app/uploads # 2. send the dump and user uploads offsite
restic forget --keep-daily 7 --keep-weekly 4 --prune # 3. keep 7 daily and 4 weekly, drop the rest
restic check # 4. verify the repository integrityThe script reads DB_HOST, DB_USER and DB_NAME from the environment, and pg_dump reads the password from PGPASSWORD or ~/.pgpass, so it never shows up in shell history. Cron doesn't inherit your shell, so the top of the script loads all of these, plus AWS_* and RESTIC_*, from /etc/backup.env (with 600 permissions). set -a exports them to pg_dump and restic, which a plain source doesn't do. Schedule it with cron at 3 a.m.: 0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1. If cron is new to you, see what is a cron job.
restic also calls its restore points "snapshots", but they live in your bucket and have nothing to do with a platform's snapshots. prune locks the repository while it runs, so don't schedule it at the same time as another backup. To choose the offsite destination, see backups to object storage.
How do you know the backup works?#
A backup that has never been restored is just a hope. Test it for real, at least once a quarter, with the same RESTIC_* and AWS_* variables exported:
restic restore latest --target /tmp/restore-test
createdb restore_test
pg_restore -d restore_test --no-owner /tmp/restore-test/var/backups/app/db.dump
psql -d restore_test -c "SELECT count(*) FROM orders;"Replace orders with one of your tables. Compare the count with production and note how long the restore took: that is your real recovery time. If the restore fails, the script needs fixing, and it's better to learn that now than on the day of the incident.
What are common backup mistakes?#
- Backing up only to the same server or account. Lose the server, lose everything. The offsite copy is not optional.
- Never testing a restore. The error shows up when you need the backup most.
- A script that fails silently. Use
set -euo pipefailand alert when the job does not run (an external monitor that expects a "ping" at the end covers this). - Forgetting uploads and the
.env. The database is safe, but the app won't start without its secrets. - Keeping the encryption passphrase only on the server. If the server disappears, the copy becomes unreadable.
- Retention that is too short. If a mistake goes unnoticed for ten days and you keep seven, every copy is already contaminated.
How to do this on Square Cloud#
On Square Cloud, snapshots fill the fast-recovery layer, copy 2 in the setup above. Databases get an automatic daily snapshot on every plan that includes them, and applications from the Pro plan. Each is kept for 7 days, with the most recent kept for up to 30 days. On any plan you can take manual snapshots before a risky change.
An application snapshot restores in full to the same application or another one, including a new application, in one click in the dashboard. A database snapshot restores to the same database, another one of the same engine or a new database. You can also download a database snapshot as a .zip, and that download is the starting point for your offsite copy: store the file outside the platform, next to the scheduled dump from the example above.
Also keep the code on GitHub and the environment variables saved somewhere safe: the dashboard imports and exports the .env in KEY=VALUE format. Retention and restore details are on the snapshots page.
Deploy
Push from GitHub, the CLI or a ZIP and go live in seconds
FAQs
What does the 3-2-1 backup rule mean?
Keep 3 copies of your data (1 primary and 2 backups) on 2 different types of media, with 1 copy offsite. US-CERT describes it in 'Data Backup Options' and credits the rule to Peter Krogh.
Are snapshots and backups the same thing?
No. A snapshot is a copy taken by the platform your app runs on and restored from its dashboard. A backup, in the 3-2-1 sense, is a copy you control and store elsewhere, so it survives even if the account or platform fails.
Do I need to back up my code if it is on GitHub?
The repository is already an offsite copy, but only of the code. Environment variables, user uploads and the database are not in it and need their own backup.
How often should I back up?
It depends on how much data you can afford to lose. If losing up to a day is acceptable, a daily backup is enough. If not, back up every hour.
Is a replica a backup?
No. A replica copies mistakes too: a DELETE without WHERE reaches it within seconds. A backup keeps old states, while a replica only holds the current one.
More in Deploy
See all 33 articles- What are environment variables?Environment variables are key-value pairs the operating system hands to a process at startup. Learn to read them in Node.js and Python and keep tokens out of code.
- What is a deployment?A deployment takes a version of your code to a server, prepares it and starts it. See the stages, the methods and how to roll back when it goes wrong.
- How much RAM and CPU does my app need?Size RAM and CPU by measuring your app's real peak and adding about 30% headroom. Learn how to measure in Node.js, Python and Java and what to do if short.
- Automatic deploys with GitHub ActionsAutomatic deploys with GitHub Actions ship your code to the server on every push. See the workflow step by step, secrets, tests before deploy and common errors.