A robust PHP command-line utility to automate the backup of Hestia Control Panel user data to various remote storage providers. It supports encryption, compression, retention policies, and multi-channel notifications, optimized for servers with limited resources.
├── app/ # Application source code (Backup, Notification, Storage, System, Utils)
├── config/ # Configuration files (app.php, constant.php)
├── libs/ # Composer dependencies and autoload
├── storage/ # Logs and runtime data
├── tests/ # PHPUnit test cases (unit, integration, E2E, hardening)
├── tmp/ # Temporary working directory for backup operations
├── .github/ # GitHub Actions, issue templates, CODEOWNERS, PR template
├── .env.example # Example environment configuration
├── run.php # Main entry point for backup
├── download.php # Restore utility
├── upload.php # (Optional) Upload utility
├── README.md # Project documentation
├── LICENSE # Apache License 2.0
├── NOTICE # Attribution notice
└── CONTRIBUTING.md # Contribution guidelines
- Multiple Storage Backends: Upload encrypted backups to AWS S3, Backblaze B2, DigitalOcean Spaces, traditional FTP/FTPS servers, or local filesystem via Flysystem.
- Strong Encryption: AES-256-CBC encryption (via OpenSSL) or GPG encryption with optional key management.
- Flexible Compression: Support for gzip, zstd, bzip2, xz, zip, 7z formats with configurable compression levels.
- Smart Rotation: Advanced retention policies supporting daily, weekly, monthly, and yearly backup retention with intelligent file grouping.
- XBK File Format: Custom .xbk archive format supporting layered compression and encryption with filename metadata.
- Dry-Run Mode: Safe simulation without creating, uploading, or deleting files for testing configurations.
- Structured Logging: Comprehensive logging with Monolog supporting multiple channels and log levels.
- Pre-flight Checks: CPU load, disk space, time window validation, and dependency verification.
- Multi-Channel Notifications: Email (SMTP), Telegram, Discord, Slack, Microsoft Teams, Google Chat notifications.
- Performance Optimized: Streaming operations for large files, memory-efficient processing, concurrent uploads.
- Security Hardened: Read-only source protection, path traversal prevention, secure credential management.
- CLI Tools Integration: Native support for 7z, zip, gzip, zstd, bzip2, xz CLI tools for optimal performance.
- PSR Compliant: Modern PHP 8.2+ codebase following PSR-4, PSR-12, PSR-3 standards with full type declarations.
- PHP 8.2 or higher
- Composer
- PHP Extensions:
ctype,mbstring,openssl,ftp(for FTP driver) - CLI Tools for compression/encryption:
- gzip (install:
sudo apt install gzip) - zstd (install:
sudo apt install zstd) - bzip2 (install:
sudo apt install bzip2) - xz-utils (install:
sudo apt install xz-utils) - zip & unzip (install:
sudo apt install zip unzip) - p7zip-full (install:
sudo apt install p7zip-full) - gnupg (install:
sudo apt install gnupg)
- gzip (install:
This tool has been tested on the following operating systems:
-
Debian/Ubuntu
sudo apt update && sudo apt install -y php-cli php-mbstring php-common \ unzip zip gzip zstd bzip2 xz-utils p7zip-full gnupg composer -
CentOS/RHEL
sudo yum install -y epel-release && sudo yum install -y php-cli php-mbstring php-common \ unzip zip gzip zstd bzip2 xz p7zip p7zip-plugins gnupg composer -
macOS
brew update && brew install php composer zstd xz p7zip gnupg
git clone https://github.com/xnetvn-com/php-backup-remote.git
cd php-backup-remote
cd libs
composer install --no-dev --optimize-autoloader
cd ..For safe and convenient updates to the latest version:
./auto_update.shNote:
-
The script will automatically detect the default branch (
mainormaster) from the remote repository. -
If the current directory is a Git repository, it will fetch and hard reset to match the remote branch (all local changes will be overwritten).
-
If the current directory is not a Git repository, it will clone the correct branch and copy files into the current directory.
-
Your configuration files and logs will be preserved.
-
Requires
gitto be installed.
⚠️ Warning: This script will overwrite any local changes in the repository directory. Make sure to back up your work if needed.
See UPDATE_GUIDE.md for detailed instructions.
-
Clone the repository and enter directory:
git clone https://github.com/xnetvn-com/php-backup-remote.git cd php-backup-remote -
Install PHP dependencies (production):
cd libs composer install --no-dev --optimize-autoloader cd ..
For development (with dev dependencies):
cd libs composer install cd ..
-
Copy and customize environment settings:
cp .env.example .env # Edit .env to configure backup paths, encryption, compression, and storage drivers -
Run a dry-run to verify settings:
php run.php --dry-run
-
Execute a real backup:
php run.php
# Core backup settings
BACKUP_PASSWORD=your-super-secret-encryption-password
BACKUP_DIRS=/backup,/home,/var/www
BACKUP_COMPRESSION=gzip # Options: none, gzip, zstd, bzip2, xz, zip, 7z
BACKUP_COMPRESSION_LEVEL=6
BACKUP_ENCRYPTION=aes # Options: none, aes, gpg, zip, 7z
BACKUP_ENCRYPTION_KEY_PATH=/path/to/public.key # For GPG (optional)
TMP_DIR=/tmp/php-backup-remote
# Advanced compression and encryption combinations:
# - BACKUP_COMPRESSION=7z + BACKUP_ENCRYPTION=7z: Uses 7z CLI for both compression and encryption in one step
# - BACKUP_COMPRESSION=zip + BACKUP_ENCRYPTION=zip: Uses zip CLI with AES-256 encryption in one step
# - BACKUP_COMPRESSION=7z + BACKUP_ENCRYPTION=none: Uses 7z CLI for compression only
# - BACKUP_COMPRESSION=zip + BACKUP_ENCRYPTION=none: Uses zip CLI for compression only
# - Separate steps: BACKUP_COMPRESSION=gzip + BACKUP_ENCRYPTION=aes (compress first, then encrypt)
# Rotation & retention
ROTATION_ENABLED=true
ROTATION_KEEP_LATEST=7
ROTATION_KEEP_DAILY=7
ROTATION_KEEP_WEEKLY=4
ROTATION_KEEP_MONTHLY=12
ROTATION_KEEP_YEARLY=3
ROTATION_PATTERN=*.xbk.*
# Remote storage (S3, B2, FTP, Local)
REMOTE_DRIVER=s3 # Options: s3, b2, ftp, local
# S3 settings
S3_KEY=your-s3-access-key
S3_SECRET=your-s3-secret-key
S3_REGION=ap-southeast-1
S3_BUCKET=your-s3-bucket-name
S3_ENDPOINT=https://s3.example.com
S3_USE_PATH_STYLE=true
# Backblaze B2 settings
B2_KEY=your-b2-application-key-id
B2_SECRET=your-b2-application-key
B2_BUCKET=your-b2-bucket-name
B2_REGION=us-west-002
# FTP settings
FTP_HOST=your-ftp-host
FTP_USER=your-ftp-username
FTP_PASS=your-ftp-password
FTP_ROOT=/backups
FTP_SSL=true
FTP_PASSIVE=true
# Local storage
LOCAL_PATH=/mnt/backup-disk
# Performance & resource limits
ALLOWED_START_TIME=01:00
ALLOWED_END_TIME=05:00
MAX_CPU_LOAD=2.5
MIN_DISK_FREE_PERCENT=15
MEMORY_LIMIT=256M
TIME_LIMIT=3600
# Notification settings
EMAIL_SMTP_HOST=smtp.example.com
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USER=your-email@example.com
EMAIL_SMTP_PASS=your-smtp-password
EMAIL_SMTP_ENCRYPTION=tls
ADMIN_EMAIL=admin@example.com
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
TELEGRAM_CHAT_ID=your-telegram-chat-id
DISCORD_WEBHOOK=your-discord-webhook-url
SLACK_WEBHOOK=your-slack-webhook-url
TEAMS_WEBHOOK=your-teams-webhook-url
GOOGLE_CHAT_WEBHOOK=your-google-chat-webhook-url
NOTIFY_INTERVAL_MINUTES=180
# Advanced options
DRY_RUN=false
LOG_CHANNEL=app
LOG_PATH=storage/logs/app.log
LOG_LEVEL=info
LOCK_FILE=storage/.backup.lock
# Security
ENFORCE_READONLY=true
SAFE_MODE=true
# Developer/debug
DEBUG=false
VERBOSE=false
# Custom user hooks (optional)
PRE_BACKUP_HOOK=/path/to/pre-backup.sh
POST_BACKUP_HOOK=/path/to/post-backup.sh
# Example for multiple backup sources
# BACKUP_DIRS=/backup,/home/user1,/home/user2,/var/www/htmlCopy the example environment file and edit your settings:
cp .env.example .envOpen .env and configure:
| Variable | Description | Default |
|---|---|---|
| BACKUP_PASSWORD | Password for AES or GPG encryption (choose a strong secret) | REQUIRED |
| BACKUP_DIRS | Comma-separated absolute paths to backup user directories | /backup |
| BACKUP_COMPRESSION | Compression method (none, gzip, zstd, bzip2, xz, zip, 7z) |
none |
| BACKUP_COMPRESSION_LEVEL | Compression level (1-9, default 6) | 6 |
| BACKUP_ENCRYPTION | Encryption method (none, aes, gpg, zip, 7z) |
aes |
| BACKUP_ENCRYPTION_KEY_PATH | Path to public key for GPG encryption (optional) | Not set |
| REMOTE_DRIVER | Override to use a single storage driver: s3, b2, ftp or local |
Not set |
| TMP_DIR | Temporary directory for backup operations | /tmp/php-backup-remote |
| Configuration | CLI Tools Used | Description |
|---|---|---|
BACKUP_COMPRESSION=7z + BACKUP_ENCRYPTION=7z |
7z a (single step) |
7z CLI compresses and encrypts in one command |
BACKUP_COMPRESSION=zip + BACKUP_ENCRYPTION=zip |
zip -e (single step) |
zip CLI compresses with traditional encryption |
BACKUP_COMPRESSION=7z + BACKUP_ENCRYPTION=none |
7z a (compression only) |
7z CLI for compression only |
BACKUP_COMPRESSION=zip + BACKUP_ENCRYPTION=none |
zip (compression only) |
zip CLI for compression only |
BACKUP_COMPRESSION=gzip + BACKUP_ENCRYPTION=aes |
gzip + openssl |
Separate compression and encryption steps |
BACKUP_COMPRESSION=zstd + BACKUP_ENCRYPTION=gpg |
zstd + gpg |
Separate compression and encryption steps |
Note: The zip CLI uses traditional password-based encryption (not AES-256). For stronger encryption, use 7z or separate AES encryption.
| Variable | Description |
|---|---|
| S3_KEY | S3 access key |
| S3_SECRET | S3 secret key |
| S3_REGION | S3 region |
| S3_BUCKET | S3 bucket name |
| S3_ENDPOINT | Custom endpoint (for non-AWS providers) |
| S3_USE_PATH_STYLE | true if using path-style URLs (e.g., DigitalOcean Spaces) |
| Variable | Description |
|---|---|
| B2_KEY | Backblaze B2 application key ID |
| B2_SECRET | Backblaze B2 application key |
| B2_BUCKET | Backblaze B2 bucket name |
| B2_REGION | Backblaze B2 region (optional) |
| Variable | Description |
|---|---|
| FTP_HOST | FTP server hostname |
| FTP_USER | FTP username |
| FTP_PASS | FTP password |
| FTP_ROOT | Base directory on FTP server (optional) |
| FTP_SSL | true or false for FTPS (optional) |
| FTP_PASSIVE | true (passive) or false (active) (recommended: true) |
| Variable | Description |
|---|---|
| LOCAL_PATH | Local file system path for backups |
| Variable | Description | Default |
|---|---|---|
| ROTATION_ENABLED | Enable automatic rotation | true |
| ROTATION_KEEP_LATEST | Number of recent backups to keep | 7 |
| ROTATION_KEEP_DAILY | Number of daily backups to keep | 7 |
| ROTATION_KEEP_WEEKLY | Number of weekly backups to keep | 4 |
| ROTATION_KEEP_MONTHLY | Number of monthly backups to keep | 12 |
| ROTATION_KEEP_YEARLY | Number of yearly backups to keep | 3 |
| ROTATION_PATTERN | Pattern to match backup files for rotation | *.xbk.* |
| Variable | Description | Default |
|---|---|---|
| ALLOWED_START_TIME | Earliest allowed backup time (HH:MM), empty to disable |
01:00 |
| ALLOWED_END_TIME | Latest allowed backup time (HH:MM), empty to disable |
05:00 |
| MAX_CPU_LOAD | Max 1-min CPU load average, 0 to disable | 2.5 |
| MIN_DISK_FREE_PERCENT | Minimum free disk % in temp dir | 15 |
| MEMORY_LIMIT | PHP memory limit (e.g., 256M) |
256M |
| TIME_LIMIT | Max script time in seconds, 0 for unlimited | 3600 |
| Variable | Description | Default |
|---|---|---|
| EMAIL_SMTP_HOST | SMTP server host | - |
| EMAIL_SMTP_PORT | SMTP server port | 587 |
| EMAIL_SMTP_USER | SMTP username | - |
| EMAIL_SMTP_PASS | SMTP password | - |
| EMAIL_SMTP_ENCRYPTION | SMTP encryption method (tls, ssl, or empty for none) |
tls |
| ADMIN_EMAIL | Email address to receive notifications | - |
| TELEGRAM_BOT_TOKEN | Telegram bot token | - |
| TELEGRAM_CHAT_ID | Telegram chat ID | - |
| DISCORD_WEBHOOK | Discord webhook URL | - |
| SLACK_WEBHOOK | Slack webhook URL | - |
| TEAMS_WEBHOOK | Microsoft Teams webhook URL | - |
| GOOGLE_CHAT_WEBHOOK | Google Chat webhook URL | - |
| NOTIFY_INTERVAL_MINUTES | Cool-down period between notifications (minutes) | 180 |
| Variable | Description | Default |
|---|---|---|
| DRY_RUN | Simulate backup without writing/uploading files | false |
| LOG_CHANNEL | Log channel name (for Monolog) | app |
| LOG_PATH | Path to log file | storage/logs/app.log |
| LOG_LEVEL | Log level (info, debug, error, etc.) |
info |
| LOCK_FILE | Path to lock file to prevent concurrent runs | storage/.backup.lock |
| Variable | Description | Default |
|---|---|---|
| ENFORCE_READONLY | Enforce read-only mode for BACKUP_DIRS | true |
| SAFE_MODE | Enable extra safety checks (recommended) | true |
| Variable | Description | Default |
|---|---|---|
| DEBUG | Enable debug mode | false |
| VERBOSE | Enable verbose output | false |
| Variable | Description | Default |
|---|---|---|
| PRE_BACKUP_HOOK | Path to script to run before backup | Not set |
| POST_BACKUP_HOOK | Path to script to run after backup | Not set |
-
Read-Only Guarantee for BACKUP_DIRS: All files and directories specified in
BACKUP_DIRSare treated as strictly read-only. The backup system will never modify, delete, move, or overwrite any original file in these directories. All backup, compression, and encryption operations are performed on temporary copies in a dedicated temp directory (TMP_DIR). This ensures absolute safety and integrity of your source data. -
AES-256-CBC Encryption: Industry-standard encryption with OpenSSL implementation providing strong data protection at rest and in transit.
-
GPG Encryption Support: Alternative encryption using GnuPG with public key cryptography for enhanced security scenarios.
-
Path Traversal Protection: Comprehensive validation to prevent directory traversal attacks and unauthorized file access.
-
Secure Credential Management: Environment-based credential storage with optional encryption for sensitive configuration values.
-
Automated Security Tests: The project includes automated tests to verify that no write, delete, or move operations are ever performed directly in
BACKUP_DIRS. -
CLI Tool Security: Secure invocation of external compression and encryption tools with proper argument sanitization.
-
File Permissions: Proper temporary directory permissions (0700) and secure file handling throughout the backup process.
-
Best Practice: Always set appropriate file system permissions to enforce read-only access for the backup process user on your backup source directories.
Real backup:
php run.phpDry-run (simulate):
php run.php --dry-runphp download.php --user=<username> --version=<YYYY-MM-DD_HH-MM-SS> [--remote=<driver>] [--outdir=<path>]30 2 * * * /usr/bin/php /path/to/php-backup-remote/run.php > /dev/null 2>&1- Initialization: Load
.envand bootstrap services. - Locking: Prevent concurrent runs with a lock file.
- Pre-flight Checks: CPU, disk space, and time-window validation.
- Archive: Create compressed archive per user directory using TAR with configurable compression.
- Encrypt: Encrypt archive with AES-256-CBC or GPG using OpenSSL/CLI tools.
- Upload: Stream to configured remote storage backends (S3/B2/FTP/Local).
- Cleanup: Remove local temporary files and release lock.
- Rotation: List remote files and delete older backups beyond retention policy.
- Notification: Send success or failure alerts via multiple channels.
Run PHPUnit unit tests:
./libs/vendor/bin/phpunit --configuration=phpunit.xmlThe project includes comprehensive test coverage:
- Unit Tests: Core functionality, encryption, compression, storage operations
- Integration Tests: Component interaction, backup workflows, storage backends
- Edge Case Tests: Large files, special characters, error conditions
- Security Tests: Encryption strength, credential management, path validation
- Performance Tests: Memory usage, streaming operations, concurrent access
For test coverage report generation:
# Install Xdebug (if not already installed)
sudo apt install php8.2-xdebug
# Generate coverage report
./libs/vendor/bin/phpunit --coverage-html coverage/We welcome contributions from the community! Please see CONTRIBUTING.md for detailed guidelines. All contributors must follow the CODEOWNERS and use the provided pull request template. Issues and feature requests should use the appropriate issue templates.
- Security Contact: Please report security vulnerabilities via security_vulnerability.md or email license@xnetvn.net.
- CI/CD: All code is automatically tested, linted, scanned for secrets and vulnerabilities, and must pass all checks before merging.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.