for Production Magento Shops
A backup is only a backup once it can actually be restored. We show how a resilient backup strategy and disaster recovery work together for Magento 2: RTO and RPO as the governing metrics, automation, offsite storage and regular restore drills.
Table of Contents
- 1. Backup and Disaster Recovery: Two Disciplines, RTO and RPO
- 2. Database Backups Compared: mysqldump, XtraBackup, mydumper
- 3. Media and Filesystem Backups: pub/media and the var Directory
- 4. The 3-2-1 Backup Rule and Offsite Storage
- 5. Automation: A Backup Script with Rotation and Retention
- 6. The Restore Process and Regular Restore Drills
- 7. The Disaster Recovery Runbook: Roles and Escalation
- 8. Monitoring Backup Jobs and Alerting
- 9. Self-Hosted vs. Managed Cloud Snapshots
- 10. Summary
- 11. FAQ
1. Backup and Disaster Recovery: Two Disciplines, RTO and RPO
Many teams treat backup strategy and disaster recovery as a single topic, yet they are two distinct disciplines with different goals. A backup is a technical copy of data at a given point in time. Disaster recovery is the organizational and technical process that turns that copy back into a working shop as quickly as possible. A team that only takes backups but has no defined recovery process ends up, in an emergency, with a pile of files and no plan. This is exactly where the two governing metrics come in that every resilient backup strategy and disaster recovery approach needs: Recovery Time Objective (RTO) and Recovery Point Objective (RPO).
The RTO answers the question of how long the shop may stay offline after an incident before the business damage becomes unacceptable. For a Magento shop with six-figure daily revenue, an RTO of four hours is an entirely different proposition from an RTO of 24 hours, and the technical architecture behind it differs massively: a warm standby instance versus a cold restore from object storage. The RPO answers the question of how much data loss is acceptable in the worst case, measured as time since the last consistent backup. An RPO of 15 minutes requires transaction log based backups or replication, while an RPO of 24 hours is covered by a nightly mysqldump.
Both values must be negotiated with the business side, not set by IT alone. A backup strategy and disaster recovery approach without documented RTO and RPO values per system is guesswork: nobody actually knows whether the current nightly backup even matches the business requirements. For a Magento 2 shop, this concretely means defining RTO and RPO separately for the database, for pub/media, and for the application logic in app/code, because these three components differ substantially in change frequency and criticality.
2. Database Backups Compared: mysqldump, XtraBackup, mydumper
Choosing a database backup tool is the single most important decision within the backup strategy, because it directly determines how much a backup interferes with live operations. mysqldump is the standard tool, logical, easy to understand and available everywhere, but it creates noticeable load on large Magento databases with multiple gigabytes of sales_order and catalog_product_entity tables. Without --single-transaction, mysqldump implicitly holds locks on InnoDB tables that can block concurrent writes. With --single-transaction, MySQL instead uses a consistent snapshot via MVCC, letting writes continue during the dump as long as no DDL statements intervene.
Percona XtraBackup solves the problem in a fundamentally different way: it copies the physical InnoDB data files directly from the filesystem and then applies the transaction log to produce a consistent state, with practically no locking and no noticeable load on the running instance. For Magento databases beyond 10 to 20 gigabytes, XtraBackup is, in practice, the only option that is both fast and unobtrusive to live operations. The downside is that an XtraBackup restore requires the same MySQL major release and is tied to the physical file structure, whereas a mysqldump export is portable across versions and even between MySQL and MariaDB.
mydumper occupies a middle ground: like mysqldump, it produces logical, portable dumps, but it parallelizes output across multiple threads per table and is therefore noticeably faster than the single-threaded original on large Magento databases. For a day-to-day backup strategy and disaster recovery approach, a combination often works best: XtraBackup for fast, low-load daily full backups, complemented by a weekly logical dump with mydumper or mysqldump for maximum portability, for example when switching database providers.
| Method | Locking Behavior | Speed | Best Suited For |
|---|---|---|---|
mysqldump |
Lock-free for InnoDB with --single-transaction |
Slow on large databases | Small to medium shops where portability matters |
Percona XtraBackup |
Practically lock-free, physical copy | Very fast, minimal live load | Large production Magento databases |
mydumper |
Consistent snapshot, parallel per table | Faster than mysqldump, slower than XtraBackup |
Logical dumps that need parallelization |
| Storage Snapshot | Depends on storage layer, mostly lock-free | Seconds | Cloud VMs with snapshot-capable block storage |
3. Media and Filesystem Backups: pub/media and the var Directory
A purely database-centric view of the backup strategy overlooks a critical part of a Magento shop: the filesystem. The pub/media directory contains product images, category assets and often uploaded customer files, whose references are firmly anchored in the database, for instance in catalog_product_entity_media_gallery. If pub/media is lost while the database stays intact, the shop renders technically correctly but loses every product image, which for an online shop is effectively a full outage. Media backups therefore belong to disaster recovery just as much as the database backup does.
The var directory, on the other hand, mostly contains regenerable content such as cache, session data and compiled code and generally does not need to be backed up, with one important exception: var/log, if audit requirements or an ongoing incident investigation call for a history of exception and system logs. Separating directories that must be backed up from those that are regenerable significantly reduces backup volume and speeds up both the daily backup and a later restore, because less data has to move.
The most critical point in media backups is consistency between the DB state and the media state. If the database is backed up at 2 a.m. and pub/media only at 4 a.m., images uploaded in between might be referenced in the database but missing from the media backup, or conversely files might exist that no database entry points to anymore. The solution is to start both backups in the same run and as tightly timed as possible, ideally running the database backup immediately before or after the tar archive of pub/media, so the window for inconsistency stays minimal.
#!/usr/bin/env bash
# backup.sh: Full production backup, database, media, upload to S3, rotation
set -euo pipefail
IFS=$'\n\t'
readonly TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
readonly BACKUP_DIR="/var/backups/magento"
readonly S3_BUCKET="s3://mironsoft-backups/shop-prod"
readonly RETENTION_DAYS=14
readonly LOG_FILE="/var/log/backup/${TIMESTAMP}.log"
exec > >(tee -a "$LOG_FILE") 2>&1
log() { echo "[$(date +%H:%M:%S)] $1"; }
mkdir -p "$BACKUP_DIR"
# 1. Database backup via Percona XtraBackup (near lock-free)
log "Starting XtraBackup..."
xtrabackup --backup \
--target-dir="${BACKUP_DIR}/db-${TIMESTAMP}" \
--user=backup_user --password="${MYSQL_BACKUP_PASSWORD}" \
--compress --compress-threads=4
xtrabackup --prepare --target-dir="${BACKUP_DIR}/db-${TIMESTAMP}"
tar -czf "${BACKUP_DIR}/db-${TIMESTAMP}.tar.gz" -C "${BACKUP_DIR}" "db-${TIMESTAMP}"
rm -rf "${BACKUP_DIR}/db-${TIMESTAMP}"
# 2. Media and filesystem backup, consistent time window with the DB dump
log "Archiving pub/media..."
tar -czf "${BACKUP_DIR}/media-${TIMESTAMP}.tar.gz" -C /var/www/html pub/media
# 3. Checksums for integrity verification on restore
sha256sum "${BACKUP_DIR}/db-${TIMESTAMP}.tar.gz" > "${BACKUP_DIR}/db-${TIMESTAMP}.sha256"
sha256sum "${BACKUP_DIR}/media-${TIMESTAMP}.tar.gz" > "${BACKUP_DIR}/media-${TIMESTAMP}.sha256"
# 4. Upload to S3-compatible offsite storage, server-side encryption enabled
log "Uploading to offsite storage..."
aws s3 cp "${BACKUP_DIR}/db-${TIMESTAMP}.tar.gz" "${S3_BUCKET}/db/" --sse aws:kms
aws s3 cp "${BACKUP_DIR}/media-${TIMESTAMP}.tar.gz" "${S3_BUCKET}/media/" --sse aws:kms
aws s3 cp "${BACKUP_DIR}/db-${TIMESTAMP}.sha256" "${S3_BUCKET}/db/"
aws s3 cp "${BACKUP_DIR}/media-${TIMESTAMP}.sha256" "${S3_BUCKET}/media/"
# 5. Local rotation: keep RETENTION_DAYS days on disk, S3 lifecycle handles offsite retention
log "Applying local retention policy (${RETENTION_DAYS} days)..."
find "$BACKUP_DIR" -type f -mtime +"$RETENTION_DAYS" -delete
log "Backup completed successfully: ${TIMESTAMP}"
4. The 3-2-1 Backup Rule and Offsite Storage
The 3-2-1 rule is the tried and tested framework that every robust backup strategy and disaster recovery approach should be built around: at least three copies of the data, on at least two different storage media, with at least one copy at a different physical location. For a Magento shop this concretely means the production database itself, a local backup on the application server or a separate backup host, and a third copy in object storage outside the primary data center, for instance at a different cloud provider or a different region of the same provider.
The reason offsite storage is required comes down to risk correlation: a server failure, a ransomware attack that moves laterally across the network, or a full data center incident is highly likely to affect backups sitting on the same system or in the same network segment simultaneously with the production data. Object storage such as S3-compatible services works particularly well as the third copy because it has its own access layer, is reachable independently of the production network, and can be additionally protected against later manipulation or deletion using versioning and object lock.
Encryption needs to cover two states: at rest and in transit. For transport, TLS is usually sufficient, and tools like aws s3 cp use it by default anyway. At rest, server-side encryption with a dedicated key (SSE-KMS instead of SSE-S3) should be enabled, so that even if credentials to the storage bucket are compromised, key access remains separately controlled. Anyone who additionally encrypts client-side before the archive leaves the network is protected even against a compromised storage provider, but then has to reliably manage the key material themselves, since a lost key renders even a correctly uploaded backup worthless.
5. Automation: A Backup Script with Rotation and a Retention Policy
A backup strategy that depends on manual triggering will, in practice, get forgotten, delayed or executed inconsistently. Automation via cron is the bare minimum, and the job definition needs to guarantee three things: a defined execution time outside peak load, complete logging of every run, and a failure notification that does not get lost in cron mail noise. For Magento shops with an international customer base, there is rarely a real nighttime window without traffic, which is exactly why XtraBackup has a real operational edge over a locking mysqldump.
The retention policy defines how long each backup generation is kept and should be tiered: daily backups for 14 days, weekly backups for 3 months, monthly backups for one to three years, depending on compliance requirements. A blanket "delete everything after 14 days" rule, as in the simple rotation script above, is a fine starting point but does not cover the case where a data problem is only noticed several weeks later and an older recovery point is needed. Object storage lifecycle rules often handle tiered retention more elegantly than hand-rolled deletion logic in the backup script, because they apply independently of whether the cron job actually ran.
# /etc/cron.d/magento-backup
# Runs nightly backup at 02:15, logs stdout/stderr, mails only on failure
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=ops@mironsoft.de
15 2 * * * deploy /usr/local/bin/backup.sh >> /var/log/backup/cron.log 2>&1 || echo "Backup FAILED at $(date)" | mail -s "[ALERT] Magento backup failed" ops@mironsoft.de
# Weekly logical dump for portability (Sunday 03:30)
30 3 * * 0 deploy /usr/local/bin/backup-weekly-dump.sh >> /var/log/backup/cron-weekly.log 2>&1
# Restore-drill reminder, first Monday of each month
0 9 1-7 * 1 deploy [ "$(date +\%u)" = "1" ] && mail -s "[REMINDER] Monthly restore drill due" ops@mironsoft.de </dev/null
6. The Restore Process and Regular Restore Drills
A backup that has never been restored is a hypothesis, not a reliable building block of disaster recovery. In practice, restores fail surprisingly often for trivial reasons: a wrong password in the script, an incompatible MySQL version between backup source and target, a forgotten chown on pub/media after extraction, or an XtraBackup archive that was never run through --prepare and is therefore inconsistent. Every one of these mistakes stays hidden until someone actually walks through the full restore process under realistic conditions.
A restore drill is the planned, repeated test of exactly this process, ideally run monthly and in an isolated environment that never touches production. The sequence: pull the latest backup from offsite storage, load the database and media into a fresh staging instance, bring the shop up there and functionally verify that orders, product data and customer data match the expected state. The measured duration of this drill is, at the same time, the most realistic figure available for the RTO actually achievable, often sobering compared with the number assumed on paper.
For a Mark Shust docker-magento operation, the restore drill can be run entirely in a separate Compose environment without ever touching the production instance, which reduces the risk of the test itself to nearly zero. It is important to document the drill: start time, duration per step, problems encountered and how they were fixed. These records feed directly into the disaster recovery runbook and turn a one-off test into a continuously improved process.
#!/usr/bin/env bash
# restore.sh: Pull latest backup from offsite storage and restore DB + media
set -euo pipefail
IFS=$'\n\t'
readonly S3_BUCKET="s3://mironsoft-backups/shop-prod"
readonly RESTORE_DIR="/tmp/magento-restore"
readonly TARGET_WEBROOT="/var/www/html"
mkdir -p "$RESTORE_DIR"
echo "[INFO] Fetching latest backup manifest..."
latest_db="$(aws s3 ls "${S3_BUCKET}/db/" | sort | tail -n 1 | awk '{print $4}')"
latest_media="$(aws s3 ls "${S3_BUCKET}/media/" | sort | tail -n 1 | awk '{print $4}')"
echo "[INFO] Downloading ${latest_db} and ${latest_media}..."
aws s3 cp "${S3_BUCKET}/db/${latest_db}" "${RESTORE_DIR}/"
aws s3 cp "${S3_BUCKET}/media/${latest_media}" "${RESTORE_DIR}/"
echo "[INFO] Verifying checksums..."
aws s3 cp "${S3_BUCKET}/db/${latest_db%.tar.gz}.sha256" "${RESTORE_DIR}/"
(cd "$RESTORE_DIR" && sha256sum -c "$(basename "${latest_db%.tar.gz}.sha256")")
echo "[INFO] Extracting and preparing database..."
tar -xzf "${RESTORE_DIR}/${latest_db}" -C "$RESTORE_DIR"
xtrabackup --decompress --target-dir="${RESTORE_DIR}/db"
xtrabackup --prepare --target-dir="${RESTORE_DIR}/db"
echo "[INFO] Stopping MySQL and swapping data directory..."
systemctl stop mysql
mv /var/lib/mysql "/var/lib/mysql.bak-$(date +%s)"
xtrabackup --copy-back --target-dir="${RESTORE_DIR}/db" --datadir=/var/lib/mysql
chown -R mysql:mysql /var/lib/mysql
systemctl start mysql
echo "[INFO] Restoring pub/media..."
tar -xzf "${RESTORE_DIR}/${latest_media}" -C "$TARGET_WEBROOT"
chown -R www-data:www-data "${TARGET_WEBROOT}/pub/media"
echo "[INFO] Restore complete. Run bin/magento cache:flush and functional checks now."
7. The Disaster Recovery Runbook: Roles, Escalation and Communication
Technology alone does not save an incident if nobody knows who does what in an emergency. A disaster recovery runbook is the documented counterpart to the technical backup strategy and disaster recovery: a concrete, step-by-step document that defines who leads the incident as Incident Commander, who performs the technical recovery, and who owns internal and external communication. Without this clarity of roles, an emergency produces duplicated work, contradictory instructions, and precious lost time at exactly the moment when every minute counts.
The escalation chain must include actual names, phone numbers and backup coverage, not just abstract roles. A runbook that says "the system administrator will be informed" helps nobody at three in the morning if it is unclear which of three system administrators is meant and through which channel they can be reached. A three-tier escalation works well: tier 1 is resolved by the on-call technician using the runbook itself, tier 2 pulls in a senior engineer if the fix has not landed within a defined window, say 30 minutes, tier 3 informs management as soon as it becomes clear the defined RTO will be exceeded.
Communication during an emergency is its own sub-process within disaster recovery and is frequently underestimated. Customers who encounter a status page or a short, honest notice during an outage react noticeably calmer than during complete silence. The runbook should therefore include pre-written communication templates, both for internal stakeholders and for a public status page, so that nobody has to draft copy under stress and only needs to fill in the actual facts.
8. Monitoring Backup Jobs and Alerting on Failures
The most dangerous backup failure is the one nobody notices. A cron job that has been silently failing for three weeks because a password changed or the target partition filled up delivers the false sense of security of an apparently working system, right up until the real emergency hits and it turns out the last usable backup is weeks old. Monitoring the backup jobs themselves is therefore not an optional extra but an integral part of any serious backup strategy and disaster recovery approach.
Two checks are the bare minimum: first, whether the last backup run completed successfully with exit code 0, second, whether a new backup has actually arrived in the target storage within the expected timeframe. The second check matters more than it first sounds: a cron job that, for whatever reason, stops being triggered at all leaves behind no failed run, just no run at all, and a pure exit-code check would never catch that. A "dead man's switch" approach that actively checks whether the newest object in the S3 bucket is younger than a defined window covers exactly this case.
Alerting should be integrated into the team's existing communication channel, such as Slack, so a notice does not sink into a mailbox nobody checks. A clear escalation threshold matters here: a single late run, say due to increased load, does not need to trigger a loud alarm immediately, but two consecutive missed backups or a repeated failure should be reported instantly and impossible to miss.
#!/usr/bin/env bash
# backup-monitor.sh: Check backup age and success, alert via Slack webhook
set -euo pipefail
IFS=$'\n\t'
readonly S3_BUCKET="s3://mironsoft-backups/shop-prod"
readonly MAX_AGE_HOURS=26
readonly SLACK_WEBHOOK_URL="${SLACK_WEBHOOK_URL:?SLACK_WEBHOOK_URL not set}"
notify_slack() {
local message="$1"
local color="$2"
curl -sf -X POST -H 'Content-Type: application/json' \
-d "{\"attachments\":[{\"color\":\"${color}\",\"text\":\"${message}\"}]}" \
"$SLACK_WEBHOOK_URL" > /dev/null
}
latest_object="$(aws s3api list-objects-v2 --bucket "${S3_BUCKET#s3://*/}" --prefix db/ \
--query 'sort_by(Contents, &LastModified)[-1].[Key,LastModified]' --output text)"
latest_key="$(echo "$latest_object" | awk '{print $1}')"
latest_time="$(echo "$latest_object" | awk '{print $2}')"
latest_epoch="$(date -d "$latest_time" +%s)"
now_epoch="$(date +%s)"
age_hours=$(( (now_epoch - latest_epoch) / 3600 ))
if (( age_hours > MAX_AGE_HOURS )); then
notify_slack "Magento backup ALERT: latest DB backup (${latest_key}) is ${age_hours}h old, exceeds ${MAX_AGE_HOURS}h threshold." "danger"
exit 1
fi
echo "[OK] Latest backup ${latest_key} is ${age_hours}h old (threshold ${MAX_AGE_HOURS}h)."
9. Self-Hosted (Mark Shust docker-magento) vs. Managed Cloud Snapshots
In a Mark Shust docker-magento setup, the database and media live in Docker volumes, which makes the backup strategy very tangible: backup jobs can run as their own service in the same compose.yaml, with direct access to the relevant volumes, without going through network shares. The advantage of the self-hosted approach is full control over timing, tooling and destination of the backup, the downside is that the team has to build and maintain the entire automation, monitoring and storage integration itself.
Managed cloud snapshots, as offered by many cloud providers for block storage, shift most of that responsibility onto the provider: a snapshot is created in seconds regardless of database size, and retention is governed by the provider's own lifecycle rules. The trade-off lies in granularity and portability: a snapshot typically backs up the entire disk including the operating system, which is inefficient for a pure database or media restore, and switching cloud providers is made difficult or impossible by proprietary snapshot formats.
For most production Magento operations, a hybrid approach works best: snapshots as a fast, provider-native first line of defense for short-term rollbacks, combined with the application-aware backups described in this article, using XtraBackup and tar archives, as the portable, provider-independent second and third copy under the 3-2-1 rule. That way, disaster recovery keeps working even when the primary cloud or hosting provider is itself part of the problem.
# compose.dev.yaml excerpt: dedicated backup service alongside Mark Shust docker-magento
services:
backup:
image: mironsoft/magento-backup:1.4
restart: unless-stopped
environment:
MYSQL_BACKUP_PASSWORD: ${MYSQL_BACKUP_PASSWORD}
S3_BUCKET: s3://mironsoft-backups/shop-prod
RETENTION_DAYS: "14"
volumes:
- db-data:/var/lib/mysql:ro
- media-data:/var/www/html/pub/media:ro
- backup-staging:/var/backups/magento
depends_on:
- db
entrypoint: ["/usr/local/bin/backup-cron-entrypoint.sh"]
volumes:
db-data:
media-data:
backup-staging:
10. Summary
A resilient backup strategy and disaster recovery approach for a production Magento shop consists of more than a nightly cron job. RTO and RPO provide the measurable targets that every technical decision has to align with. Percona XtraBackup solves the locking problem of large databases, while media backups from pub/media need to happen in tight time proximity to the database backup to guarantee consistency. The 3-2-1 rule with encrypted offsite storage protects against scenarios that hit the production environment and local backups at the same time.
Automation with rotation, regular restore drills and a documented disaster recovery runbook with clear roles and escalation chains turn a plain backup routine into a genuine disaster recovery capability. Monitoring the backup jobs themselves closes the gap where silently failing backups go unnoticed. Whether self-hosted in a Mark Shust docker setup or with managed cloud snapshots, combining multiple backup layers is the most reliable way to bring a Magento shop back online in a planned, predictable way even after a serious incident.
Backup Strategy and Disaster Recovery: The Essentials at a Glance
RTO and RPO
Maximum downtime and maximum accepted data loss, defined per system and agreed with the business side.
Database Backup
Percona XtraBackup for low-load daily backups of large databases, complemented by portable logical dumps.
3-2-1 Rule
Three copies, two media types, one offsite copy in encrypted object storage, independent of the production network.
Restore Drills
A monthly, documented restore test delivers the realistic RTO and surfaces process errors before the real emergency does.
11. FAQ: Backup Strategy and Disaster Recovery
1Backup vs. disaster recovery?
2What do RTO and RPO mean?
3XtraBackup instead of mysqldump?
4Back up pub/media at the same time?
5What does the 3-2-1 rule say?
6Why are untested backups risky?
7What goes into a DR runbook?
8How do you detect missing backups?
9Do cloud snapshots replace backups?
10Backups in the Mark Shust setup?
Mironsoft
Backup concepts, automation and disaster recovery for Magento 2
Could your Magento shop actually be restored in an emergency?
We build resilient backup strategies and disaster recovery processes for production Magento shops, from automation through offsite storage to a regularly tested restore drill.
Backup Concept
RTO/RPO analysis and a tailored backup strategy for database and media
Automated Pipelines
XtraBackup, rotation, offsite upload and monitoring implemented production-ready
DR Runbooks & Restore Tests
Documented escalation chains and regular restore drills for real crisis resilience