Planning backups that actually work

What to back up, how often, where to keep the copy, and why a backup you have never restored is not a backup yet.

Security7 min read

On this page
  1. Who is responsible for what
  2. What to back up
  3. The 3-2-1 idea, plainly
  4. A copy on the same machine is not a backup
  5. Pull, do not push, if you can
  6. How often
  7. Taking the copies
  8. If you want a tool rather than a script
  9. A backup you have never restored is not a backup
  10. Restoring after a compromise
  11. Related

A backup plan answers three questions: what has to survive, how much recent work you can afford to lose, and where the copy lives when the machine it came from is gone. Everything else on this page is detail underneath those three.

The short version: dump your data with the right tool, keep at least one copy on a machine that is not this one, take the copies often enough that the gap does not hurt, and restore one on purpose before you have to do it in a panic.

Who is responsible for what

Two separate things, and mixing them up is how people end up with nothing.

What the panel gives you. The Backups tab lists the snapshots taken on the schedule that comes with your plan. It is a listing: there is no control to take one, delete one, or restore one, and a restore is carried out by the support team on request. See Backups for what the tab shows and what to put in a restore request.

What you own. Your data, and every copy of it you can act on yourself, immediately, without a ticket and without waiting for anyone. That is the copy this page is about. Snapshots on a schedule you do not set, restored by someone else on their timing, are worth having and are not a plan on their own.

What to back up

Back up the things you cannot reproduce:

  • Databases, dumped with the database's own tool, not copied as files while the service is running.
  • Configuration you have edited. Your web server config, your game server config, your systemd units, your firewall rules, cron jobs.
  • User-generated content. Uploads, character and player data, saves, world files, anything your users created.
  • Licence keys and tokens, kept in a password manager rather than in the backup, but kept.
  • A written note of what is installed and how it was set up. A short file listing the packages, versions, and the handful of things you did by hand turns a rebuild from archaeology into an afternoon.

Do not bother backing up:

  • The operating system and packaged software. Reinstalling is faster than restoring and gives you a clean machine instead of an old one.
  • Anything you can download again, including game server binaries, engine builds, and dependency directories such as node_modules or vendor.
  • Caches, temporary files, and most logs. Keep logs long enough to investigate an incident, which is a retention question rather than a backup question.

The test is simple: if losing it costs you a download, skip it. If losing it costs you work that no longer exists anywhere, back it up.

The 3-2-1 idea, plainly

Three copies of the data. On two different kinds of storage. One of them somewhere else entirely.

  • Three copies means the live data plus two backups, so that discovering one backup is corrupt is an annoyance rather than the end of the story.
  • Two kinds of storage means the copies do not share a failure. Two files on the same disk are one copy wearing a disguise.
  • One off-site means one copy survives the loss of the whole machine, whatever caused it.

You do not need enterprise tooling to satisfy this. A dump on the server, the same dump pulled to a machine at home or to object storage, and a second remote copy kept for longer is a real 3-2-1 that fits in a shell script.

A copy on the same machine is not a backup

It protects you from exactly one failure: you deleting a file and noticing. It protects you from none of the others.

  • The machine is unreachable or the disk is gone. So is the copy.
  • A rebuild. Reinstalling wipes the disk, and the backup on it. See Reinstalling your server.
  • Someone else has root. An intruder who wants leverage deletes or encrypts the local backups first, because that is what makes the rest of the demand work. See What to do if your server is compromised.

So at least one copy leaves the machine. That is not optional, and it is the single change that turns a backup routine into a recovery plan.

You delete afile and noticeThe machine isunreachable orthe disk is goneA rebuild wipesthe diskSomeone else hasrootA copy on the same machineThe dump in /srv/backupsSurvivesLostLostLostDeleted orencrypted firstA copy on another machinePulled by the backup machineSurvivesSurvivesSurvivesSurvivesIf the servercannot delete itSame machineAnother machineYou delete a file and noticeSurvivesSurvivesThe machine is unreachable or the disk isgoneLostSurvivesA rebuild wipes the diskLostSurvivesSomeone else has rootLostDeleted or encryptedfirstSurvivesIf the server cannotdelete it
A copy on the same machine survives one failure of the four, and a copy on another machine survives all of them. Against someone with root it survives only if the server cannot delete it, which is why the next section pulls rather than pushes. Illustration.

Pull, do not push, if you can

If the server holds a credential that can write to and delete from the backup destination, then anyone who takes the server holds it too.

