How to back up a Linux server

Dump the databases, copy the files to another machine every night with rsync and cron, and prove the backup works by restoring it.

3–5 minutes
Open hard disk drive showing the platter and read arm

A backup has to survive whatever destroys the server, so it must live somewhere else. A good rule is 3-2-1: three copies of your data, on two different kinds of storage, with one copy off site.

Back up three things: website files, databases (as dump files, not copied folders), and configuration. Copy them every night to a different machine or storage service, keep several days of history, and test a restore every few months.

The 3-2-1 rule

A widely used rule of thumb: keep 3 copies of your data, on 2 different kinds of storage, with 1 copy off-site. For a web server, that might be the live server, a nightly copy on a backup server or object storage, and your host’s weekly snapshots. The point is that no single failure, mistake or company can take every copy at once.

What to back up

  • Website files, for example /var/www.
  • Databases, dumped to a file. Copying the raw database folder while it runs gives a broken copy.
  • Configuration: /etc/nginx, /etc/php, crontabs, and anything else you changed.

1. Dump the databases

sudo mysqldump --all-databases --single-transaction --routines | gzip > /root/backup/db-$(date +%F).sql.gz

--single-transaction takes a consistent copy of InnoDB tables without locking the site.

2. Copy everything to another machine

rsync over SSH only sends what changed, so nightly runs are fast. Set up an SSH key from the server to the backup machine first.

rsync -az --delete /var/www/ backup@backup.example.net:/backups/server1/www/
rsync -az /root/backup/ backup@backup.example.net:/backups/server1/db/
rsync -az /etc/ backup@backup.example.net:/backups/server1/etc/

3. Run it every night

Put the commands in a script, /root/backup.sh, make it executable with chmod +x, and schedule it with sudo crontab -e:

30 3 * * * /root/backup.sh > /var/log/backup.log 2>&1

Delete old local dumps so the disk does not fill:

find /root/backup -name 'db-*.sql.gz' -mtime +7 -delete

A complete backup script

Put it together in /root/backup.sh:

#!/bin/bash
set -euo pipefail
mkdir -p /root/backup
mysqldump --all-databases --single-transaction --routines | gzip > /root/backup/db-$(date +%F).sql.gz
find /root/backup -name 'db-*.sql.gz' -mtime +7 -delete
rsync -az --delete /var/www/ backup@backup.example.net:/backups/server1/www/
rsync -az /root/backup/ backup@backup.example.net:/backups/server1/db/
rsync -az /etc/ backup@backup.example.net:/backups/server1/etc/

set -euo pipefail makes the script stop on the first error instead of carrying on and reporting success. Check /var/log/backup.log the morning after the first run.

4. Keep history, not just a mirror

A plain mirror copies mistakes too: delete a file today and it disappears from the backup tonight. Keep dated copies, or use a tool built for this, such as restic or BorgBackup, which store compressed, deduplicated, encrypted snapshots.

Snapshots with restic

restic sends encrypted, deduplicated snapshots to a backup server or object storage such as S3, Backblaze B2 or similar. After installing it (sudo apt install restic or sudo dnf install restic from EPEL):

export RESTIC_REPOSITORY=sftp:backup@backup.example.net:/backups/server1
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init
restic backup /var/www /etc /root/backup
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

forget keeps a week of daily snapshots, a month of weekly ones and six monthly ones, and deletes the rest. Store the password somewhere other than the server too: without it, the backups cannot be read.

5. Test a restore

A backup you have never restored is a hope, not a backup. Every few months, restore to a spare server or a local virtual machine and check the site works:

gunzip < db-2026-10-01.sql.gz | mysql

What a restore involves

Write the steps down while things are calm, so you are not working them out during an outage:

  1. Create a new server with the same operating system and install the web stack.
  2. Copy the files back: rsync -az backup@backup.example.net:/backups/server1/www/ /var/www/
  3. Restore the configuration you need from the etc copy, such as Nginx sites and PHP pools. Do not copy the whole /etc over a new system.
  4. Import the databases.
  5. Point DNS at the new server, or test first through your hosts file.

Time it. If a full restore takes four hours, that is your real recovery time, and it is worth knowing before you need it.

Do not rely only on host snapshots

Host snapshots are convenient and fast to restore, but they live with the same company. Keep at least one copy somewhere your host cannot affect.

Monitor your backups

Backups fail silently: a full disk, an expired key, a changed password. Add a check that alerts you when no new backup has arrived. Free “dead man’s switch” services work by expecting a ping at the end of each run, and email you when one is missed:

curl -fsS --retry 3 https://your-check-url >/dev/null

Add that as the last line of the backup script, so it only runs when everything above succeeded.

Something out of date? Software changes. If a step no longer works, tell us and we will check it and update the page.