Skip to content
CH SCShop classgeneral · beginner · ~40 min · 6 steps

How to take a website backup that actually restores

Database plus files plus proof: how to capture a complete backup of any CMS site, get a copy off the server, and run the restore drill that turns hope into coverage.

August 10, 2026 · by Dane Petersen

A backup nobody has restored is a rumor. This is the shop’s method for any CMS site — Drupal, WordPress, whatever: capture all three parts, get a copy off the server, and then actually prove it. Works with nothing fancier than SSH access; adapt the commands to your setup. If some of it reads like the backups essay, good — this is that essay with its sleeves rolled up.

Know the three parts you’re capturing

A website is a database (content, users, orders), files (everything uploaded — images, PDFs), and code (the theme and customizations). “We have backups” often turns out to mean one of the three. Missing files is the classic: a restored site where every image is a broken square. All three, every time.

Dump the database

Drupal:

drush sql:dump --gzip --result-file=../backups/db-$(date +%Y-%m-%d).sql

WordPress:

wp db export backup-$(date +%Y-%m-%d).sql

Neither? Raw mysqldump does it anywhere:

mysqldump -u USER -p DATABASE | gzip > db-$(date +%Y-%m-%d).sql.gz

Archive the files

The uploads directory is the part you can’t regenerate. Drupal:

tar -czf files-$(date +%Y-%m-%d).tar.gz web/sites/default/files

WordPress:

tar -czf files-$(date +%Y-%m-%d).tar.gz wp-content/uploads

Code should already live in git — if it doesn’t, tar the whole docroot today and make “get this into version control” a very near-future project.

Get a copy off the server

If the hosting account is the thing that fails — billing lapse, account takeover, a dispute — backups stored inside it fail with it. Pull a copy down, or push to separate storage:

scp user@server:~/backups/db-2026-08-10.sql.gz ~/site-backups/

Cloud storage, a NAS, even a dated folder on your laptop that syncs somewhere — the bar is “exists outside the hosting account.” This is the 3-2-1 rule’s whole point, and it has saved every rescue it was present for.

Run the restore drill

The step everyone skips, which is why it’s the step that matters. Spin up a local environment (DDEV, Local, MAMP — anything), load the dump, unpack the files, and click around:

gunzip -c db-2026-08-10.sql.gz | mysql -u USER -p TEST_DATABASE
tar -xzf files-2026-08-10.tar.gz

You’re proving three things: the restore completes, the content is current, and the result is whole — images load, you can log in. Twenty minutes, twice a year. Checking the canoe for cracks in May beats discovering them mid-lake in July.

Put it on a schedule

A backup you take manually once is a good day, not a system. Cron the dump-and-archive, keep a rolling window (dailies for a week, weeklies for a couple of months), and calendar the restore drill twice a year. If nobody at your organization will actually own this, that’s not a character flaw — it’s just what maintenance plans are for.

That's the lesson. Back to the shop for more — or if this is the chore your organization never gets to,that's literally what we're for.