WP-Migrate Documentation
A CLI tool for migrating WordPress sites between servers. Automatically detects SSH, SFTP, or FTP access and uses the fastest method available.
Installation
macOS & Linux
Run the installer script which automatically detects your system and downloads the correct binary:
$ curl -fsSL https://wp-migrate.dev/install.sh | bash
This installs wp-migrate to /usr/local/bin (requires sudo).
Windows
Download wp-migrate-windows.exe from the latest release and add it to your PATH.
Manual Installation
Download the appropriate binary for your platform from GitHub Releases:
wp-migrate-macos- macOS ARM64 (Apple Silicon, works on Intel via Rosetta 2)wp-migrate-linux- Linux x86_64wp-migrate-windows.exe- Windows x86_64
Quick Start
Interactive Wizard
The easiest way to run a migration is with the interactive wizard:
$ wp-migrate
The wizard will prompt you for source and destination credentials, then run the migration automatically.
Command-Line Mode
For automation or scripted migrations, generate a sample config, edit it, then run the migration:
$ wp-migrate init # writes a documented config.yaml
$ wp-migrate validate -c config.yaml # check it before running
$ wp-migrate migrate -c config.yaml # run the full migration
Licensing
WP-Migrate includes 3 free migrations to try the tool. After that, you'll need to purchase a license.
Activating a License
$ wp-migrate activate YOUR-LICENSE-KEY
Deactivating a License
To move your license to a new machine:
$ wp-migrate deactivate
Check License Status
$ wp-migrate status
Single User ($39) - 2 machine activations, unlimited migrations.
Agency ($149) - Unlimited activations, unlimited migrations.
Configuration
Create a YAML configuration file for scripted or repeatable migrations:
# config.yaml — generate with `wp-migrate init`
source:
host: oldsite.com
user: oldsite_user
pass: "${SOURCE_PASS}" # Environment variable
type: auto # auto | ssh | sftp | ftps | ftp
# key_file: ~/.ssh/id_rsa # Optional: SSH key instead of password
wp_path: /public_html
destination:
host: newsite.com
user: newsite_user
pass: "${DEST_PASS}"
type: auto
wp_path: /public_html
# Database credentials (required for SFTP/FTP, optional with SSH+WP-CLI)
db_host: localhost
db_name: newsite_wp
db_user: newsite_dbuser
db_pass: "${DEST_DB_PASS}"
new_url: https://newsite.com
Configuration Reference
| Key | Description | Required |
|---|---|---|
host |
Server hostname | Yes |
user |
Connection username | Yes |
pass |
Connection password (supports env vars) | Yes* |
type |
Connection type: auto (default), ssh, sftp, ftps, or ftp |
No |
port |
Connection port (defaults based on type: 22 for SSH/SFTP, 21 for FTP) | No |
key_file |
Path to SSH private key (instead of pass) |
No |
key_passphrase |
Passphrase for an encrypted SSH key | No |
wp_path |
Path to WordPress installation | Yes |
db_host |
Database hostname | Dest only |
db_name |
Database name | Dest only |
db_user |
Database username | Dest only |
db_pass |
Database password | Dest only |
new_url |
New site URL for search-replace | Dest only |
server_ip |
Server IP for pre-DNS testing | No |
* pass is required unless you authenticate with an SSH key_file. Database credentials are required for SFTP/FTP transfers and optional when SSH + WP-CLI is available.
Older configs using ftp_host, ftp_user, ftp_pass, ftp_type, and ftp_port still work as aliases for host, user, pass, type, and port. New configs should use the shorter names above.
Never commit passwords to version control. Use environment variables like ${VAR_NAME} in your config file, then set them in your shell before running.
Multisite
Multisite networks are auto-detected from the source wp-config.php, so a normal migrate handles a whole network with no extra config. Add an optional multisite block only to override behaviour or remap specific subsites:
# Optional — only needed to override auto-detection
multisite:
enabled: true # omit to auto-detect; false forces single-site handling
subdomain: true # omit to inherit source SUBDOMAIN_INSTALL
subsite_mappings: # optional: remap specific subsites
- old: shop.oldsite.com
new: shop.newbrand.com
To import a standalone site into an existing network as a new subsite, set mode: multisite-import with a multisite_import block and run wp-migrate import-to-network -c config.yaml:
mode: multisite-import
# destination.new_url is the NETWORK's base URL in this mode
multisite_import:
subsite_slug: myblog # required; lowercase letters/digits/hyphens
default_role: editor # role for source users lacking one (default: subscriber)
# network_admin_user: admin # optional: grant this network user admin on the subsite
Commands
| Command | Description |
|---|---|
wp-migrate |
Run the interactive migration wizard (no arguments) |
wp-migrate start |
Start the wizard, optionally pre-loading a config |
wp-migrate init |
Write a sample, documented config file |
wp-migrate validate -c <file> |
Validate a migration config file |
wp-migrate test-connection -c <file> |
Test source/destination connections |
wp-migrate migrate -c <file> |
Run a full migration (files + database + URL replace) |
wp-migrate import-to-network -c <file> |
Import a standalone site into an existing multisite network |
wp-migrate export -c <file> |
Export the source database only |
wp-migrate import -c <file> --sql <file> |
Import a database to the destination |
wp-migrate verify -c <file> |
Show pre-DNS-cutover testing instructions |
wp-migrate verify-site -c <file> |
HTTP-check the migrated site before DNS cutover |
wp-migrate deploy-agent -c <file> |
Deploy the PHP agent to a server and verify it works |
wp-migrate cleanup -c <file> |
Remove PHP agents and temporary files from servers |
wp-migrate activate <key> |
Activate license on this machine |
wp-migrate deactivate |
Deactivate license from this machine |
wp-migrate status |
Show license and trial status |
wp-migrate --version |
Show version information |
wp-migrate --help |
Show help (works on any subcommand too) |
Common Flags
| Flag | Description |
|---|---|
--config, -c |
Path to YAML configuration file |
--dry-run |
Show what would be done without making changes |
--skip-files |
Skip file transfer (database only) |
--skip-db |
Skip database migration (files only) |
--skip-search-replace |
Skip URL search-replace (same-URL migrations with DNS cutover) |
--source-url |
Override the source site URL used to reach the agent |
--yes, -y |
Skip the confirmation prompt |
Pre-DNS Testing
Test your migrated site before switching DNS by adding the server_ip option to your destination config:
destination:
# ... other settings ...
new_url: https://newsite.com
server_ip: 192.168.1.100 # Destination server IP
After migration completes, add an entry to your local /etc/hosts file:
192.168.1.100 newsite.com
Now you can visit https://newsite.com in your browser to test the migrated site on the new server before updating DNS.
How It Works
WP-Migrate automatically detects the best available method for each server:
1. Server Detection
When connecting to a server, WP-Migrate checks for:
- SSH access - Enables rsync file transfer and direct MySQL/WP-CLI access
- WP-CLI - Used for database operations when available
- MySQL access - Direct database export/import
- SFTP/FTPS - Secure file transfer
- FTP - Fallback file transfer method
2. File Transfer
- With SSH: Uses rsync for incremental, compressed transfers (4-10x faster)
- Without SSH: Uses FTP/SFTP with parallel transfers and retry logic
3. Database Migration
- With WP-CLI: Direct export/import using
wp db exportandwp db import - Without WP-CLI: Deploys a temporary PHP agent for database operations
4. URL Replacement
WP-Migrate performs serialization-aware search-replace:
- Properly handles PHP serialized arrays (preserving string lengths)
- Replaces URLs in all database tables
- Handles both HTTP and HTTPS URLs
- Preserves theme settings, widget configurations, and plugin data
Multisite
WP-Migrate supports WordPress multisite in two ways:
Migrate an entire network (network → network)
Auto-detected from the source wp-config.php — just run a normal migration. Network constants are regenerated for the new host and every subsite's URLs (including the wp_blogs / wp_site domain columns) are rewritten.
$ wp-migrate migrate -c config.yaml
Import a standalone site into an existing network
Add mode: multisite-import and a multisite_import block (see Configuration), then preview and run:
$ wp-migrate import-to-network -c config.yaml --dry-run # preview
$ wp-migrate import-to-network -c config.yaml # run
WP-Migrate registers a new subsite, imports the source's tables under a new wp_{N}_* prefix, merges users (matching by login/email), relocates media to wp-content/uploads/sites/{N}/, and rewrites URLs — leaving the rest of the network untouched. The network's wp-config.php and database are preserved.
Security
PHP Agent Security
When SSH is not available, WP-Migrate deploys a temporary PHP agent for database operations. The agent includes multiple security measures:
- Randomized filename - Agent is deployed with a random 32-character filename
- Token authentication - All requests require a secure token
- Auto-expiry - Agent automatically expires after 1 hour
- Self-deletion - Agent deletes itself after cleanup command
- IP restriction - Optional IP whitelist support
Credentials
- Credentials are never logged or stored permanently
- Use environment variables in config files to avoid committing secrets
- License keys are stored locally in
~/.wp-migrate/
Recommended Practices
- Use SFTP or FTPS instead of plain FTP when possible
- Use SSH keys instead of passwords when available
- Set environment variables for passwords rather than putting them in config files
- Delete config files containing credentials after migration
Support
Need help? Here's how to get support:
- Email: support@wp-migrate.dev
- GitHub Issues: Report bugs or request features
Agency license holders receive priority email support with faster response times.