How to Automate WordPress Backups to Object Storage

A backup you’ve never restored isn’t a backup — it’s a hope with a timestamp. The habit that actually protects a site is to automate WordPress backups of the two things you can’t rebuild, the database and the uploads, push them off the server to object storage, and prove on a schedule that you can restore them. I’ve inherited plenty of “backed-up” systems over the years whose backups turned out to be empty, unreadable, or sitting on the same disk that just died. This is the backup setup I trust: what it captures, how it runs unattended, and why the restore drill is the only part that really counts.

The only test that matters is the restore

Everyone measures backups by whether the job ran. That’s the wrong number. The only measurement that means anything is whether you can take yesterday’s backup and rebuild a working site from it, and most people never check until the day they desperately need to. That’s how you discover the dump was truncated, the uploads were skipped, or the encryption key is gone.

So when I automate WordPress backups, the restore is a first-class part of the design, not an afterthought. A backup exists to be restored, which means an untested backup is an assumption, and assumptions are exactly what fail under pressure. Everything else in this piece — what to capture, how to schedule it, where to store it — is in service of one goal: that when I pull an archive and run the restore, a real site comes back. If that step isn’t proven, the rest is theater.

Flow diagram showing how to automate WordPress backups, from database dump and uploads archive to encrypted object storage and a restore test.
The backup job, end to end: dump the database, archive the uploads, encrypt, push off-site, then prove the restore works. Diagram by Nuriforge (AI-assisted).

What actually needs backing up

Two things on a WordPress site are irreplaceable: the database and the uploads directory. The database holds every post, comment, user, and setting; the uploads folder (wp-content/uploads) holds every image and media file you’ve ever added. Lose either and no reinstall brings it back. Everything else — WordPress core, themes, plugins — is code you can reinstall from source, so it doesn’t strictly need to live in the backup if you manage it as versioned code.

That distinction keeps backups small and fast, which matters more than it sounds. A backup that captures the whole filesystem including reinstallable code is bulky, slow, and expensive to store, so it gets run less often and pruned aggressively — the opposite of what you want. A lean backup of just the irreplaceable data can run frequently and be kept longer for the same cost. The WordPress backup documentation walks through the same split of database versus files, and it’s the foundation everything else builds on.

If you’re unsure whether something belongs in the backup, ask one question: could I recreate this from a source repository or a fresh install? If yes, it’s code and lives in version control. If no, it’s data and must be in the backup. That single test sorts almost everything cleanly.

The backup job, step by step

The job itself is a short shell script, and its shape is the same everywhere. First, dump the database to a single file — WP-CLI’s wp db export or mysqldump both produce a consistent SQL file you can restore from. Second, archive the uploads directory into a compressed tarball. Third, combine those into one timestamped bundle so a given backup is a single, self-contained artifact.

Fourth, encrypt that bundle before it leaves the server, because a backup contains your entire database — users, hashed passwords, private content — and it’s about to sit on storage you don’t physically control. Encrypting locally means the object store only ever holds ciphertext. Fifth, upload the encrypted archive to object storage with a versioned, timestamped key. The whole thing is maybe forty lines, runs in seconds to minutes depending on media size, and needs no plugin. The discipline of describing operations as small, repeatable scripts is the same one behind the content pipeline I run — if it happens on a schedule, it should be code, not a person remembering.

One detail that saves you later: have the script fail loudly. If the database dump is zero bytes or the upload returns an error, the job should exit non-zero and alert you, not quietly “succeed” and leave you with a folder full of empty files. A silent backup failure is worse than no backup, because it’s a failure wearing the costume of safety.

Schedule, retention, and encryption

Unattended is the whole point, so the script runs on a schedule — a cron entry, a systemd timer, or a scheduled task in your platform. Daily is a reasonable baseline for a content site; a busier site might dump the database more often than the uploads, since posts change faster than media. The cadence should match how much work you’re willing to lose, measured in hours.

Retention is a policy, not an accident. Keeping every backup forever is expensive and pointless; keeping only the latest means a corruption that happened last week and went unnoticed is now your only copy. A tiered scheme — keep several daily backups, a few weekly, a couple monthly — balances cost against being able to reach back past a problem you didn’t catch immediately. Set it once as a lifecycle rule on the bucket and let the storage expire old copies for you.

