How to back up and restore a Joomla site
A Joomla backup needs three things: a dump of the database named in configuration.php, an archive of the site directory, and configuration.php itself, which holds the database password and $secret. Joomla installs extensions into its own folders, so archive the whole directory minus the caches. mysqldump or pg_dump writes the database; Joomla's database:export command is not a backup tool.
What a complete Joomla backup includes
Joomla is free, open-source software under the GNU GPL. This guide covers Joomla 5 and 6 (5.4.9 and 6.1.4 were released on 29 September 2026). The site lives in /var/www/example.com and PHP runs as www-data, as on Debian and Ubuntu.
| Part | Where | Back it up? |
|---|---|---|
| Database | MySQL, MariaDB or PostgreSQL, named in $db | Yes. Articles, menus, users and every extension's settings. |
| Media | images/, plus files/ on Joomla 5.3 and later | Yes. Uploads cannot be rebuilt. |
| Extensions and templates | components/, modules/, plugins/, templates/, media/, language/ and their administrator/ twins | Yes. Joomla copies each extension's files into these core folders. |
configuration.php | Site root | Yes, on its own and encrypted. Database password and $secret. |
.htaccess | Site root (Apache) | Yes. Joomla ships it as htaccess.txt; yours may hold custom rules. |
| Caches and temp files | cache/, administrator/cache/, tmp/ | No, but keep the empty folders. |
| Logs | administrator/logs/ | Optional. Keep the folder. |
configuration.php stores the temp and log folders as absolute paths, and Joomla's Global Configuration screen says that if the log folder is missing or not writable, Joomla will not load at all. Leave out the folders' contents, never the folders.
Read the database settings from configuration.php
configuration.php defines a JConfig class with one property per setting. Print the database lines:
grep -E 'public \$(dbtype|host|user|db|dbprefix) ' /var/www/example.com/configuration.php$dbtypeismysqliormysqlfor MySQL and MariaDB, orpgsqlfor PostgreSQL.$hostcan carry a port or socket after a colon, such as127.0.0.1:3307; mysqldump needs it as a separateport=orsocket=line in its option file.$dbprefixstarts the name of every Joomla table, for exampleabc12_. The restored tables must keep it.
Joomla's CLI shows the same group, password included: sudo -u www-data php /var/www/example.com/cli/joomla.php config:get --group db. Run the CLI as the web server user so any files it writes stay usable by PHP.
Dump a MySQL or MariaDB database
install -d -m 700 /var/backups/joomlaPut the user and password from configuration.php in an option file, so the password stays out of ps and your shell history:
[client]
user=joomla
password="your-password-here"
host=localhostchmod 600 /etc/mysql/joomla-backup.cnfmysqldump --defaults-extra-file=/etc/mysql/joomla-backup.cnf --single-transaction --no-tablespaces joomla | gzip > /var/backups/joomla/example.com-db-$(date +%F).sql.gz--single-transactionreads InnoDB tables from one consistent snapshot without locking them, so the site stays up. Joomla's core tables are InnoDB, apart from two MEMORY tables Smart Search uses while indexing.--no-tablespacesavoids the globalPROCESSprivilege, which a site's database user rarely has.- The dump covers the whole database, including tables with other prefixes. On MariaDB 11 the same program is called
mariadb-dump.
A complete dump ends with a -- Dump completed on line. Check it, then list the engines in use: a third-party extension's MyISAM tables can change mid-dump, because --single-transaction only covers transactional tables:
gunzip -c /var/backups/joomla/example.com-db-2026-10-04.sql.gz | tail -n 1mysql --defaults-extra-file=/etc/mysql/joomla-backup.cnf -e "SELECT ENGINE, COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'joomla' GROUP BY ENGINE"On PostgreSQL, store the password in /root/.pgpass and write a custom-format dump, which pg_restore --list can check without restoring it. The pg_dump guide covers both:
pg_dump -h localhost -U joomla -Fc -f /var/backups/joomla/example.com-db-$(date +%F).dump joomlaWhat about php cli/joomla.php database:export?
Joomla's CLI includes database:export and database:import, which come from the Joomla Framework's database package. Their source shows why they are no substitute for a dump:
- They write one XML file per table in Joomla's own format, which only
database:importreads. You cannot load it withmysqlorpsql. - Each table is read on its own, outside any transaction, so on a busy site the tables come from different moments. Each table's XML is built in memory first.
- Only tables that start with
$dbprefixare exported. - The options are
--folder,--tableand--zip. Guides that add--allgetThe "--all" option does not exist. database:importdrops each table before loading its file.
Joomla core has no other backup feature. Use mysqldump or pg_dump for the database and tar for the files.
Archive the site files
Dump the database first, then the files, so the database never points at a file the archive lacks. Leave configuration.php out of this archive; it gets its own below.
tar -czf /var/backups/joomla/example.com-files-$(date +%F).tar.gz --exclude='example.com/configuration.php' --exclude='example.com/cache/*' --exclude='example.com/administrator/cache/*' --exclude='example.com/tmp/*' --exclude='example.com/administrator/logs/*' -C /var/www example.com-C /var/www example.comstores paths asexample.com/..., so the archive extracts anywhere.- A
/*pattern drops a folder's contents but keeps the folder itself, which Joomla needs. - Exclude patterns match the stored names and must come before the directory.
tar run as root records owners and permissions. The tar guide explains excludes and how to test an archive.
Joomla 5 and 6 can serve the site from a separate public folder made by site:create-public-folder, holding an index.php, a defines.php that points back at the site root, and symlinks to media, images and files. Back that folder up too, or re-create it after a restore with php cli/joomla.php site:create-public-folder --public-folder=/var/www/example.com-public, which also fixes the root path if it changed.
Keep configuration.php and $secret
$secret is a random string the installer writes into configuration.php. Joomla encrypts every user's multi-factor authentication settings with it, authenticator keys and backup codes included, and checks Web Services API tokens against an HMAC made with it. Under a different $secret, users with multi-factor authentication cannot finish signing in and every API token is rejected. Passwords do not depend on it.
tar -czf /var/backups/joomla/example.com-config-$(date +%F).tar.gz -C /var/www example.com/configuration.phpconfiguration.php holds the database password and the key to the multi-factor secrets in the dump. Store its archive apart from the database dumps, encrypt it, and archive it again whenever Global Configuration changes.
Offline mode during a backup
A dump with --single-transaction does not need the site offline. Take it offline for the final backup before a move, so nothing changes between the last dump and the switch:
sudo -u www-data php /var/www/example.com/cli/joomla.php site:downsite:down sets $offline = true in configuration.php and site:up sets it back; visitors see the offline page in between. Because the flag lives in configuration.php, not the database, a copy archived while offline restores an offline site. Run the CLI as the user that owns configuration.php, since it rewrites the file.
Restore on a new server
Joomla 6 needs PHP 8.3 or later and MySQL 8.0.13, MariaDB 10.4 or PostgreSQL 12 at least; Joomla 5's CLI accepts PHP 8.1. Create an empty database and user:
CREATE DATABASE joomla CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'joomla'@'localhost' IDENTIFIED BY 'your-password-here';
GRANT ALL PRIVILEGES ON joomla.* TO 'joomla'@'localhost';tar -xzf /var/backups/joomla/example.com-files-2026-10-04.tar.gz -C /var/wwwtar -xzf /var/backups/joomla/example.com-config-2026-10-04.tar.gz -C /var/wwwEdit configuration.php for this server. These lines usually change. Keep $secret and $dbprefix exactly as they were:
public $host = 'localhost';
public $user = 'joomla';
public $password = 'your-password-here';
public $db = 'joomla';
public $live_site = '';
public $tmp_path = '/var/www/example.com/tmp';
public $log_path = '/var/www/example.com/administrator/logs';$tmp_pathand$log_pathare absolute paths written at install. Point them at this server's folders.$live_siteis normally empty. When set, Joomla builds every URL from it, so an old value sends links and stylesheets to the old address.$force_sslis0for none,1for the administrator,2for both. Use0until the new server has a certificate.- If
$session_handleror$cache_handlerisredisormemcached, install it or switch todatabaseandfile. Fix$cookie_domainif it names the old domain.
Then load the dump and give the files to the PHP user:
gunzip -c /var/backups/joomla/example.com-db-2026-10-04.sql.gz | mysql --defaults-extra-file=/etc/mysql/joomla-backup.cnf joomlachown -R www-data:www-data /var/www/example.comOn PostgreSQL, create the role and database, then run pg_restore --no-owner -h localhost -U joomla -d joomla with the .dump file; restoring a PostgreSQL dump covers the details. Finally clear the cache, and run site:up if the site came back offline:
sudo -u www-data php /var/www/example.com/cli/joomla.php cache:cleanVerify the restore
On Joomla 5.2 and later, compare the database schema with what the installed code expects:
sudo -u www-data php /var/www/example.com/cli/joomla.php maintenance:databaseIt lists each extension and ends with All database table structures are up to date. or, when something differs, There are tables not up to date!. Since the files and database come from the same night, there should be nothing to fix. Then count articles (with your table prefix) and compare with the live site, and fetch the home page, expecting 200:
mysql --defaults-extra-file=/etc/mysql/joomla-backup.cnf joomla -e "SELECT COUNT(*) FROM abc12_content"curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/In the administrator, System > Information > System Information > Folder Permissions shows whether the temp and log folders are writable, and System > Information > Warnings flags a wrong $tmp_path. Sign in as a user with multi-factor authentication and open a page with images. Testing restores explains how to make this routine.
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
SITE_DIR="/var/www"
SITE="example.com"
DB_NAME="joomla"
BACKUP_DIR="/var/backups/joomla"
KEEP_DAYS=14
STAMP="$(date +%Y-%m-%d_%H%M)"
DB="$BACKUP_DIR/$SITE-db-$STAMP.sql.gz"
FILES="$BACKUP_DIR/$SITE-files-$STAMP.tar.gz"
mkdir -p "$BACKUP_DIR"
trap 'rm -f "$DB.partial" "$FILES.partial"' EXIT
mysqldump --defaults-extra-file=/etc/mysql/joomla-backup.cnf \
--single-transaction --no-tablespaces "$DB_NAME" | gzip > "$DB.partial"
gunzip -c "$DB.partial" | tail -n 1 | grep -q "Dump completed"
mv "$DB.partial" "$DB"
tar -czf "$FILES.partial" \
--exclude="$SITE/configuration.php" \
--exclude="$SITE/cache/*" --exclude="$SITE/administrator/cache/*" \
--exclude="$SITE/tmp/*" --exclude="$SITE/administrator/logs/*" \
-C "$SITE_DIR" "$SITE" || [ $? -eq 1 ]
mv "$FILES.partial" "$FILES"
find "$BACKUP_DIR" -type f \( -name "$SITE-db-*" -o -name "$SITE-files-*" \) -mtime +"$KEEP_DAYS" -deletechmod 755 /usr/local/bin/joomla-backup.shPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
25 3 * * * root flock -n /run/lock/joomla-backup.lock /usr/local/bin/joomla-backup.sh >> /var/log/joomla-backup.log 2>&1-mtime +14 keeps about two weeks of nightly backups and leaves the configuration archives alone, and flock -n stops two runs overlapping. Copy the backups off the server each night, for example with rclone to Backblaze B2; the cron guide covers logging and alerts.
Common errors
| Error | Fix |
|---|---|
Install Joomla to run cli commands | configuration.php is missing or empty. Restore it before running the CLI. |
Sorry, your PHP version is not supported. | The php on your PATH is older than the CLI needs: 8.1 for Joomla 5, 8.3 for Joomla 6. Call the right binary, such as php8.3. |
Could not connect to database: followed by the server's reason | $host, $user, $password or $db is wrong for this server, or the user has no grant on the database. |
An error that names a Joomla table and ends in doesn't exist | $dbprefix does not match the tables in the dump, or the import did not run. |
The Joomla temporary folder is not writable or does not exist. | $tmp_path still points at the old server's path. Extension installs and updates fail until it is fixed. |
The log folder is not writable: followed by a path | $log_path is wrong or the folder is missing. Joomla may not load at all. |
| Links and stylesheets point at the old domain | Empty $live_site or set it to the new address, then run cache:clean. |
| Users with multi-factor authentication cannot sign in | configuration.php is not the original, so $secret changed. Restore the original; otherwise delete their rows from the #__user_mfa table (with your prefix) so they can set it up again. |
Access denied; you need (at least one of) the PROCESS privilege(s) for this operation | Add --no-tablespaces to mysqldump. |
Moving the site to another provider? See moving a server to a new provider.
Frequently asked questions
- Does Joomla have a built-in backup?
- No. Joomla core has no backup feature. The CLI's database:export writes one XML file per table in Joomla's own format, table by table without a transaction, and covers no files. Use mysqldump or pg_dump and tar.
- Which Joomla folders do I need to back up?
- The whole site directory, because extensions install into the core folders, minus the contents of cache, administrator/cache and tmp. Back up configuration.php separately and the database with mysqldump or pg_dump.
- What happens if I lose configuration.php?
- Rebuild it from configuration.php-dist in the Joomla package with the old database name, user and $dbprefix. A new $secret makes multi-factor authentication settings and API tokens unusable, so users must set those up again.
- Do I need to take Joomla offline to back it up?
- Not with mysqldump --single-transaction, which reads InnoDB tables from one snapshot while the site runs. Use php cli/joomla.php site:down for the final backup before moving servers, and site:up afterwards.
How this was checked
Commands, limits and prices were checked against these official pages, on October 4, 2026:
- Joomla Programmers Documentation: Technical Requirements
- Joomla Programmers Documentation: Install Process and Script Files
- Joomla 6.1.4 and 5.4.9 releases
- Joomla 6.1.4 source: configuration.php-dist
- Joomla 6.1.4 source: installer ConfigurationModel ($secret, $tmp_path, $log_path)
- Joomla 6.1.4 source: CLI commands (site:down, site:up, cache:clean, config:get, maintenance:database, site:create-public-folder)
- Joomla 5.4.9 source: CLI commands
- Joomla 6.1.4 source: cli/joomla.php (PHP version and installation checks)
- Joomla Framework Database 4.0.0 source: database:export and database:import
- Joomla Framework Database 4.0.0 source: MysqliDriver (connection errors)
- Joomla 6.1.4 source: multi-factor authentication settings encrypted with $secret
- Joomla 6.1.4 source: API token authentication (HMAC with $secret)
- Joomla 6.1.4 source: Global Configuration save (temp and log folder checks)
- Joomla 6.1.4 language strings: com_config.ini and com_installer.ini
- Joomla 6.1.4 source: Uri::base() and $live_site
- Joomla 6.1.4 source: public folder generator
- Joomla 6.1.4 source: base.sql (InnoDB tables, #__user_mfa)
- Joomla 6.1.4 source: System dashboard menu (Warnings, System Information, Database)
- Symfony Console 7.4 source: ArgvInput (unknown option error)
- MySQL 8.4 Reference Manual: mysqldump
- MySQL 8.4 Error Message Reference: server errors
- PostgreSQL documentation: pg_dump
- PostgreSQL documentation: pg_restore