Skip to content

Pawikoski/wp-migrator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WordPress VPS Migrator

Automated WordPress site migration between VPS servers with Ansible, including Nginx configuration, SSL certificate provisioning, and database migration.

A production-ready toolkit for migrating WordPress sites from any source VPS to a target server with zero-downtime deployment, automatic SSL/TLS certificate provisioning via Let's Encrypt, and optimized Nginx configuration.

License: MIT

Features

  • Automated WordPress Migration - Complete site migration including database and wp-content directory
  • Intelligent Source Detection - Automatically locates WordPress installations on source servers
  • Database Management - Creates isolated databases with secure credentials for each site
  • Nginx Configuration - Production-ready virtual host configuration with performance optimizations
  • SSL/TLS Automation - Let's Encrypt certificate provisioning via Certbot with HTTP-01 challenge
  • Security Hardening - Includes catch-all vhost protection and security headers
  • Per-Domain Logging - Separate access and error logs for each migrated domain
  • Cloudflare Integration - Handles Cloudflare proxy mode requirements for SSL provisioning
  • Zero Downtime - Migrates content before DNS cutover for seamless transitions

Requirements

Target Server

  • Debian/Ubuntu-based Linux distribution
  • Root or sudo access
  • Python 3.6+
  • Ansible 2.9+ (installed on target or control machine)

Source Server

  • SSH access with key-based authentication
  • WP-CLI installed (for database export)
  • Read access to WordPress files and database

Domain Prerequisites

  • Access to DNS management (for A record updates)
  • If using Cloudflare: ability to toggle proxy mode

Installation

  1. Clone the repository

    git clone https://github.com/Pawikoski/wp-migrator
    cd wp-migrator
  2. Install Ansible (if not already installed)

    # Debian/Ubuntu
    apt update && apt install -y ansible
    
    # macOS
    brew install ansible
  3. Configure Ansible (already set up in ansible.cfg)

    # The project includes pre-configured ansible.cfg
    # Roles path: /opt/wp-migrator/roles

Configuration

1. Configure Source Servers

Edit sources.yml to define your source VPS servers:

sources:
  sh01:
    host: "0.0.0.0"                     # Source server IP or hostname
    user: "username"                    # SSH user
    port: 22                            # SSH port
    identity_file: "/root/.ssh/id_rsa"  # SSH private key path
    wp_path_suffix: "/home/username/domains/{domain}/public_html"

Note: {domain} will be automatically replaced with the actual domain during migration.

2. Configure Global Variables

Edit group_vars/all.yml:

# Target WordPress installation directory
wp_base_dir: /var/www/blogs

# Nginx paths
nginx_sites_available: /etc/nginx/sites-available
nginx_sites_enabled: /etc/nginx/sites-enabled

# PHP-FPM socket (adjust to your PHP version)
php_fpm_socket: /run/php/php8.4-fpm.sock

# Certbot configuration
enable_certbot: true
certbot_email: "admin@yourdomain.com"

# MariaDB/MySQL configuration
mysql_root_login_unix_socket: /run/mysqld/mysqld.sock
wp_db_admin_user: root
wp_db_admin_password: ""  # Leave empty for Unix socket auth

# Cleanup source tmp files after migration
source_cleanup_tmp: false

Usage

Full Migration Process

IMPORTANT: Follow these steps in order to ensure successful SSL provisioning:

1. Update DNS Records

Point your domain's A record to the target VPS IP address:

Type: A
Name: @
Value: [target-vps-ip]
TTL: 300 (5 minutes)

2. Disable HTTPS Proxy (Cloudflare Users)

If using Cloudflare, temporarily disable the proxy:

  • Go to Cloudflare Dashboard → DNS
  • Click the orange cloud icon to turn it gray (DNS only)
  • This allows Let's Encrypt to verify domain ownership

3. Run Migration

./migrate.sh <source_name> <domain>

Example:

./migrate.sh sh01 example.com

This will:

  1. ✅ Verify SSH connection to source VPS
  2. ✅ Auto-detect WordPress directory on source
  3. ✅ Create database and user on target VPS
  4. ✅ Export database from source VPS (via WP-CLI)
  5. ✅ Archive and transfer wp-content directory
  6. ✅ Import database and files to target VPS
  7. ✅ Configure Nginx virtual host (HTTP)
  8. ✅ Provision SSL certificate via Let's Encrypt (if enabled)
  9. ✅ Update Nginx configuration to enable HTTPS
  10. ✅ Update WordPress site URLs to HTTPS