Two more rules make the difference between a backup and real protection. Off-site is mandatory: a backup on the same server, or even the same provider region, is not a backup against the failure that takes that server or region down. And encryption is non-negotiable for the reasons above. Off the machine, in another location, encrypted at rest — that’s the bar.

Flow diagram showing how to automate WordPress backups, from a nightly cron job through database dump and uploads archive to encryption, object storage, and a restore test.
One scheduled run, end to end: cron fires nightly, dumps the database and tars uploads, encrypts, ships off-site to object storage, then runs a restore test. Diagram by Nuriforge (AI-assisted).

Restore drills without touching production

Here’s how I close the loop the setup is built around. On a schedule — monthly is plenty for a small site — I pull the latest backup, decrypt it, and restore it into a throwaway environment that isn’t production: a local container, a scratch database, a staging box. Then I actually load the site and click around. Do the posts render? Are the images there? Does login work?

A throwaway restore target is what makes this safe to do often, because you’re never risking the live site to test the backup. It also surfaces the unglamorous failures that only appear on restore — a character-set mismatch that mangles text, an uploads path that doesn’t line up, a dump that restores but is missing a table. You want to meet those problems during a calm Tuesday drill, not during an outage with the site down and your pulse up. The drill converts “I think we have backups” into “I restored one last month,” and only the second sentence is worth anything.

Plugin backups versus a scripted job

Plugins can back up WordPress, and for some sites they’re the right call. But once you’re running your own infrastructure, a scripted job gives you control the plugin can’t — over where data lands, how it’s encrypted, and how failures are reported. Here’s how the two compare on the things that actually decide whether a backup saves you.

Concern Backup plugin Scripted job to object storage
Runs when Via WordPress cron (needs traffic) System cron / timer, independent of the app
What it captures Often the whole site, bulky Just the irreplaceable data: DB + uploads
Where it lands Depends on the plugin/tier Any object storage you choose, off-site
Encryption Varies, sometimes paid Encrypted locally before upload, always
Failure reporting Easy to miss in the dashboard Non-zero exit + alert you control
Restore testing Rarely automated Scheduled drill into a throwaway target

FAQ

What do I actually need to back up on a WordPress site?

The database and the uploads directory, because those are the only irreplaceable parts. WordPress core, themes, and plugins are code you can reinstall from source, so if you manage them as versioned code they don’t need to be in the backup. Keeping backups to just the data makes them small enough to run often and keep longer.

How often should I automate WordPress backups?

Daily is a sensible baseline for a content site, and the right cadence is however many hours of work you’re willing to lose. A busy site might dump the database more frequently than the uploads, since posts change faster than media. Pair the frequency with a tiered retention policy so you can reach back past a problem you didn’t catch immediately.

Where should WordPress backups be stored?

Off the server, in object storage, ideally in a different location or region from the site itself. A backup on the same disk or the same server is worthless against the exact failure most likely to destroy the original. Encrypt the archive locally before uploading so the storage only ever holds ciphertext.

Do I need a plugin to back up WordPress?

No. A short shell script using WP-CLI or mysqldump plus a tar of the uploads, run from system cron and pushed to object storage, does the job with more control over encryption, destination, and failure alerts. Plugins are convenient, but a scripted job doesn’t depend on site traffic to fire and doesn’t hide failures in a dashboard.

How do I know my backups actually work?

Restore one on a schedule into a throwaway environment that isn’t production, then load the site and check that posts, images, and login all work. This is the single most skipped step and the only one that proves the backup is real. An untested backup is an assumption, and assumptions fail exactly when you need them not to.

My Thoughts

If you take one thing from this, make it the restore drill. It’s tempting to set up the backup job, watch it turn green, and move on — and I understand the pull, because the job running feels like the finish line. It isn’t. The finish line is the day you calmly rebuild a site from an archive and everything comes back, which only happens if you’ve practiced it when nothing was on fire. The backup that saves you is the one you’ve already restored at least once. Everything before that is preparation for a test you haven’t taken yet.