Prefer the reverse direction: the backup machine reaches into the server on a schedule and pulls the files. Where you have to push, use a destination credential that can only add, with retention or object-lock rules applied at the destination so that deleting history requires access the server does not have.

How often

The gap between backups is the amount of work you lose. Pick the gap by asking what is acceptable to redo, then match it:

What it holdsA reasonable gap
Player or character data on a busy serverHourly, because an hour of lost progress is an hour of complaints
A database behind a website or a storeDaily at minimum, more often if orders arrive between the backups
Configuration filesOn change. Keep them in version control and the question disappears
Everything, as a full copyWeekly, alongside the more frequent data-only copies

Two things to decide alongside the frequency.

Retention. Keep some old copies, not only the newest. Corruption, a bad update, and an intrusion are all routinely noticed days after they started, and by then every recent backup contains the problem. A common shape is: keep the last seven daily copies, the last four weekly, and a monthly for as long as you have space.

MonthlyWeeklyDailyThe last seven daily copies onlyEvery copy contains the problemSeven daily, four weekly, and monthlyOlder copies predate itThe problem startsToday: noticedContains the problemCleanMonthlyWeeklyDailyThe last seven daily copies onlyEvery copy contains the problemSeven daily, four weekly, and monthlyOlder copies predate itThe problem startsToday: noticedContains the problemClean
A problem noticed some days after it started. With seven daily copies alone, every copy was taken after it began; the weekly and monthly copies reach back past it, so a clean one still exists. Illustration: the time axis is in bands, not to one scale, and when the problem started is made up.

Timing. Run backups when the server is quiet, and take a fresh one by hand before anything destructive: a rebuild, a plan change, a major version upgrade, or a change to something you do not fully understand.

Taking the copies

Dump databases with their own tools, so the file you get is one the database will load back.

Shell
mysqldump -u root -p --single-transaction --routines --events mydb > /srv/backups/mydb.sql
Shell
pg_dump -U postgres -Fc mydb > /srv/backups/mydb.dump
Shell
sqlite3 /srv/app/data.db ".backup /srv/backups/data.db"

Archive the files that are not in a database:

Shell
tar -czf /srv/backups/config-$(date +%F).tar.gz /etc/nginx /srv/app/config /srv/app/uploads

Then get the result off the machine. From the machine that is keeping the copy, pulling rather than pushing:

Shell
rsync -avz -e ssh root@203.0.113.10:/srv/backups/ ./clouduxe-backups/

Put the whole sequence in a script, run it from cron, and send its output somewhere you will see it:

Shell
0 4 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

If you want a tool rather than a script

Dedicated backup tools handle deduplication, encryption, and retention for you, which is worth having once the script grows. restic and borg are the common choices on Linux. A minimal restic cycle looks like this:

Shell
restic -r sftp:backup@203.0.113.10:/srv/restic init
restic -r sftp:backup@203.0.113.10:/srv/restic backup /srv/backups /srv/app/uploads
restic -r sftp:backup@203.0.113.10:/srv/restic snapshots
restic -r sftp:backup@203.0.113.10:/srv/restic check

A backup you have never restored is not a backup

Until a copy has been read back and the data has come out intact, what you have is a file you believe in. Backups fail quietly in ordinary ways: a dump taken while the database was mid-write, an archive of the wrong directory, a job that has been writing a zero-byte file since a path changed, a repository whose password nobody wrote down.

Test a restore on a schedule you keep, monthly is a reasonable habit, and test it somewhere that is not production:

  1. Restore into a scratch location

    Load the dump into a throwaway database name, or unpack the archive into an empty directory. Never test by restoring over live data.

  2. Check the data came back, not only the file

    Count rows in the tables you care about, open the newest user upload, start the application against the restored copy. A file that exists proves nothing about what is inside it.

  3. Time it

    Note how long the restore took, end to end, including downloading the copy. That number is how long your outage is, and it is usually longer than people expect.

  4. Write down the steps that worked

    Keep the exact commands with your notes. The restore is done under pressure and often by whoever is available, not by whoever wrote the backup script.

Quick integrity checks are worth running on every copy, not only during a test:

Shell
gzip -t /srv/backups/config-2026-08-01.tar.gz
pg_restore -l /srv/backups/mydb.dump > /dev/null

Restoring after a compromise

If the reason you are restoring is that somebody got in, do not restore the most recent backup by reflex. Work out when the intrusion started first, then restore from before it, and restore data rather than whole system images or configuration files that may carry the way back in. What to do if your server is compromised covers the order in full.

Need a server, or a hand with one?

Pick a plan to deploy a server, or open a ticket in the panel. Support runs 24/7/365.