To back up a Moodle site properly you need three things from the same moment — the database (a mysqldump, never a file copy), the moodledata directory, and the code plus config.php — stored offsite. A reliable, low-cost way is to use restic (encrypted, deduplicated, incremental snapshots) to back up to Backblaze B2 object storage, scheduled every 12 hours via cron, with retention and Object Lock. This gives you an encrypted, ransomware-resistant, point-in-time-restorable offsite backup for a few dollars a month per site.
Key takeaways
- A backup on the same server or hosting account is not a real backup — you need an offsite copy.
- A complete Moodle backup is three things: the database dump, moodledata, and code + config.php.
- Never file-copy a live database — always take a consistent logical dump (mysqldump --single-transaction).
- restic gives you encryption, deduplication, and point-in-time snapshots; Backblaze B2 gives cheap storage with free-ish egress.
- Automate it with a small script + one cron line (every 12h), plus retention/prune and integrity checks.
- Enable Object Lock for ransomware immunity, and store your repo password + B2 keys off the server.
- A backup you've never restored is not a backup — drill the restore.
Why Moodle backups are non-negotiable
A Moodle site is not just files on a disk. It is years of courses, every learner's grades and certificates, forum history, uploaded assignments, and configuration that took real effort to get right. Lose the server and, without a good backup, you lose all of it — and "the server" fails in more ways than people expect: a bad Moodle upgrade, a corrupt database, a fat-fingered DROP TABLE, a hosting account suspended, a disk that dies, or ransomware that encrypts everything including whatever "backup" sat on the same machine.
The uncomfortable truth is that a backup on the same server, or a snapshot in the same hosting account, is not a backup — it's a convenience copy that dies with the thing it was meant to protect. Real protection means an offsite, encrypted, automated, tested copy. This guide walks through exactly how we do that for the Moodle fleet we run — using restic to back up to Backblaze B2 — with example commands you can adapt, a cost comparison, and the trade-offs to plan for. (All keys, buckets and paths below are dummy placeholders.)
The 3-2-1 rule (the only backup strategy worth following)
The industry-standard rule is simple: keep 3 copies of your data, on 2 different types of media, with at least 1 copy offsite. For a Moodle site that usually means: the live site (copy 1), a local or provider snapshot for fast rollback (copy 2), and an offsite copy in object storage like Backblaze B2 (copy 3). The offsite copy is the one that saves you when the whole server is gone — and it's the one most institutions are missing.
What actually needs backing up in Moodle
Newcomers often back up one part and miss another, then discover at restore time that the pieces don't fit. A complete Moodle backup is three things, and you need all three from the same moment in time.
| Component | What it holds | How to back it up |
|---|---|---|
| Database (MySQL/MariaDB) | Courses, users, grades, activity data, most settings | A dump (mysqldump / mariadb-dump) — never a file copy of the live data directory |
| moodledata | Uploaded files, submissions, sessions, cache | File backup of the moodledata dir, excluding cache/temp/localcache |
| Code + config.php | Moodle core, plugins, and the config that points to the DB and moodledata | File backup of the web root plus the vhost/webserver config |
Miss the database and you have files nobody can log into. Miss moodledata and every uploaded assignment is a broken link. Miss config.php and you can't reconnect the site. For how these pieces sit on a real server, see our guide on Moodle hosting and server architecture.
Why Backblaze B2 for the offsite copy
Backblaze B2 is S3-compatible object storage priced for exactly this job. The headline reasons we chose it: storage is roughly $6 per TB per month, and — crucially for backups — egress (downloading your data back) is free up to three times your stored amount each month. That matters because the whole point of a backup is that one day you'll download it in a hurry, and providers that charge steep egress punish you precisely when you're already having a bad day. B2 also supports Object Lock, which makes snapshots immutable for a set period — ransomware can't delete or encrypt what it can't overwrite.
Why restic (not tar, rsync, or a plain S3 copy)
Restic is a modern backup tool built for object storage. Four properties make it the right engine:
Client-side encryption. Everything is encrypted with AES-256 before it leaves your server, so Backblaze only ever stores an opaque blob. It's zero-knowledge — your data is useless to anyone without your repository password.
Deduplication. Restic splits data into chunks and only ever stores each unique chunk once. Back up a 20 GB moodledata folder every 12 hours and you're not storing 20 GB each time — you store the handful of chunks that changed. This is what keeps the B2 bill tiny.
Incremental snapshots. Every backup is a full, browsable snapshot, but only new data is uploaded. You can restore any point in time without juggling "full vs incremental" chains.
Integrity checking. Restic can verify that what's in B2 is intact and restorable — so you find out about corruption on your schedule, not during a disaster.
The golden rule: never file-copy a live database
This is the single most common way DIY Moodle backups go wrong. Copying the MySQL/MariaDB data directory while the database is running gives you a torn, inconsistent snapshot that may not restore at all. Always take a logical dump with a consistent-read flag, which captures the database as it was at one instant without locking your site:
# MySQL / MariaDB — consistent dump, no long table locks on InnoDB
mysqldump --defaults-file=/opt/moodle-backup/db-backup.cnf \
--single-transaction --quick --databases moodle \
> /var/backups/moodle-dumps/moodle-db.sql
Use a dedicated read-only database user for backups — it only needs to read, never write:
CREATE USER 'moodlebackup'@'localhost' IDENTIFIED BY 'a_dummy_password';
GRANT SELECT, SHOW VIEW, EVENT, TRIGGER, LOCK TABLES ON *.* TO 'moodlebackup'@'localhost';
Store its credentials in a MySQL option file (never on the command line, where they'd leak into the process list). One hard-won gotcha: that .cnf file must contain no # characters in the password line (a # starts a comment and silently truncates the password), no quotes, and no Windows line endings.
Setting it up: from zero to first backup
Install restic and point it at a Backblaze B2 bucket. We use one bucket for the fleet and one folder per VM, so each server's snapshots stay cleanly separated.
# 1) Install restic
sudo apt-get update && sudo apt-get install -y restic
# 2) Point restic at your B2 bucket (dummy values shown)
export RESTIC_REPOSITORY="b2:moodle-backups:moodle-vm-01"
export B2_ACCOUNT_ID="your_b2_key_id"
export B2_ACCOUNT_KEY="your_b2_application_key"
export RESTIC_PASSWORD_FILE="/opt/moodle-backup/.restic-pass" # chmod 600
# 3) One-time: create the encrypted repository
sudo -E restic init
Now take the first snapshot — the DB dump, moodledata, and code together, tagged so you can find them later, with Moodle's throwaway cache directories excluded:
sudo -E restic backup \
/var/backups/moodle-dumps/moodle-db.sql \
/var/moodledata \
/var/www/moodle \
--tag site=learn.example.com --tag type=full \
--exclude /var/moodledata/cache \
--exclude /var/moodledata/localcache \
--exclude /var/moodledata/temp
A tiny wrapper script that dumps the database and then runs that restic backup is all the "automation" you need — a few dozen lines of shell. That's the whole point: robust backups don't require a heavy product, just a couple of simple, well-tested scripts.
Scheduling with cron
Backups only protect you if they run without anyone remembering to run them. A single cron entry gives you a full backup every 12 hours:
# /etc/cron.d/moodle-backup — full backup at 00:00 and 12:00
0 */12 * * * root /opt/moodle-backup/cron-backup.sh
Point the script at a healthcheck/monitoring URL so you get alerted when a backup does not run — a silent cron failure is how people discover, months later, that they haven't had a backup since spring.
Retention and pruning
You don't want infinite snapshots, but you do want depth. Restic's policy language keeps a sensible ladder — recent days, then weeks, then months — and prunes the rest, reclaiming space in B2:
# Keep 7 daily, 4 weekly, 6 monthly snapshots, then prune the storage
sudo -E restic forget \
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 \
--prune
Note: if you enabled Object Lock, prune can't free space until the lock period expires — a deliberate trade-off for ransomware immunity.
Verifying and restoring — the part most people skip
A backup you have never restored is a hope, not a backup. Restic makes verification and point-in-time restore straightforward:
# List snapshots (each is a point in time you can restore)
sudo -E restic snapshots --tag site=learn.example.com
# Prove the repository is intact and restorable
sudo -E restic check
# Restore the whole site to a staging directory (non-destructive)
sudo -E restic restore latest --target /srv/restore --tag site=learn.example.com
# Load the database dump back into MySQL/MariaDB
sudo mariadb moodle < /srv/restore/var/backups/moodle-dumps/moodle-db.sql
For a full server loss, disaster recovery is the same commands pointed at a fresh VM: install restic with the same repository, B2 key and password, restore the files and the dump, recreate the site's real database user (the read-only backup user can't own the live DB), fix ownership, and repoint DNS. Because restic stores each snapshot as a complete point in time, you can also roll back to before a bad upgrade — pair this with our Moodle upgrade guide so you always have an escape hatch.
Cost comparison
The economics are why this approach scales across a fleet. Figures are directional 2026 list prices — check current pricing before you commit.
| Destination | Storage (~/TB/month) | Egress / restore | Notes |
|---|---|---|---|
| Backblaze B2 | ~$6 | Free up to 3× stored, then ~$0.01/GB | S3-compatible; Object Lock; our choice |
| AWS S3 (Standard) | ~$23 | ~$0.09/GB | Powerful but egress makes restores expensive |
| Wasabi | ~$7 | Free (fair-use) | Minimum storage duration & size rules |
| Provider VM snapshots | Varies (often pricey) | n/a | Same account/region — not a true offsite backup |
Because restic deduplicates, a typical small-to-mid Moodle site's ongoing storage is a few GB to low tens of GB, so the real-world B2 bill is often a rounding error — a few cents to a couple of dollars a month per site.
How EDZLMS runs this across a fleet
We operate this exact system for the Moodle sites we host — a config-driven kit where each site is one small config file, backups run every 12 hours to Backblaze B2, retention and integrity checks run automatically, and restores are drilled so we know they work. As a Bengaluru-based Moodle and eLearning company serving 400+ clients and 18,000+ learners, disaster recovery isn't optional for us. If you'd rather not build and babysit this yourself, our DevOps & support team sets it up, monitors it, and owns the restore when it matters. Explore the wider platform on our LMS page.
- 1Create a read-only backup DB user
Grant SELECT/SHOW VIEW/EVENT/TRIGGER/LOCK TABLES only, and store its credentials in a MySQL option file (no '#' in the password line).
- 2Install restic and create the repository
Install restic, set the B2 bucket as the repository (one folder per VM), and run 'restic init' once to create the encrypted store.
- 3Dump the database
Use mysqldump --single-transaction to a staging file — a consistent dump, never a copy of the live data directory.
- 4Back up dump + moodledata + code in one snapshot
Run 'restic backup' over the dump, moodledata and web root, tagged by site, excluding Moodle's cache/temp/localcache dirs.
- 5Schedule it with cron
Add one cron entry to run a wrapper script every 12 hours, and wire a healthcheck URL so you're alerted if a backup doesn't run.
- 6Set retention and integrity checks
Run 'restic forget --keep-daily/weekly/monthly --prune' on a schedule, plus periodic 'restic check'.
- 7Test a restore
Restore to a staging dir and load the dump into a scratch database. Only a completed restore proves the backup works.
Advantages of restic + Backblaze B2
- Encrypted client-side (AES-256) before it leaves the server — zero-knowledge
- Deduplicated + incremental — tiny daily deltas, very low storage cost
- Offsite + Object Lock — survives server loss and ransomware
- Any-point-in-time restore from a single snapshot list
- Cheap B2 storage with free-ish egress on restores
- Just scripts + cron — scriptable and repeatable across a whole fleet
Trade-offs to plan for
- You must store the repo password + B2 keys off the VM — lose them and backups are unrecoverable
- Restores must be tested; an untested backup can't be trusted
- You must dump the DB correctly — never file-copy a live database
- Object Lock can delay prune / space reclaim until the lock expires
- Someone must monitor that cron actually ran (use healthchecks)
- Initial setup and DB-user grants need care to get right
Want backups and disaster recovery handled for you?
EDZLMS sets up, monitors, and drills restic-to-Backblaze backups for Moodle and application fleets — including retention, Object Lock, healthcheck alerting, and tested disaster recovery. Book a free demo at https://edzlms.com/book-a-demo/ or email marketing@edzlms.com.
Schedule a restore drill, not just backups
Put a quarterly calendar reminder to restore a real snapshot to a scratch server and log in. The first time you test a restore should never be during an actual outage.
Frequently asked questions
Isn't Moodle's built-in backup enough?
Moodle's course backup exports individual courses, which is useful for moving a course — but it doesn't capture the whole site, the database, moodledata, or config.php, and it isn't automated or offsite. For real disaster recovery you need a full, offsite, automated backup like restic to Backblaze B2.
Can I just copy the MySQL data directory?
No. Copying a live database's files gives a torn, inconsistent snapshot that may not restore. Always take a logical dump with mysqldump --single-transaction (or mariadb-dump), which captures a consistent point in time without locking the site.
How much does it cost to back up a Moodle site to Backblaze B2?
Very little. B2 storage is around $6 per TB per month, and because restic deduplicates, a typical Moodle site's ongoing storage is only a few to low tens of GB — often a few cents to a couple of dollars a month per site. Egress is free up to three times your stored amount.
Is restic secure?
Yes. Restic encrypts everything with AES-256 on your server before uploading, so the storage provider only ever holds an opaque, encrypted blob. Keep your repository password safe — without it, the backups can't be decrypted (by you or anyone else).
How often should I back up Moodle?
It depends on how much data you can afford to lose (your recovery point objective). We run full backups every 12 hours; busy sites may want more frequent database dumps. Incremental, deduplicated backups make frequent runs cheap.
How do I restore after a full server crash?
Install restic on a fresh VM pointed at the same B2 repository and password, list snapshots, restore the files and database dump, recreate the site's real database user, load the dump, fix ownership and config.php, then repoint DNS. Practising this once makes the real event calm instead of frightening.
What is Object Lock and do I need it?
Object Lock makes stored snapshots immutable for a set period, so even compromised credentials can't delete or encrypt them — strong protection against ransomware. The trade-off is that pruning can't reclaim that space until the lock expires. For production backups it's well worth enabling.
Running Moodle without a tested offsite backup is a bet you don't want to lose. Book a Free Demo and we'll set up restic-to-Backblaze backups and a drilled disaster-recovery plan for your Moodle — so a dead server is an inconvenience, not a catastrophe.