4. Re-enable Cloudflare Proxy

After successful migration:

  • Click the gray cloud icon to turn it orange (Proxied)
  • Set SSL/TLS mode to Full (strict)

Certificate-Only Provisioning

If you've already migrated but need to add/renew SSL certificates:

./certbot.sh <domain>

Example:

./certbot.sh example.com

Requirements:

  • Domain DNS must already point to target VPS
  • Cloudflare proxy must be disabled (gray cloud)
  • WordPress files must exist at {{ wp_base_dir }}/{{ domain }}/public

Cloudflare Integration

SSL/TLS Modes

WordPress Migrator works seamlessly with Cloudflare when configured correctly:

Cloudflare Mode Origin Certificate Status
DNS Only (gray cloud) Not required ✅ Works
Proxied (orange cloud) + Flexible Not required ⚠️ Mixed content warnings
Proxied (orange cloud) + Full Required ✅ Recommended
Proxied (orange cloud) + Full (strict) Required (Let's Encrypt) ✅ Best security

Recommended Workflow

  1. During Migration: Gray cloud (DNS only)
  2. After Certbot: Orange cloud (Proxied) + Full (strict) mode
  3. Ongoing: Certificates auto-renew via systemd timer

Common Cloudflare Errors

  • 521 Error: Origin server refused connection

    • Cause: SSL not configured on origin while Cloudflare expects HTTPS
    • Fix: Run ./certbot.sh domain.com and enable Full/Full (strict) mode
  • 526 Error: Invalid SSL certificate

    • Cause: Certificate expired or misconfigured
    • Fix: Check certificate validity with certbot certificates

Monitoring and Logs

Per-Domain Logs

Each migrated domain has dedicated log files:

# Access logs
tail -f /var/log/nginx/example.com.access.log

# Error logs
tail -f /var/log/nginx/example.com.error.log

# Follow both simultaneously
multitail /var/log/nginx/example.com.access.log /var/log/nginx/example.com.error.log

Certbot Renewal Status

Check automatic certificate renewal:

# List all certificates
certbot certificates

# Test renewal process (dry run)
certbot renew --dry-run

# Check systemd timer status
systemctl status certbot.timer
systemctl list-timers | grep certbot

Nginx Configuration

Catch-All Default Vhost

The migrator installs a default catch-all vhost that returns 444 (connection closed) for unrecognized domains. This prevents:

  • Showing wrong site content for unmigrated domains
  • Information disclosure
  • Certificate errors

To disable this protection:

# In group_vars/all.yml
nginx_enable_default_deny: false

Performance Optimizations

The generated Nginx configuration includes:

  • FastCGI Buffering: Optimized buffer sizes for PHP-FPM
  • Static Asset Caching: 30-day cache for images, CSS, JS
  • Gzip Compression: Reduces bandwidth usage
  • Client Max Body Size: 64MB upload limit
  • HTTP/2: Enabled when SSL is active

Security Features

  • Blocks direct access to wp-config.php, readme.html, license.txt
  • Denies access to hidden files (.htaccess, .git, etc.)
  • Prevents PHP execution in wp-content/uploads/
  • Sets SCRIPT_FILENAME to prevent path traversal

Project Structure

wp-migrator/
├── ansible.cfg                 # Ansible configuration
├── sources.yml                 # Source VPS definitions
├── group_vars/
│   └── all.yml                # Global variables
├── playbooks/
│   ├── migrate.yml            # Full migration playbook
│   ├── certbot.yml            # Certificate-only playbook
│   └── vault/                 # Encrypted database passwords (auto-generated)
├── roles/
│   ├── wp_site/               # Nginx + SSL configuration
│   │   ├── tasks/
│   │   │   ├── main.yml       # Main site provisioning tasks
│   │   │   └── certbot.yml    # SSL certificate tasks
│   │   └── templates/
│   │       ├── nginx-site.conf.j2
│   │       └── nginx-default-deny.conf.j2
│   └── wp_migrate/            # Database + file migration
│       ├── tasks/
│       │   └── main.yml       # Migration tasks
│       └── defaults/
│           └── main.yml       # Default variables
├── migrate.sh                 # Migration wrapper script
└── certbot.sh                 # Certificate wrapper script

Troubleshooting

Migration Issues

Problem: Could not find wp-config.php on source VPS

Solution: The auto-detection failed. Manually specify the path in sources.yml:

sources:
  sh01:
    wp_path_suffix: "/full/absolute/path/to/wordpress"  # Use absolute path

Problem: SSH connection failed

Solution: Verify SSH access manually:

ssh -i /path/to/key -p PORT user@host "whoami"

SSL Certificate Issues

Problem: Certbot did not issue certificate

Causes and Solutions:

  1. DNS not propagated: Wait for DNS TTL to expire, verify with dig +short example.com
  2. Cloudflare proxy enabled: Disable orange cloud (set to gray)
  3. Port 80 blocked: Ensure firewall allows HTTP (needed for HTTP-01 challenge)
  4. Webroot not accessible: Verify {{ wp_base_dir }}/{{ domain }}/public exists

Problem: Certificate already exists warning

Solution: Certbot detected existing cert. To force renewal:

certbot certonly --webroot -w /var/www/blogs/example.com/public \
  -d example.com -d www.example.com --force-renewal

Nginx Issues

Problem: nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)

