Automate migrating PHP sites between CloudPanel servers — including site setup, configuration, content, FTP users, cron jobs, MySQL databases, and optional Laravel .env updates.
- Overview
- Features
- Prerequisites
- Installation
- Configuration
- Usage
- Workflow
- Safety & Idempotency
- Post-Migration Steps
- Troubleshooting
- Logs
- License
This Bash script streamlines migrating multiple PHP sites (and their MySQL databases) from one CloudPanel server to another.
It uses the CloudPanel CLI (clpctl) with rsync, sshpass, and sqlite3 to handle every step end-to-end with minimal downtime.
- Site setup: Auto-creates PHP sites with fallback defaults
- Config sync: Copies Nginx configs and SSL certificates
- Content sync: Rsyncs site files with ownership/permissions
- FTP users: Re-creates FTP users with random secure passwords
- Cron jobs: Migrates cron jobs, fixes
/etc/cron.dformat (includes user column) - MySQL:
- Parallel export + copy (throttled via
MAX_JOBS) - Gzip integrity check before import
- Sequential create + import into new CloudPanel
- Parallel export + copy (throttled via
- Laravel: Safely updates
.envDB credentials with escaping - Idempotent: Skips already existing sites, users, cron jobs on re-run
- Secure: Credentials logged with
chmod 600,umask 077
- Destination server: Ubuntu 18.04+ with CloudPanel installed
- Source server: SSH access with
clpctlpermissions - Installed automatically if missing:
sshpass,sqlite3,rsync,openssl,gzip
Clone the repository on your destination server:
git clone https://github.com/rick001/CloudPanel-Migration.git
cd CloudPanel-Migration
chmod +x migration.shYou can either export env vars before running or edit the top of migration.sh:
export SSH_USER=myuser
export SSH_HOST=source.server.com
export SSH_PASS='mypassword'
export SSH_PORT=22Optional knobs:
MAX_JOBS=3→ control concurrency of parallel export+copy- Paths:
remote_db_path=/home/clp/htdocs/app/data/db.sq3local_db_path=/home/clp/htdocs/app/data/db.sq3
Run the migration:
./migration.shThe script will:
- Copy the remote CloudPanel SQLite DB
- Create sites (with PHP version, vhost, users)
- Copy Nginx conf + SSL certs
- Rsync site content
- Re-create FTP users + cron jobs
- Export+copy MySQL DBs (parallel)
- Create DBs + import dumps (sequential)
- Update Laravel
.envif found
- Sequential
- Site creation, config sync, FTP, cron, DB creation/import
- Parallel (throttled)
- MySQL DB export+copy (
MAX_JOBScontrols concurrency, default 3)
- MySQL DB export+copy (
- Integrity checks
gzip -tvalidates.sql.gzdumps before import
- Re-running the script:
- Skips existing sites in local CloudPanel DB
- Skips existing FTP users and cron jobs
- Skips failed DB exports (status written under
/tmp/migrate_status)
- Credentials: stored at
/tmp/credentials.log(permissions600) - Logs: detailed step-by-step output in
/tmp/clp_migration_debug.log
- Reload/restart Nginx + PHP-FPM if required:
sudo systemctl reload nginx
- Test migrated sites before DNS switch (e.g., use
/etc/hosts) - For Laravel apps:
php artisan config:cache
- Once verified, update DNS to point to the new server
- SSH issues: ensure firewall allows port, check
SSH_USERhas rights - DB export hangs: lower
MAX_JOBSto1or2 - Corrupt dumps: check
gzip -tlog entries - Rerun needed: clear
/tmp/migrate_status/and/tmp/credentials.log
- Debug log:
/tmp/clp_migration_debug.log - Credentials:
/tmp/credentials.log - Per-DB status:
/tmp/migrate_status/<db_name>.status
MIT License — see LICENSE.
Based on CloudPanel official migration best practices and extended with automation, idempotency, and Laravel support.