py-wordpress-backup
A Python package for backing up and restoring a self-hosted WordPress installation, including the database and uploaded content.
The backup file will contain:
- Your database as a MySQL dump.
- An exact copy of your entire WordPress directory.
The restore process will:
- Extract the files into the correct location
- Update
wp-config.phpwith new settings (if needed) - Create the database if it doesn't exist
- Add the user and grant it access if it doesn't exist
- Restore the database
Credit
Credit for the original version of this goes to Cariad Eccleston.
Installation
pip install l3io-wordpress-backup
That is everything a backup and a restore need: l3io-wordpress-database, l3io-wordpress-config and the l3io-age-passphrase binder are required dependencies, and every dependency installs from PyPI; no extra index is needed. The optional extras add:
aws— AWS Secrets Manager credentials (boto3):pip install 'l3io-wordpress-backup[aws]';otel— the OpenTelemetry SDK and a JSON-file exporter, for tracing.
pip install 'l3io-wordpress-backup[database]', the install earlier releases documented, still works: database is now an empty extra that adds nothing. A feature used without its extra (aws) stops with exit 7 (NOT_READY) naming the extra to install. An installation missing a required dependency (installed with --no-deps, or pruned afterwards) stops with exit 7 too, saying the installation is incomplete; reinstall l3io-wordpress-backup.
Requirements: Python 3.11 or newer. Extracting an archive also needs a Python with the June 2025 tarfile security fixes (3.11.13+, 3.12.11+, 3.13.4+; CVE-2025-4517); on an older one extraction is refused — upgrade Python.
Upgrading from 0.2.21: that release can no longer be installed from PyPI, because two of its dependencies (wpconfigr, chesney) were removed from there. You do not need it: this package reads 0.2.21 Archives itself (wpbackup2 verify --archive <file> reports them as STRUCTURAL_ONLY, and restore restores them). The restore side is tested with Archives in 0.2.21's layout built by the test suite and by extracting the committed 0.2.21-layout fixtures; no test restores an Archive written by a 0.2.21 install.
External tools
A Run checks every external tool its plan needs before its first effect, and stops with exit 7 (NOT_READY) naming the tool, the step that needed it and what was found. The table lists the tools this package runs itself and the minimum a plan in this build asks for:
| Tool | Minimum | Purpose | Needed for | Install |
|---|---|---|---|---|
age |
1.3 | encrypts the Archive under a passphrase (CLI passphrase backend) | the encrypted payload (ENCRYPT, VERIFY) | https://github.com/FiloSottile/age#installation |
age-plugin-batchpass |
— | supplies the passphrase to age non-interactively (CLI backend) | the encrypted payload (ENCRYPT, VERIFY) | https://github.com/FiloSottile/age/releases |
wp |
2.12.0 | WP-CLI, which relocation runs to rewrite the Site's URLs | SEARCH_REPLACE: serialization-safe content URL rewrite for --new-site-url/--new-site-home-url (FR-12) | https://wp-cli.org/#installing |
| MySQL/MariaDB client tools | client 8.4+, MariaDB 11.4+ | the database dump and load | checked per Site by l3io-wordpress-database (check_dump/check_load), never run by this package |
your distribution's MySQL or MariaDB client package |
The l3io-age-passphrase binder, a required dependency, is published as wheels for Linux (glibc and musl; x86_64, aarch64, armv7l), macOS (x86_64, arm64) and Windows (amd64). On any other platform the installer builds it from its source distribution, which needs a Rust toolchain; if that build fails, the package does not install. When the binder is not importable, the passphrase backend falls back to the age command line (L3IO_AGE_PASSPHRASE_BACKEND=auto, the default), so age 1.3 or newer and age-plugin-batchpass must be on PATH, or an encrypted Run stops with exit 7 (NOT_READY) before its first effect. L3IO_AGE_PASSPHRASE_BACKEND=cli selects that backend explicitly.
Usage
Quick start: unattended, with no flags
Every option is also an L3IO_WP_BACKUP_<NAME> variable (--wp-dir is
L3IO_WP_BACKUP_WP_DIR) and a key of a TOML config file (wp_dir = "..."),
so a scheduled job needs no flags. A flag wins over a variable, a variable over
the file, the file over the default. The file is --config PATH or
L3IO_WP_BACKUP_CONFIG, else /etc/l3io-wp-backup/config.toml and then
~/.config/l3io-wp-backup/config.toml when present; one holding a secret
(passphrase, db_password, admin_password, new_db_password) must be
readable by its owner only (chmod 600), or it is refused. ADR-0028 records
the design.
As a Docker sidecar next to the official wordpress image, sharing its
site volume and its WORDPRESS_DB_* environment:
services:
backup:
image: <your wpbackup2 image>
env_file: wordpress.env # the site's WORDPRESS_DB_*
environment:
L3IO_WP_BACKUP_WP_DIR: /var/www/html
L3IO_WP_BACKUP_ARCHIVE_DIR: /backups
volumes:
- html:/var/www/html
- backups:/backups
then docker compose exec backup wpbackup2 backup on a schedule, and to
restore the newest backup of this site:
docker compose exec backup wpbackup2 restore --archive latest
Set L3IO_WP_BACKUP_PASSPHRASE (and add --encrypt-with-passphrase, or
encrypt_with_passphrase = true in the config file) to encrypt; a restore
then decrypts with the same passphrase. Started as root, a backup or a restore
runs as the owner of /var/www/html (www-data in the official image), after
reading the passphrase and credentials, so Archives and restored files belong
to it and both share its state directory; /backups must then be writable by
that user, or the backup is refused before it starts. tests/sidecar/ is a
working example.
From cron, with the tool installed by pip:
# ~/.config/l3io-wp-backup/config.toml (chmod 600)
wp_dir = "/var/www/example"
archive_dir = "/var/backups/example"
encrypt_with_passphrase = true
passphrase = "a long passphrase, 12 characters or more"
log_format = "console"
15 3 * * * wpbackup2 backup
Without --archive or archive_dir, the Archive goes into the current
directory, or into the per-user data directory
(~/.local/share/l3io-wp-backup/archives) when the current directory is
inside the site, never inside the Site Root. A target you put inside the Site
Root is written with a warning: the web server may serve it. A backup started
as root runs as the Site Root's owner: that user's data directory, and an
Archive directory that user can write (otherwise the backup is refused before
it starts, naming the directory).
By hand
To backup:
wpbackup2 backup --wp-dir /www/wordpress --archive ~/backup.tar.gz
To rehearse a backup first, add --whatIf (before or after backup). The Dry Run resolves the credentials, runs the preflight and asks the database whether it can be dumped, all for real; then it prints the ordered plan and writes nothing. Its last line says so. It does not detect what only writing finds: a full disk, a file that cannot be read, or an Archive larger than the Manifest cap. A Site Lock is taken as for a real Run, so a Dry Run waits for nobody and is refused (exit 9) while another Run of the same Site by the same OS user holds the lock.
Output for cron mail. stderr is the log stream. On a terminal it is human-readable; anywhere else (cron, a pipe, journald) it is strict JSON lines, one object per line, so a log collector can parse every line. Where a person reads the output instead — a cron job that mails it — pass --log-format console before the subcommand to get human-readable lines there too, failures and warnings included:
wpbackup2 --log-format console backup --wp-dir /www/wordpress --archive ~/backup.tar.gz
To check an archive without touching a site:
wpbackup2 verify --archive ~/backup.tar.gz
Without a key, verify proves the archive's checksums are consistent with its manifest, not that the archive is authentic: the manifest is unprotected, so anyone who can rewrite the archive can rewrite its checksums too. It says so (authenticity not checked (no key)). For an encrypted archive, --authenticate decrypts the whole payload with its passphrase — taken from L3IO_WP_BACKUP_PASSPHRASE or a terminal prompt, never from the command line — and age authenticates every chunk. A wrong passphrase or a tampered payload exits 8; a plain archive is refused, since there is nothing to authenticate. Encryption protects file contents only. The manifest stays readable in clear in an encrypted archive: the site path, the database name, every file path and size, and the exclusions are visible to anyone who can read the archive, without the passphrase. To refuse a plain archive where an encrypted one is expected (for example one substituted by someone who can write the storage target), restore with --require-encrypted; without it a plain archive is restored with a warning, since plain archives are not authenticated.
Memory when reading an archive. verify and restore read the whole manifest into memory and parse it before anything is authenticated, taking about 4.3 times its size. A manifest is capped at 256 MiB, so the worst case is about 1.1 GB; an ordinary site's manifest is a few MB. A streaming manifest reader is planned after v1.
To restore (see docs/restore.md for every mode, the refusals and their exit codes):
wpbackup2 restore --wp-dir /www/wordpress --archive ~/backup.tar.gz --mode all
A restore takes a Safety Snapshot of whatever it would change first. A database restore then loads the Dump straight into the live database, after dropping every table that carries the site's table prefix (tables without it are never touched), and reloads the Safety Snapshot automatically if the load fails; see docs/restore.md. Over a Site Root that is not empty and does not look like WordPress (none of wp-config.php, wp-includes/, wp-content/), or a database with tables none of which carries the site's prefix, it refuses unless --force is given (exit 3, naming the flag). --archive latest restores the newest Archive of this site in --archive-dir (see docs/restore.md). A target with no wp-config.php takes its database from an encrypted archive's config as it says, and from an unencrypted one only when you confirm it with --archive-database USER@HOST[:PORT]/NAME. Database connections require no TLS unless you ask with --db-tls {required,verify-ca,verify-identity} (and --db-tls-ca FILE). Started as root, a restore (like a backup) runs as the Site Root's owner. The library (api.backup, api.restore) refuses to run as root: it never switches its host's user.
wp-config.php is read for your database credentials. It is looked for in the WordPress directory, then its parent; pass --wp-config PATH to use another.
Credentials
No credential has to be on the command line, so a cron line needs none. Each account's credentials come from the first source that has them, in a fixed order:
| Account | 1. Explicit | 2. Environment | 3. wp-config.php |
|---|---|---|---|
| WordPress database (backup) | --db-user/--db-password, their config-file keys, or library BackupRequest.credentials |
L3IO_WP_BACKUP_DB_USER and L3IO_WP_BACKUP_DB_PASSWORD |
DB_USER/DB_PASSWORD |
| WordPress database (restore) | none | none | DB_USER/DB_PASSWORD, the account the restored site will use |
| Administrator (restore) | --admin-user (or --admin-username)/--admin-password, their config-file keys, or library RestoreRequest.admin |
L3IO_WP_BACKUP_ADMIN_USER and L3IO_WP_BACKUP_ADMIN_PASSWORD |
never |
- Both variables of a pair, or neither. Setting one without its partner is an error (exit 4), not a fall-through to
wp-config.php. A variable set to an empty value counts as set. The one exception is--admin-usernamewithout--admin-password: the password then comes fromL3IO_WP_BACKUP_ADMIN_PASSWORD, and an empty value there counts as unset. The administrator pair is read only by a restore that needs an administrator, so--mode filesnever asks for one, and neither does a database restore whose WordPress account can load its own database (owner decision 55): an administrator is needed only when the database does not exist yet or the account cannot log in or lacks a right on it. - A source that fails stops the search. An unreadable AWS secret, for example, never becomes the next source's account. The administrator is never taken from
wp-config.phpor from the WordPress account. A restore that needs one and has none is refused before anything changes (exit 4), naming the database, the account and what is missing. - AWS Secrets Manager. A secret holding the JSON
{"username": ..., "password": ...}can supply either account as its explicit source. Install theawsextra:pip install 'l3io-wordpress-backup[aws]'. Without it, exit 7 names the extra. Without a region from the request or the AWS session, exit 4 names the region flag. In this release only the library selects it:AwsSecretCredentialSpec(secret_id, region)asBackupRequest.credentialsorRestoreRequest.admin. The--db-credentials-aws-*and--admin-credentials-aws-*flags are accepted but not implemented yet. getenv_docker()andgetenv()inwp-config.php. The officialwordpressDocker image definesDB_HOST,DB_NAME,DB_USER,DB_PASSWORDand$table_prefixasgetenv_docker('WORDPRESS_DB_HOST', 'mysql')and so on. Backup,doctorand restore resolve them as PHP would, from their own environment: the variable, a Docker secret named by<NAME>_FILE, else the default. Run the tool with the site'sWORDPRESS_DB_*(a sidecar container can share the WordPress container's environment file); the resolved host is logged (database_host) and named in the Dry Run. Any other PHP expression in those settings is refused (exit 3) without being evaluated. A database restore loads straight into the database the environment names and leaveswp-config.phpas it is (owner decision 49); only a relocation flag (--new-db-*) that would rewrite such a constant is refused (exit 3).--overwrite-wp-configkeeps those definitions as written, and is refused (exit 3) when the Archive'swp-config.phpdoes not definegetenv_docker(): see docs/restore.md.- The log names the source, never the value. Each resolution logs
credential_source_selectedwith the source's label, for exampleenvironment:L3IO_WP_BACKUP_DB_USER,L3IO_WP_BACKUP_DB_PASSWORD.
The environment of a process can be read by the same user and by root (/proc/<pid>/environ), and a secret flag (--passphrase, --db-password, --admin-password, --new-db-password) is visible to every user in ps and logs WARNING password_on_command_line. On a shared host, prefer wp-config.php, a chmod 600 config file or AWS Secrets Manager. A database password, or a passphrase that decrypts (verify --authenticate, restore), shorter than 4 characters is accepted with WARNING credential_below_redaction_floor: it is too short to be masked in logs. A passphrase that encrypts needs 12 characters or more.
Command-line reference
Generated from the parser by tools/gen_cli_docs.py; CI fails if it drifts.
$ wpbackup2 --help
usage: wpbackup2 [-h] [--version] [--config PATH]
[--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--log-format {console,json}]
[--whatIf]
{backup,restore,verify,doctor} ...
Backup/restore your WordPress content and database.
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
positional arguments:
{backup,restore,verify,doctor}
Action being requested
backup Perform a WordPress backup
restore Perform a WordPress restore
verify Check an archive without touching a site
doctor Report whether this host is ready to back up and restore
options:
-h, --help Show this help message and exit
--version Show the program's version number and exit
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
backup/config.toml). A file holding a secret must be readable by its owner
only
--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
Logging level (stdlib level names; default INFO)
--log-format {console,json}
Log format on stderr (default: console on a terminal, else json)
--whatIf, --what-if Dry Run: resolve credentials, run preflight and every query for real, and
print the ordered plan; changes no Site, database or archive, but takes
the Site Lock (exit 9 LOCKED while another Run holds it)
exit codes:
0 OK the Run succeeded
1 FAILED the Run failed for a reason no other code names
2 USAGE the command line was invalid
3 REFUSED a check found a reason not to act; nothing was done
4 CREDENTIALS credentials were missing, unusable or rejected
5 UNREACHABLE the database server could not be reached
7 NOT_READY a required tool or optional extra is not available
8 VERIFICATION_FAILED the archive failed verification
9 LOCKED another Run holds the site lock
$ wpbackup2 backup --help
usage: wpbackup2 backup [-h] [--config PATH] [--wp-dir WP_DIR] [--archive-dir DIR] [--whatIf]
[--archive ARCHIVE] [--wp-config PATH] [--mode {files,db,all}]
[--encrypt-with-passphrase] [--passphrase SECRET] [--exclude PATTERN]
[--no-default-excludes] [--list-default-excludes]
[--db-credentials-aws-secret-id ID] [--db-credentials-aws-region REGION]
[--db-user USER] [--db-password SECRET]
[--db-tls {required,verify-ca,verify-identity}] [--db-tls-ca FILE]
Perform a WordPress backup
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
options:
-h, --help Show this help message and exit
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
backup/config.toml). A file holding a secret must be readable by its owner
only
--wp-dir WP_DIR Path to the root of the WordPress directory (required, except with --list-
default-excludes)
--archive-dir DIR Directory the archive is written to when --archive is not given (default:
the current directory, or the per-user data directory when the current
directory is inside the Site Root)
--whatIf, --what-if Dry Run: run every check and query for real and print the ordered plan;
writes no archive but takes the Site Lock (also accepted before `backup`)
--archive ARCHIVE Archive path or file: URL (default: wpbackup2-<db>-<UTC timestamp>.tar.gz
in --archive-dir). Inside the Site Root it is written with a warning: the
web server may serve it
--wp-config PATH Use this wp-config.php instead of searching for it
--mode {files,db,all}
What to back up: files, db, or all (default: all)
--encrypt-with-passphrase
Encrypt the archive with a passphrase (--passphrase,
L3IO_WP_BACKUP_PASSPHRASE, the config file or a prompt)
--passphrase SECRET Passphrase that encrypts the archive (with --encrypt-with-passphrase). The
value is visible in the process list; prefer L3IO_WP_BACKUP_PASSPHRASE or
the config file (chmod 600)
--exclude PATTERN Leave matching paths out of the archive (repeatable)
--no-default-excludes
Do not apply the default exclusion set
--list-default-excludes
Print the default exclusion set and exit without backing up (needs no
--wp-dir; takes no other backup flag)
database credentials:
--db-credentials-aws-secret-id ID
Read the WordPress database account from this AWS Secrets Manager secret
--db-credentials-aws-region REGION
AWS region of --db-credentials-aws-secret-id (needs the secret id)
--db-user USER WordPress database account to dump as, instead of wp-config.php's (give
--db-password with it)
--db-password SECRET Password of --db-user. The value is visible in the process list; prefer
L3IO_WP_BACKUP_DB_PASSWORD or the config file (chmod 600)
--db-tls {required,verify-ca,verify-identity}
Require TLS on every database connection, at this level (default: no
requirement, as WordPress connects)
--db-tls-ca FILE CA bundle that verifies the database server, with --db-tls verify-ca or
verify-identity (default: the system store)
exit codes:
0 OK the Run succeeded
1 FAILED the Run failed for a reason no other code names
2 USAGE the command line was invalid
3 REFUSED a check found a reason not to act; nothing was done
4 CREDENTIALS credentials were missing, unusable or rejected
5 UNREACHABLE the database server could not be reached
7 NOT_READY a required tool or optional extra is not available
8 VERIFICATION_FAILED the archive failed verification
9 LOCKED another Run holds the site lock
$ wpbackup2 restore --help
usage: wpbackup2 restore [-h] [--config PATH] --wp-dir WP_DIR --archive ARCHIVE
[--archive-dir DIR] [--mode {files,db,all}] [--cleanfirst]
[--drop-database] [--force] [--whatIf] [--wp-config PATH]
[--overwrite-wp-config] [--reset-existing-db-user] [--no-safety-snapshot]
[--require-encrypted] [--passphrase SECRET] [--snapshot-dir PATH]
[--archive-database USER@HOST[:PORT]/NAME]
[--db-tls {required,verify-ca,verify-identity}] [--db-tls-ca FILE]
[--admin-tls {required,verify-ca,verify-identity}] [--admin-tls-ca FILE]
[--resolve-interrupted-run RUN_ID] [--admin-user USER]
[--admin-password SECRET] [--admin-credentials-aws-secret-id ID]
[--admin-credentials-aws-region REGION] [--only-appointed-in-asg]
[--new-site-url URL] [--new-site-home-url URL] [--new-db-host HOST]
[--new-db-name NAME] [--new-db-user USER] [--new-db-password SECRET]
[--new-db-password-file PATH] [--allow-unencrypted]
[--db-credentials-aws-secret-id ID] [--db-credentials-aws-region REGION]
Perform a WordPress restore
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
options:
-h, --help Show this help message and exit
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
backup/config.toml). A file holding a secret must be readable by its owner
only
--wp-dir WP_DIR Path to the root of the WordPress directory
--archive ARCHIVE Path or file: URL of the archive to restore from, or `latest`: the newest
archive of this site in --archive-dir (required, --resolve-interrupted-run
included)
--archive-dir DIR Directory --archive latest searches (default: the current directory, or
the per-user data directory when the current directory is inside the Site
Root)
restore mode:
Together these select one Restore Mode; any other combination is refused.
--mode {files,db,all}
What to restore: files, db, or all (default: all)
--cleanfirst Clear the Site Root before restoring the files. The database is replaced
by every restore that loads it
--drop-database Deprecated; has no effect. A database restore loads into the live database
and drops the tables with the site's prefix first, so no previous database
is left to remove
--force Restore over a non-empty Site Root that does not look like WordPress (none
of wp-config.php, wp-includes/, wp-content/), or a non-empty database none
of whose tables has the site's table prefix. It selects no mode and
removes nothing
restore options:
The flags that do not select the Restore Mode: Dry Run, wp-config.php, the Safety Snapshot, the database administrator and relocation
--whatIf, --what-if Dry Run: run every check and query for real and print the ordered plan;
changes no Site or database but takes the Site Lock (also accepted before
`restore`)
--wp-config PATH Use this wp-config.php instead of searching for it
--overwrite-wp-config
Replace the target's existing wp-config.php with the archive's (only with
a mode that restores files), keeping the target's DB_* definitions as
written, getenv_docker(...) included. Refused (exit 3) when the target
uses getenv_docker(...) and the archive's wp-config.php does not define it
--reset-existing-db-user
When the target has no wp-config.php and the archive's database account
already exists: reset its password to the archive's (refused otherwise)
--no-safety-snapshot Do not take the Safety Snapshot before changing anything
--require-encrypted Refuse an archive that is not encrypted, before anything changes (guards
against a plain archive substituted for an encrypted one; without it a
plain archive is restored with a warning)
--passphrase SECRET Passphrase that decrypts the archive and encrypts the Safety Snapshot. The
value is visible in the process list; prefer L3IO_WP_BACKUP_PASSPHRASE or
the config file (chmod 600)
--snapshot-dir PATH Directory to write the Safety Snapshot to (default: the per-user state
directory). Refused inside the Site Root or the wp-config.php directory; a
directory a web server serves from elsewhere is not detected
--archive-database USER@HOST[:PORT]/NAME
When the target has no wp-config.php and the archive is not encrypted:
confirm the database the archive's config names (user, host, port and name
must all match), or the restore is refused
--db-tls {required,verify-ca,verify-identity}
Require TLS on every database connection, at this level (default: no
requirement, as WordPress connects)
--db-tls-ca FILE CA bundle that verifies the database server, with --db-tls verify-ca or
verify-identity (default: the system store)
--admin-tls {required,verify-ca,verify-identity}
Deprecated: use --db-tls
--admin-tls-ca FILE Deprecated: use --db-tls-ca
--resolve-interrupted-run RUN_ID
Resolve the journal left by the interrupted Run with this ID, then stop:
nothing is restored (run the restore again afterwards)
--admin-user, --admin-username USER
Database administrator username (give --admin-password with it, or set
L3IO_WP_BACKUP_ADMIN_PASSWORD)
--admin-password SECRET
Database administrator password. The value is visible in the process list;
prefer L3IO_WP_BACKUP_ADMIN_PASSWORD or the config file (chmod 600)
--admin-credentials-aws-secret-id ID
Read the database administrator from this AWS Secrets Manager secret
--admin-credentials-aws-region REGION
AWS region of --admin-credentials-aws-secret-id (needs the secret id)
--only-appointed-in-asg
Removed; accepted only to say so (not available in this build)
--new-site-url URL Relocate the Site to this URL
--new-site-home-url URL
Relocate the Site's home URL
--new-db-host HOST Relocate to this database host
--new-db-name NAME Relocate to this database name; it must be missing (created, and dropped
again if the restore fails) or empty, --force or not
--new-db-user USER Relocate to this database user
--new-db-password SECRET
Password of --new-db-user. The value is visible in the process list;
prefer --new-db-password-file, L3IO_WP_BACKUP_NEW_DB_PASSWORD or the
config file (chmod 600)
--new-db-password-file PATH
Read the password of --new-db-user from this file (its first line; make it
readable only by you)
--allow-unencrypted Deprecated, no effect: a plain archive restored with a passphrase proceeds
with a warning; --require-encrypted refuses it
database credentials:
--db-credentials-aws-secret-id ID
Read the WordPress database account from this AWS Secrets Manager secret
--db-credentials-aws-region REGION
AWS region of --db-credentials-aws-secret-id (needs the secret id)
exit codes:
0 OK the Run succeeded
1 FAILED the Run failed for a reason no other code names
2 USAGE the command line was invalid
3 REFUSED a check found a reason not to act; nothing was done
4 CREDENTIALS credentials were missing, unusable or rejected
5 UNREACHABLE the database server could not be reached
7 NOT_READY a required tool or optional extra is not available
8 VERIFICATION_FAILED the archive failed verification
9 LOCKED another Run holds the site lock
$ wpbackup2 verify --help
usage: wpbackup2 verify [-h] [--config PATH] [--whatIf] --archive ARCHIVE [--authenticate]
[--passphrase SECRET]
Check an archive without touching a site
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
options:
-h, --help Show this help message and exit
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
backup/config.toml). A file holding a secret must be readable by its owner
only
--whatIf, --what-if Accepted for symmetry: verify is read-only, so it changes nothing
--archive ARCHIVE Path or file: URL of the archive to check
--authenticate Decrypt the encrypted payload to prove the archive authentic; without it,
verify proves only that its checksums are consistent. The passphrase comes
from --passphrase, L3IO_WP_BACKUP_PASSPHRASE, the config file or a prompt.
It does not show the archive is the latest one: an older archive under the
same passphrase also passes
--passphrase SECRET Passphrase that decrypts the archive (with --authenticate). The value is
visible in the process list; prefer L3IO_WP_BACKUP_PASSPHRASE or the config
file (chmod 600)
exit codes:
0 OK the Run succeeded
1 FAILED the Run failed for a reason no other code names
2 USAGE the command line was invalid
3 REFUSED a check found a reason not to act; nothing was done
4 CREDENTIALS credentials were missing, unusable or rejected
5 UNREACHABLE the database server could not be reached
7 NOT_READY a required tool or optional extra is not available
8 VERIFICATION_FAILED the archive failed verification
9 LOCKED another Run holds the site lock
$ wpbackup2 doctor --help
usage: wpbackup2 doctor [-h] [--config PATH] [--wp-dir DIR] [--archive TARGET] [--json]
Report whether this host is ready to back up and restore
Every option can also be set as an L3IO_WP_BACKUP_<NAME> variable
(--wp-dir is L3IO_WP_BACKUP_WP_DIR) or as a key in a TOML config file
(wp_dir = "..."); a flag wins over a variable, a variable over the file.
options:
-h, --help Show this help message and exit
--config PATH Read settings from this TOML file (also L3IO_WP_BACKUP_CONFIG; default:
/etc/l3io-wp-backup/config.toml, then the per-user ~/.config/l3io-wp-
backup/config.toml). A file holding a secret must be readable by its owner
only
--wp-dir DIR Also check this Site Root
--archive TARGET Also check this archive target
--json Print the diagnosis as JSON
exit codes:
0 OK the Run succeeded
1 FAILED the Run failed for a reason no other code names
2 USAGE the command line was invalid
3 REFUSED a check found a reason not to act; nothing was done
4 CREDENTIALS credentials were missing, unusable or rejected
5 UNREACHABLE the database server could not be reached
7 NOT_READY a required tool or optional extra is not available
8 VERIFICATION_FAILED the archive failed verification
9 LOCKED another Run holds the site lock
Exit codes
A scheduler can branch on the exit code alone: 0 means the Run succeeded and
every failure exits non-zero with a code naming its kind. The table is
append-only — a code is never renumbered or reused. Ctrl-C exits with the
shell's 130 (INTERRUPTED in the log), which is not part of the table.
| Code | Name | Meaning |
|---|---|---|
| 0 | OK |
the Run succeeded |
| 1 | FAILED |
the Run failed for a reason no other code names |
| 2 | USAGE |
the command line was invalid |
| 3 | REFUSED |
a check found a reason not to act; nothing was done |
| 4 | CREDENTIALS |
credentials were missing, unusable or rejected |
| 5 | UNREACHABLE |
the database server could not be reached |
| 7 | NOT_READY |
a required tool or optional extra is not available |
| 8 | VERIFICATION_FAILED |
the archive failed verification |
| 9 | LOCKED |
another Run holds the site lock |
Reserved for the l3io-wp-* family and never used: 6.
Development
Prerequisites
Python 3.11 or newer, and uv.
Installing dependencies
uv sync
Metadata
Release files for l3io-wordpress-backup 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| l3io_wordpress_backup-1.0.0.tar.gz | 1.1 MB | With provenance | Details for l3io_wordpress_backup-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| l3io_wordpress_backup-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for l3io_wordpress_backup-1.0.0-py3-none-any.whl |
Total release size: 1.5 MB
Release files / l3io_wordpress_backup-1.0.0.tar.gz
| Download URL | l3io_wordpress_backup-1.0.0.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c389e8f3c4a74d25473a4f302e620092751019efd9e4865be74912a9a41e4a1a
|
|
BLAKE2b-256 checksum How to use checksums |
17f8f4eb7934070268da2b3e390e06bbb8f4df549ccfc52408497b291bd3c66f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logRelease files / l3io_wordpress_backup-1.0.0-py3-none-any.whl
| Download URL | l3io_wordpress_backup-1.0.0-py3-none-any.whl |
|---|---|
| Size | 373.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4416e64bde063eaa3348c526491dde724bc1a7e20cb1fbe9343a296922fdabd5
|
|
BLAKE2b-256 checksum How to use checksums |
f2b441b8e4c78786f378ce4a2cc3f0ee303f7ac22150ab7dc066eab5fb6c3ba4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logSecurity for l3io-wordpress-backup 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for l3io_wordpress_backup-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.