Working Features:
- PostgreSQL full backup using
pg_basebackupwith WAL streaming - PostgreSQL differential backup by collecting archived WAL files from a configured
archive_directory - Basic PostgreSQL WAL validation (sequence gaps, timeline consistency, file sanity checks)
- MySQL full backup using
xtrabackup(Percona XtraBackup) - MySQL differential backup command implemented via
xtrabackup --incrementalagainst the last full backup - Encrypted credential profiles (MySQL login-path, PostgreSQL .pgpass)
- Backup catalog tracking and per-backup metadata
- Optional single
.tar.zstarchive creation for full backups whenzstdis available
Not Yet Implemented:
- Automated restore functionality
- Point-in-Time Recovery (PITR) restore interface
- Cloud storage integration (CLI placeholders exist, upload implementation does not)
- End-to-end backup verification / automated test-restore workflow
- Production-grade error handling
- Stable user-facing PostgreSQL incremental backup workflow
Known Issues:
- Direct filesystem access is required for MySQL physical backups and PostgreSQL archived WAL access
- Limited testing across different PostgreSQL/MySQL versions
- No automated cleanup of old backups
- WAL validation is basic only; there is no full restore verification
- Timezone handling inconsistencies
This utility is a work in progress and not recommended for production use at this time.
The utility uses native database backup tools instead of SQL dumps:
PostgreSQL:
- Full backup:
pg_basebackupwith tar format and gzip compression - Differential backup: copies archived WAL files from a configured
archive_directory - WAL chain validation (sequence gaps, timeline consistency, file integrity β basic)
- Requires REPLICATION privilege,
wal_level = replica, and access to the configured WAL archive directory
MySQL:
- Full backup:
xtrabackupphysical backup with compression - Differential backup command:
xtrabackup --incremental --incremental-basedir=<last full backup> - Requires Percona XtraBackup installed and access to the MySQL data directory
- Python 3.10 or higher
- PostgreSQL client tools (pg_basebackup)
- MySQL: Percona XtraBackup 8.0
- Optional:
zstdandtarfor single-file.tar.zstarchives - User permissions:
- PostgreSQL: REPLICATION privilege
- PostgreSQL: Access to the configured WAL archive directory
- MySQL: Standard backup privileges + read access to data directory
git clone https://github.com/<your_user>/<repo_name>.git
cd <repo_name>
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r requirements.txtThe utility supports three configuration methods:
Most secure option - Credentials are encrypted and never exposed in environment variables or command line.
MySQL uses mysql_config_editor to store encrypted credentials in ~/.mylogin.cnf:
# Create a login-path profile
mysql_config_editor set --login-path=xtrabackup \
--host=localhost \
--user=backup_user \
--password
# Enter password when prompted
# Verify the profile (password is obfuscated)
mysql_config_editor print --all
# Test connection
mysql --login-path=xtrabackup -e "SELECT VERSION();"PostgreSQL uses ~/.pgpass file for password storage:
# Create .pgpass file in your home directory (~)
echo "localhost:5432:*:backup_user:your_password" >> ~/.pgpass
# Set correct permissions (required)
chmod 0600 ~/.pgpass
# Test connection (no password prompt)
psql -h localhost -U backup_user -d postgresπ Replace backup_user and your_password with your actual credentials.
More information: PostgreSQL .pgpass documentation
Usage with profile configuration:
# PostgreSQL with .pgpass
python cli/dbtool.py backup --db postgres --database mydb \
--storage local --config profile
# MySQL with login-path
python cli/dbtool.py backup --db mysql --database mydb \
--storage local --config profileπ Use --config profile parameter to read encrypted credentials.
Traditional method using a local .env file. Create it manually in the project root (an .env.example template is not currently committed).
Configure .env with your database credentials:
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=yourpassword
DB_NAME=yourdatabaseUsage:
python cli/dbtool.py backup --db postgres --database mydb \
--storage local --config filePGPASSWORD environment variable is not recommended because other users can see process environment variables.
PostgreSQL 18 / Environment Variables
Pass credentials directly via command line (least secure):
python cli/dbtool.py backup --db postgres --database mydb \
--storage local --config manual \
--host localhost --port 5432 \
--user backup_user --password secretGrant replication privilege:
ALTER USER your_user REPLICATION;Ensure wal_level is set correctly in postgresql.conf:
wal_level = replica
Configure WAL archiving so PostgreSQL copies segments into an archive directory:
archive_mode = on
archive_command = 'cp %p /path/to/archive/%f'
Restart PostgreSQL after configuration changes.
On first PostgreSQL run, the utility asks you to confirm or save that archive_directory in ~/.backup_utility/config.json.
Install Percona XtraBackup:
# Ubuntu/Debian
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
sudo apt-get update
sudo apt-get install percona-xtrabackup-80
# macOS
brew install percona-xtrabackup
# Verify
xtrabackup --versionOperations that require direct filesystem access need elevated privileges:
Operations requiring sudo/elevated permissions:
- MySQL
xtrabackupfull or differential backup (reads MySQL data files) - PostgreSQL differential backup when the configured WAL archive directory is protected by OS permissions
Operations NOT requiring sudo:
- PostgreSQL
pg_basebackupfull backup (uses replication protocol)
macOS/Linux:
# MySQL full backup (requires sudo)
sudo python cli/dbtool.py backup --db mysql --database mydb --storage local --config profile
# PostgreSQL differential backup (may require sudo or postgres user)
sudo python cli/dbtool.py backup --db postgres --database mydb --storage local --config profile
# PostgreSQL full backup (no sudo needed - uses replication protocol)
python cli/dbtool.py backup --db postgres --database mydb --storage local --config profileWindows:
- Run Command Prompt or PowerShell as Administrator
- Or grant read permissions to database data directories for your user account
Alternative to sudo:
- Run as database user:
sudo -u postgres python cli/dbtool.py ...orsudo -u mysql python cli/dbtool.py ... - Grant read permissions to the MySQL data directory / configured PostgreSQL archive directory (one-time setup)
Security Note: When using sudo with encrypted profile configuration, credentials remain secure and are never exposed in process lists.
# Full database backup
full database -path /path/to/backups
# Differential backup
# PostgreSQL: archived WAL copy with basic validation
# MySQL: xtrabackup incremental from the last full backup
differential backup
# Execute SQL query
SQL SELECT * FROM users WHERE id < 100
# Export query results to CSV
SQL SELECT * FROM users -extract -path /tmp/exports
# Show help
help
# Exit
exitBackups are created directly under the path passed to full database -path .... Differential backups are created as sibling directories next to the last full backup. Exact contents depend on the database type:
/backups/
βββ full_mydb_20251105_150000_a1b2/
β βββ metadata.json
β βββ base.tar.gz # PostgreSQL full backup
β βββ pg_wal.tar.gz # PostgreSQL WAL at backup time
β βββ backup_manifest # PostgreSQL 13+, optional
β βββ xtrabackup_checkpoints # MySQL full backup metadata
β βββ ...
βββ full_mydb_20251105_150000_a1b2.tar.zst # Optional single archive
βββ differential_mydb_20251105_160000_c3d4/
βββ base_backup_id.txt
βββ metadata.json
βββ 0000000100000000000000A1 # PostgreSQL archived WAL files
βββ xtrabackup_checkpoints # MySQL differential backup metadata
βββ ...
Other on-disk artifacts:
backup_catalog.jsonstores backup history across runsbackup_<database>.logstores per-database logs
Current Limitations:
- No partial/table-level backups (full database only)
- PostgreSQL differential backup depends on a preconfigured WAL archive directory
- No automated restore scripts
- No end-to-end restore verification; only basic WAL validation for the PostgreSQL differential flow
- No retention policy management
- No background scheduling or orchestration; most operations are still sequential
- Limited error recovery
PostgreSQL:
- Differential backup may require running as
postgresuser or equivalent permissions - Differential backup depends on filesystem access to the configured WAL archive directory
- PITR restore interface not yet implemented
MySQL:
- Full backups and differential backups are implemented
- Differential backup is based on
xtrabackup --incrementalagainst the last full backup - Requires xtrabackup prepare step before restore (manual)
This project is currently paused. Current follow-up areas if development resumes:
- Hardening the MySQL differential / incremental-from-full workflow
- Adding PITR restore interface for PostgreSQL
- Improving error handling and validation
- Adding automated restore functionality
- Testing across more database versions
- Improving documentation
- Deciding whether to expose the experimental PostgreSQL incremental/WAL pipeline code currently living under
services/backup/incremential/andservices/wal/
PostgreSQL differential backup fails with permission denied:
The differential flow needs read access to the configured PostgreSQL archive directory. Options:
- Run as the postgres user:
sudo -u postgres python cli/dbtool.py ... - Run with sudo:
sudo python cli/dbtool.py ... - Grant read permissions to the configured archive directory (security consideration required)
MySQL xtrabackup fails with permission denied:
xtrabackup: Can't change dir to '/usr/local/mysql/data/' (OS errno 13 - Permission denied)
xtrabackup needs read access to MySQL data directory. Options:
- Run with sudo:
sudo python cli/dbtool.py ... - Run as mysql user:
sudo -u mysql python cli/dbtool.py ... - Grant read permissions to data directory (security consideration required)
pg_basebackup: must be superuser or replication role:
ALTER USER your_user REPLICATION;wal_level error:
Edit postgresql.conf:
wal_level = replica
Then restart PostgreSQL.
xtrabackup command not found:
Install Percona XtraBackup as shown in the MySQL Setup section.
This project is experimental. Contributions are welcome, particularly:
- Testing on different PostgreSQL/MySQL versions
- Hardening MySQL differential / incremental-from-full backups
- Implementing PITR restore interface
- Automated restore functionality
- Backup verification tools
- Error handling improvements
- Documentation
Please note that APIs and project structure may change if development resumes.
MIT License - see LICENSE file
This software is provided as-is, without warranty. It is not production-ready and should only be used in development/testing environments. Always verify your backups and test restore procedures before relying on this tool.