Solution: Another process is using port 443:

# Find the process
lsof -i :443

# If it's another nginx, restart it
systemctl restart nginx

Problem: Wrong site appears for domain

Solution: Check Nginx configuration:

# Test configuration
nginx -t

# Check which vhost handles the domain
curl -H "Host: example.com" http://127.0.0.1/

# Verify symlink exists
ls -la /etc/nginx/sites-enabled/ | grep example.com

Database Issues

Problem: Access denied for user 'root'@'localhost'

Solution: Ensure MySQL root can authenticate via Unix socket:

# Test MySQL root access
mysql -u root -e "SELECT 1;"

# If fails, update group_vars/all.yml with password
wp_db_admin_password: "your_root_password"

Advanced Configuration

Custom PHP Version

If using a different PHP version, update group_vars/all.yml:

php_fpm_socket: /run/php/php8.2-fpm.sock  # Change version number

Multiple Source Servers

Add multiple source definitions in sources.yml:

sources:
  hosting01:
    host: "192.168.1.10"
    user: "webmaster"
    port: 22
    identity_file: "~/.ssh/hosting01_rsa"
    wp_path_suffix: "/var/www/{domain}"

  vps02:
    host: "example.com"
    user: "admin"
    port: 2222
    identity_file: "~/.ssh/vps02_ed25519"
    wp_path_suffix: "/home/sites/{domain}/public_html"

Then migrate from different sources:

./migrate.sh hosting01 site1.com
./migrate.sh vps02 site2.com

Disable Certbot for Manual SSL

To use custom SSL certificates instead of Let's Encrypt:

# In group_vars/all.yml
enable_certbot: false
enable_ssl: true
ssl_cert_path: "/etc/ssl/certs/example.com.crt"
ssl_key_path: "/etc/ssl/private/example.com.key"

Security Considerations

  1. SSH Keys: Store private keys securely with chmod 600
  2. Database Passwords: Auto-generated and stored in playbooks/vault/
  3. Vault Encryption: Consider encrypting sensitive files:
    ansible-vault encrypt sources.yml
    ansible-vault encrypt group_vars/all.yml
  4. Firewall: Configure UFW/iptables to allow only necessary ports (22, 80, 443)
  5. Regular Updates: Keep system packages and WordPress core updated

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

# Clone the repository
git clone https://github.com/Pawikoski/wp-migrator.git
cd wp-migrator

# Create a feature branch
git checkout -b feature/your-feature-name

# Make changes and test
./migrate.sh test_source test.example.com

# Commit changes
git add .
git commit -m "Add: description of changes"

# Push and create PR
git push origin feature/your-feature-name

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Acknowledgments


Made with ❤️ for the WordPress community

About

Automated WordPress migration toolkit using Ansible. Migrate sites between VPS servers with zero downtime. Includes automatic WordPress detection, database migration, Nginx configuration, Let's Encrypt SSL provisioning, and Cloudflare integration. Production-ready with security hardening and per-domain logging.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors