How to back up and restore a Magento 2 store
A Magento 2 backup needs three things: a dump of the database, the pub/media directory, and app/etc/env.php, which holds the database credentials and the encryption key. vendor/, generated/, pub/static and the caches are rebuilt by Composer and bin/magento, and your code and app/etc/config.php belong in git. Lose the encryption key and the values Magento encrypted, such as payment and shipping integration passwords, cannot be decrypted.
What to back up, and what to skip
This guide covers Magento Open Source, the free edition, and Adobe Commerce on your own servers; both are Magento 2. Adobe Commerce on cloud infrastructure has its own snapshots and ece-tools db-dump, and Adobe runs the servers for Adobe Commerce as a Cloud Service. The store here lives in /var/www/magento, owned by a file system owner named magento in the web server's www-data group.
| Part | Where | Back it up? |
|---|---|---|
| Database | MySQL or MariaDB, set in env.php | Yes. Catalog, customers, orders and configuration. |
| Media | pub/media | Yes. Product images and other uploads cannot be rebuilt. |
| Resized image cache | pub/media/catalog/product/cache | Optional. Rebuilding it is slow on large catalogs; see below. |
env.php | app/etc/env.php | Yes, on its own and encrypted. Encryption key and every connection setting. |
config.php | app/etc/config.php | In git. Modules, scopes and themes. |
| Code | composer.json, composer.lock, app/code, app/design | In git. Back it up only if it is not in a repository. |
auth.json | Next to composer.json, or in Composer's home directory | Keep it with your secrets, or create new keys in Commerce Marketplace. |
| Rebuilt | vendor/, generated/, pub/static, var/cache, var/page_cache, var/view_preprocessed | No. |
Adobe calls config.php the shared configuration file and says to check it in to source control. env.php holds the settings for one installation.
env.php and the encryption key
Adobe's docs describe the key in env.php's crypt section as protecting passwords and other sensitive data with ChaCha20-Poly1305, including credit card data and payment and shipping integration passwords. It is set at install and, by default, stored only in env.php. A dump restored under a different key loads, but those values cannot be decrypted.
Rotating the key with bin/magento encryption:key:change (the Admin page for it was removed in 2.4.8) does not replace the old key. env.php keeps every key in the same value, separated by whitespace; Magento encrypts with the last one and records which key each value used. An env.php saved before a rotation therefore cannot read values written after it. Archive env.php again after every key change:
install -d -m 700 /var/backups/magentotar -czf /var/backups/magento/shop-env-$(date +%F).tar.gz -C /var/www/magento app/etc/env.phpenv.php also holds the database password and the cache, session and queue credentials. Store its archive apart from the database dumps and encrypt it. Adobe's advice for a new key applies to every key: keep a record of it in a secure location, because it is required to decrypt the data.
auth.json holds the 32-character keys Composer uses for repo.magento.com. Composer's docs say to keep it out of git. Keep a copy with your other secrets, or generate a new pair in Commerce Marketplace when you restore.
Dump the database with mysqldump
Adobe's docs say every bin/magento command must run as the file system owner. Read the connection settings from env.php the same way:
cd /var/www/magentosudo -u magento php -r '$db = (include "app/etc/env.php")["db"]["connection"]["default"]; echo $db["host"], " ", $db["dbname"], " ", $db["username"], PHP_EOL;'Put the user and password in an option file, so the password stays out of ps and your shell history:
[client]
user=magento
password="your-password-here"
host=localhostchmod 600 /etc/mysql/magento-backup.cnfmysqldump --defaults-extra-file=/etc/mysql/magento-backup.cnf --single-transaction --no-tablespaces magento | gzip > /var/backups/magento/shop-db-$(date +%F).sql.gz--single-transactiondumps InnoDB tables, Magento's engine, from one consistent snapshot without locking them, so the store keeps taking orders.--no-tablespacesavoids the globalPROCESSprivilege, which Adobe's recommendedGRANT ALL ON magento.*does not include.- Triggers are dumped by default. Keep them: indexers set to Update by Schedule depend on database triggers. Never add
--skip-triggers. - On MariaDB 11 and later, Adobe recommends
mariadb-dump, since themysqldumpname is deprecated there. It is the same program under its new name.
A complete dump ends with a -- Dump completed on line:
gunzip -c /var/backups/magento/shop-db-2026-10-04.sql.gz | tail -n 1Adobe's own dump commands take the whole database, and its backup documentation lists no tables a backup may skip. Its knowledge base does note that index tables can be emptied and rebuilt with a reindex, which can take a long time on a large catalog, so a backup without them makes the restore slower. For a large database, consider a binary backup: Adobe's backup page itself recommends binary tools such as Percona XtraBackup, covered in the XtraBackup guide. The mysqldump guide covers the remaining options.
Archive pub/media
Dump the database first, then the media:
tar -czf /var/backups/magento/shop-media-$(date +%F).tar.gz -C /var/www/magento pub/mediapub/media/catalog/product/cache holds the resized product images. Adding --exclude='pub/media/catalog/product/cache' before -C makes the archive much smaller, but the cache must then be rebuilt after a restore, or pages resize images as they load, which Adobe says slows the site for days to weeks. Adobe's playbook also warns that bin/magento catalog:images:resize is single-threaded, redoes images already resized and cannot be interrupted, and that its faster storefront method still needs under 8 hours for 100,000 images where the CLI takes 6 days. For a big catalog, keep the cache in the backup.
A full media archive every night gets heavy. Tools that only send changed files, such as restic or rsync, suit large pub/media directories better.
What about bin/magento setup:backup?
Magento 2.4.9 still ships bin/magento setup:backup (--code, --media, --db) and setup:rollback, but Adobe's docs mark the feature deprecated as of 2.1.16, 2.2.7 and 2.3.0. It is disabled until you run bin/magento config:set system/backup/functionality_enabled 1, keeps the store in maintenance mode for the whole run, and writes into var/backups on the same server. Adobe also warns that a rollback can silently fail and leave incomplete data. Use the commands above instead.
Restore on a new server
Restoring over the live store on the same server? Run bin/magento maintenance:enable and bin/magento cron:remove, stop any running queue:consumers:start processes, and drop and recreate the database as Adobe's restore article does. Then continue with the tar commands below.
On a new server, install the PHP, MySQL or MariaDB, search engine and Composer versions Adobe's system requirements list for your Magento release, plus Redis or Valkey and RabbitMQ if env.php uses them. Create an empty database and user as Adobe's install guide does:
CREATE DATABASE magento;
CREATE USER 'magento'@'localhost' IDENTIFIED BY 'your-password-here';
GRANT ALL ON magento.* TO 'magento'@'localhost';Clone the repository into /var/www/magento, check out the commit that was live at backup time, put auth.json next to composer.json, and install the locked dependencies as the file system owner. --no-dev skips require-dev packages:
sudo -u magento composer install --no-devtar -xzf /var/backups/magento/shop-env-2026-10-04.tar.gz -C /var/www/magentotar -xzf /var/backups/magento/shop-media-2026-10-04.tar.gz -C /var/www/magentoEdit env.php for this server: the db connection, and the cache, session and queue hosts if they changed. Keep crypt exactly as it is. Then import the dump. Triggers in it name the original database user as their DEFINER, and MySQL 8.4 only lets you create objects for another account with the SET_ANY_DEFINER privilege. Adobe's knowledge base strips the clause on import:
gunzip -c /var/backups/magento/shop-db-2026-10-04.sql.gz | sed -e 's/DEFINER[ ]*=[ ]*[^*]*\*/\*/' | mysql --defaults-extra-file=/etc/mysql/magento-backup.cnf magentoThe triggers are then created for the importing user. If the domain changed, set the base URLs; the scheme and trailing slash are required:
sudo -u magento bin/magento setup:store-config:set --base-url="https://shop.example.com/" --base-url-secure="https://shop.example.com/"Websites or store views with their own base URLs need bin/magento config:set with --scope=websites or --scope=stores and --scope-code=<code>, for web/unsecure/base_url and web/secure/base_url. If the search engine runs on a different host, set catalog/search/opensearch_server_hostname with bin/magento config:set.
Rebuild code, static files and indexes
These follow Adobe's production deployment steps, run as the file system owner from /var/www/magento:
sudo -u magento bin/magento setup:upgradesudo -u magento bin/magento setup:di:compilesudo -u magento bin/magento setup:static-content:deploy en_USsudo -u magento bin/magento indexer:reindexsudo -u magento bin/magento cache:flushsetup:upgradebrings the database schema and data in line with the installed modules.setup:di:compilegenerates factories, proxies, interceptors and the dependency injection configuration ingenerated/.setup:static-content:deploywritespub/static. Adobe's docs giveen_USas its default language, so list every locale your store views use, such asen_US de_DE. It runs only in production mode unless you add-f.indexer:reindexrebuilds every index, including catalog search in OpenSearch, which the dump does not contain.cache:flushempties the whole cache storage, not just Magento's entries.
Set ownership with Adobe's shared-group commands, from /var/www/magento:
find var generated vendor pub/static pub/media app/etc -type f -exec chmod g+w {} +find var generated vendor pub/static pub/media app/etc -type d -exec chmod g+ws {} +chown -R :www-data .chmod u+x bin/magentoThen install the Magento crontab with sudo -u magento bin/magento cron:install, and run bin/magento maintenance:disable if you enabled maintenance mode.
Verify the restore
sudo -u magento bin/magento setup:db:statusAll modules are up to date. means the schema matches the code. bin/magento indexer:status should show every indexer as Ready. Then fetch the storefront and expect 200:
curl -sS -o /dev/null -w '%{http_code}\n' https://shop.example.com/Log in to the Admin, open a product with images, run a storefront search, and test a payment method that uses stored credentials: that last check fails if env.php's key does not match the data. The restore testing guide shows how to repeat this on a schedule.
Automate it with cron
This script writes each file under a temporary name, checks the dump, and only then gives it its real name. tar exits with 1 when a file changed while it was read, so 1 counts as success.
#!/bin/bash
set -euo pipefail
APP="/var/www/magento"
BACKUP_DIR="/var/backups/magento"
KEEP_DAYS=14
STAMP="$(date +%Y-%m-%d_%H%M)"
DB="$BACKUP_DIR/shop-db-$STAMP.sql.gz"
MEDIA="$BACKUP_DIR/shop-media-$STAMP.tar.gz"
mkdir -p "$BACKUP_DIR"
trap 'rm -f "$DB.partial" "$MEDIA.partial"' EXIT
mysqldump --defaults-extra-file=/etc/mysql/magento-backup.cnf \
--single-transaction --no-tablespaces magento | gzip > "$DB.partial"
gunzip -c "$DB.partial" | tail -n 1 | grep -q "Dump completed"
mv "$DB.partial" "$DB"
tar -czf "$MEDIA.partial" -C "$APP" pub/media || [ $? -eq 1 ]
mv "$MEDIA.partial" "$MEDIA"
find "$BACKUP_DIR" -type f \( -name "shop-db-*" -o -name "shop-media-*" \) -mtime +"$KEEP_DAYS" -deletechmod 755 /usr/local/bin/magento-backup.shPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
40 2 * * * root flock -n /run/lock/magento-backup.lock /usr/local/bin/magento-backup.sh >> /var/log/magento-backup.log 2>&1-mtime +14 keeps about two weeks of nightly backups; the pattern leaves the env.php archives alone. Copy the backups off the server each night, for example with rclone; the cron guide covers logging and alerts.
Common errors
| Error | Fix |
|---|---|
Access denied; you need (at least one of) the PROCESS privilege(s) for this operation | Add --no-tablespaces to mysqldump. |
The import fails on a CREATE TRIGGER line with access denied | The dump's DEFINER names another account. Strip it with Adobe's sed command, or import as the same user and host. |
| The store redirects to the old domain | Run setup:store-config:set with the new base URLs, check website and store view scopes, then flush the cache. |
| Every page returns 503 | Maintenance mode is still on. Check with bin/magento maintenance:status, then maintenance:disable. |
| Pages load without CSS or JavaScript | Static files are missing for that theme or locale. Run setup:static-content:deploy with every locale code. |
| Catalog search returns nothing | OpenSearch is not reachable or not indexed. Fix catalog/search/opensearch_server_hostname, then bin/magento indexer:reindex catalogsearch_fulltext. |
| Product images are missing | pub/media was not restored, or the web server cannot write to it to rebuild resized images. Check ownership; catalog:images:resize rebuilds the cache. |
| Payment or shipping integrations fail to authenticate | env.php comes from another install or predates a key rotation. Restore the env.php that matches the dump. |
Moving the store to another provider? See moving a server to a new provider.
Frequently asked questions
- Is bin/magento setup:backup still supported?
- It still ships in Magento 2.4.9, but Adobe has marked it deprecated since 2.1.16, 2.2.7 and 2.3.0. It is disabled by default, puts the store in maintenance mode, and saves to var/backups on the same server.
- What happens if I lose the Magento encryption key?
- The database still restores, but values Magento encrypted with that key, such as payment and shipping integration passwords, cannot be decrypted. You would have to enter them again. The key cannot be recovered from the data.
- Do I need maintenance mode to back up Magento?
- Not for mysqldump with --single-transaction, which reads InnoDB tables from a consistent snapshot while the store runs. The deprecated setup:backup command and the cloud ece-tools db-dump switch it on themselves.
- Can I leave tables out of a Magento database dump?
- Adobe documents no list of tables a backup may skip, and its own dump tools take the whole database. Index tables can be rebuilt with a reindex, but on a large catalog that makes the restore much slower.
- Do I need to back up vendor, generated or pub/static?
- No. composer install rebuilds vendor from composer.lock, setup:di:compile rebuilds generated, and setup:static-content:deploy rebuilds pub/static.
How this was checked
Commands, limits and prices were checked against these official pages, on October 4, 2026:
- Adobe Commerce Installation Guide: Backup and rollback the file system, media, and database
- Adobe Commerce Configuration Guide: env.php reference
- Adobe Commerce Configuration Guide: config.php reference
- Adobe Commerce Configuration Guide: Configuration files for deployment
- Adobe Commerce Admin Systems Guide: Encryption key
- Magento Open Source 2.4 source: Encryptor (crypt/key holds a list of keys)
- Adobe Commerce Installation Guide: Enable or disable maintenance mode
- Adobe Commerce Installation Guide: System requirements
- Adobe Commerce Installation Guide: MySQL (create the database and user)
- Adobe Commerce KB: Create database dump on Adobe Commerce on cloud infrastructure
- Adobe Commerce on Cloud: Back up the database (ece-tools db-dump)
- Adobe Commerce KB: Restore a DB snapshot from Staging or Production (DEFINER removal)
- Adobe Commerce KB: MySQL disk space is low (index tables and reindexing)
- Adobe Commerce Configuration Guide: Single-machine deployment
- Adobe Commerce Performance Best Practices: Deployment flow
- Adobe Commerce Installation Guide: Upgrade the database schema and data
- Adobe Commerce Configuration Guide: Code compiler
- Adobe Commerce Configuration Guide: Deploy static view files
- Adobe Commerce Configuration Guide: Manage the indexers
- Adobe Commerce Configuration Guide: Manage the cache
- Adobe Commerce Configuration Guide: Configure cron jobs
- Adobe Commerce Installation Guide: Configure the store (setup:store-config:set)
- Adobe Commerce Configuration Guide: Set configuration values (config:set)
- Adobe Commerce Configuration Guide: Configure the search engine
- Magento Open Source 2.4.9 source: OpenSearch system.xml (catalog/search/opensearch_server_hostname)
- Adobe Commerce Implementation Playbook: Catalog image resizing best practices
- Adobe Commerce Installation Guide: Configure file ownership and permissions
- Adobe Commerce Installation Guide: Get your authentication keys
- Magento Open Source 2.4.9 source: setup:backup and setup:db:status commands
- Composer: Authentication for privately hosted packages (auth.json)
- Composer: Command-line interface (install)
- MySQL 8.4 Reference Manual: mysqldump
- MySQL 8.4 Reference Manual: Stored Object Access Control (DEFINER)