Skip to content Skip to footer

Complete Guide: Migrate WordPress to VPS & Optimize for High Traffic

This comprehensive guide walks you through migrating a WordPress site from shared hosting to a VPS and optimizing it for maximum performance.

1. Prerequisites

What You’ll Need:

  • VPS Server (e.g., Hetzner, DigitalOcean, Linode)
    • Minimum: 2GB RAM, 2 CPU cores
    • Recommended: 4GB RAM, 3+ CPU cores
  • SSH access to your VPS
  • Access to current hosting (cPanel/hPanel)
  • Domain registrar access (for DNS changes)
  • FTP/SFTP client (FileZilla recommended)
  • Basic command line knowledge

VPS Requirements:

  • Ubuntu 20.04+ or Debian 11+
  • Apache or Nginx installed
  • MySQL/MariaDB installed
  • PHP 8.0+ installed
  • Root or sudo access

2. Prepare Your VPS

Install LAMP Stack

# Connect to VPS
ssh root@your_vps_ip

# Update system
apt update && apt upgrade -y

# Install Apache
apt install apache2 -y

# Install MySQL
apt install mysql-server -y

# Secure MySQL installation
mysql_secure_installation

# Install PHP and extensions
apt install php php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip -y

# Enable Apache modules
a2enmod rewrite
systemctl restart apache2

# Verify installations
apache2 -v
mysql --version
php -v

Create WordPress Directory

# Set proper permissions
cd /var/www/html
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html

3. Backup Your Current Site

Method 1: Using Hosting Control Panel

Step 1: Download Files

  1. Log into your hosting control panel (cPanel/hPanel)
  2. Go to File Manager
  3. Navigate to your WordPress directory (usually public_html)
  4. Right-click → Compress → Choose ZIP format
  5. Wait for compression to complete
  6. Download the ZIP file to your computer

Step 2: Export Database

  1. In control panel, go to phpMyAdmin
  2. Select your WordPress database from left sidebar
  3. Click Export tab at the top
  4. Choose Quick export method
  5. Format: SQL
  6. Click Go to download

Method 2: Manual FTP + Database Export

Download Files via FTP:

# Use FileZilla or similar FTP client
# Connect to your shared hosting
# Download entire WordPress directory

Export Database:

-- In phpMyAdmin, select database and export
-- OR use command line if available:
mysqldump -u username -p database_name > backup.sql

Method 3: Using Migration Plugins (For Sites Under 500MB)

Using Duplicator:

  1. Install Duplicator plugin on current site
  2. Go to Duplicator → Packages → Create New
  3. Follow wizard to build package
  4. Download both installer.php and .zip archive

Note: Free version has 500MB limit. For larger sites, use manual method.


4. Migrate to VPS

Upload Files to VPS

Using SCP (Secure Copy):

# From your local machine
scp wordpress-backup.zip root@your_vps_ip:/var/www/html/
scp database-backup.sql root@your_vps_ip:/root/

Or use SFTP client like FileZilla:

  • Protocol: SFTP
  • Host: your_vps_ip
  • Username: root
  • Password: your_password
  • Port: 22

Extract Files on VPS

# SSH into VPS
ssh root@your_vps_ip

# Clear default WordPress installation if exists
cd /var/www/html
rm -rf *

# Extract your backup
unzip /root/wordpress-backup.zip -d /var/www/html/

# If files are in subdirectory (like public_html), move them
cd /var/www/html
if [ -d "public_html" ]; then
    mv public_html/* .
    mv public_html/.htaccess . 2>/dev/null
    rm -rf public_html/
fi

# Set proper permissions
chown -R www-data:www-data /var/www/html
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;

Create Database and Import

# Access MySQL
mysql -u root -p

# Create database and user
CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wordpress_user'@'localhost' IDENTIFIED BY 'strong_password_here';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wordpress_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

# Import your database backup
mysql -u root -p wordpress_db < /root/database-backup.sql

# Verify import
mysql -u root -p -e "USE wordpress_db; SHOW TABLES;"

Update wp-config.php

nano /var/www/html/wp-config.php

Update these lines:

define('DB_NAME', 'wordpress_db');
define('DB_USER', 'wordpress_user');
define('DB_PASSWORD', 'strong_password_here');
define('DB_HOST', 'localhost');

Update Site URLs

mysql -u wordpress_user -p wordpress_db

# Replace old-domain.com with your actual domain
UPDATE wp_options SET option_value = 'http://your-vps-ip' WHERE option_name = 'siteurl';
UPDATE wp_options SET option_value = 'http://your-vps-ip' WHERE option_name = 'home';
EXIT;

Test Your Site

Visit http://your_vps_ip in browser – your migrated site should load!


5. Optimize for High Performance

A. Optimize PHP Settings

# Find your PHP version
php -v

# Edit PHP configuration (adjust version number)
nano /etc/php/8.3/apache2/php.ini

Update these values:

memory_limit = 512M
max_execution_time = 300
max_input_time = 300
post_max_size = 128M
upload_max_filesize = 128M
max_input_vars = 3000

; OPcache - Critical for performance
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.fast_shutdown=1
opcache.enable_cli=0

B. Optimize Apache (MPM Prefork)

# Enable MPM Prefork
a2dismod mpm_event mpm_worker 2>/dev/null
a2enmod mpm_prefork

# Edit configuration
nano /etc/apache2/mods-available/mpm_prefork.conf

For 4GB RAM / 3 CPUs:

<IfModule mpm_prefork_module>
    StartServers             5
    MinSpareServers          5
    MaxSpareServers          10
    MaxRequestWorkers        150
    MaxConnectionsPerChild   3000
</IfModule>

Calculation guide:

  • Each Apache process ≈ 50-80MB
  • MaxRequestWorkers = (Total RAM – 1GB for system) / 80MB
  • For 4GB RAM: (4096 – 1024) / 80 = ~38 safe concurrent workers
  • Set MaxRequestWorkers higher (150) for burst capacity

C. Enable Apache Performance Modules

# Enable essential modules
a2enmod deflate      # Compression
a2enmod expires      # Browser caching
a2enmod headers      # HTTP headers control
a2enmod rewrite      # URL rewriting

# Restart Apache
systemctl restart apache2

D. Configure Apache Virtual Host

nano /etc/apache2/sites-available/000-default.conf

Add inside <VirtualHost *:80>:

<VirtualHost *:80>
    ServerName yourdomain.com
    DocumentRoot /var/www/html
    
    # Compression
    <IfModule mod_deflate.c>
        AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
        AddOutputFilterByType DEFLATE text/javascript application/javascript application/json
        AddOutputFilterByType DEFLATE application/xml application/xhtml+xml
    </IfModule>
    
    # Browser caching
    <IfModule mod_expires.c>
        ExpiresActive On
        ExpiresByType image/jpg "access plus 1 year"
        ExpiresByType image/jpeg "access plus 1 year"
        ExpiresByType image/gif "access plus 1 year"
        ExpiresByType image/png "access plus 1 year"
        ExpiresByType image/webp "access plus 1 year"
        ExpiresByType image/svg+xml "access plus 1 year"
        ExpiresByType text/css "access plus 1 month"
        ExpiresByType application/javascript "access plus 1 month"
        ExpiresByType application/pdf "access plus 1 month"
        ExpiresByType text/javascript "access plus 1 month"
    </IfModule>
    
    # Security headers
    <IfModule mod_headers.c>
        Header set X-Content-Type-Options "nosniff"
        Header set X-Frame-Options "SAMEORIGIN"
        Header set X-XSS-Protection "1; mode=block"
    </IfModule>
    
    # WordPress permalinks
    <Directory /var/www/html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
systemctl restart apache2

E. Optimize MySQL/MariaDB

nano /etc/mysql/mysql.conf.d/mysqld.cnf

Add in [mysqld] section:

For 4GB RAM VPS:

[mysqld]
# InnoDB Settings (50-60% of RAM for MySQL)
innodb_buffer_pool_size = 2G
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1

# Connection Settings
max_connections = 200
thread_cache_size = 50
table_open_cache = 4000
table_definition_cache = 2000

# Memory Tables
tmp_table_size = 128M
max_heap_table_size = 128M

# Query Optimization
join_buffer_size = 4M
sort_buffer_size = 4M
read_rnd_buffer_size = 2M

# Logging (disable in production for performance)
slow_query_log = 0
# slow_query_log_file = /var/log/mysql/mysql-slow.log
# long_query_time = 2

For 2GB RAM VPS:

innodb_buffer_pool_size = 1G
tmp_table_size = 64M
max_heap_table_size = 64M
# Restart MySQL
systemctl restart mysql

# Verify settings
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"

F. WordPress-Specific Optimizations

Edit wp-config.php:

nano /var/www/html/wp-config.php

Add before /* That's all, stop editing! */:

// Memory limits
define('WP_MEMORY_LIMIT', '512M');
define('WP_MAX_MEMORY_LIMIT', '512M');

// Enable caching
define('WP_CACHE', true);

// Disable post revisions (or limit them)
define('WP_POST_REVISIONS', 3);

// Set autosave interval (in seconds)
define('AUTOSAVE_INTERVAL', 300);

// Disable file editing from dashboard
define('DISALLOW_FILE_EDIT', true);

// Increase timeout for external requests
define('WP_HTTP_BLOCK_EXTERNAL', false);

// Optimize database queries
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);

G. Install Redis Object Cache (Optional but Recommended)

# Install Redis
apt install redis-server php-redis -y

# Configure Redis
nano /etc/redis/redis.conf

Update these settings:

maxmemory 512mb
maxmemory-policy allkeys-lru
supervised systemd
# Start Redis
systemctl enable redis-server
systemctl start redis-server

# Restart PHP/Apache
systemctl restart apache2

Install Redis Object Cache Plugin:

  1. WordPress Admin → Plugins → Add New
  2. Search “Redis Object Cache”
  3. Install and Activate
  4. Settings → Redis → Enable Object Cache

H. Install Caching Plugin

Option 1: WP Super Cache (Free, Simple)

  1. Plugins → Add New → Search “WP Super Cache”
  2. Install & Activate
  3. Settings → WP Super Cache
  4. “Caching On” → Update Status
  5. Go to Advanced tab
  6. Enable: “Compress pages”, “Cache rebuild”

Option 2: W3 Total Cache (Free, Advanced)

  1. Plugins → Add New → Search “W3 Total Cache”
  2. Install & Activate
  3. Performance → General Settings
  4. Enable:
    • Page Cache (Disk: Enhanced)
    • Browser Cache
    • Object Cache (Redis if installed)
  5. Performance → Page Cache → Cache front page

Option 3: WP Rocket (Paid, Best Performance)

  • Premium plugin with one-click optimization
  • Lazy loading, database cleanup, CDN integration

I. Image Optimization

Install Optimization Plugin:

  • ShortPixel (Free tier available)
  • Imagify (Free tier available)
  • Smush (Free)

Bulk optimize existing images:

  1. Install plugin
  2. Go to plugin settings
  3. Run bulk optimization on Media Library

6. Point Domain to VPS

A. Update DNS Records

Log into Domain Registrar:

  1. Find DNS Management / Name Servers section
  2. Update A Records:Type: A Name: @ Value: your_vps_ip TTL: 3600
  3. Add WWW subdomain:Type: A Name: www Value: your_vps_ip TTL: 3600
  4. Save changes

DNS Propagation:

B. Update WordPress URLs

Once DNS propagates:

mysql -u wordpress_user -p wordpress_db

UPDATE wp_options SET option_value = 'https://yourdomain.com' WHERE option_name = 'siteurl';
UPDATE wp_options SET option_value = 'https://yourdomain.com' WHERE option_name = 'home';
EXIT;

Or in WordPress Admin:

  • Settings → General
  • Update both URL fields
  • Save Changes

C. Install SSL Certificate (Let’s Encrypt)

# Install Certbot
apt install certbot python3-certbot-apache -y

# Get SSL certificate
certbot --apache -d yourdomain.com -d www.yourdomain.com

# Follow prompts:
# - Enter email address
# - Agree to terms
# - Choose redirect HTTP to HTTPS: Yes (Option 2)

# Test auto-renewal
certbot renew --dry-run

SSL auto-renewal is configured automatically!

D. Force HTTPS in WordPress

Add to .htaccess (after # BEGIN WordPress):

# Force HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Or in wp-config.php:

define('FORCE_SSL_ADMIN', true);
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
    $_SERVER['HTTPS']='on';

7. Final Steps & Monitoring

A. Clean Up

# Remove backup files from VPS
rm /root/database-backup.sql
rm /root/wordpress-backup.zip

# Clear any temporary files
apt autoremove -y
apt clean

B. Install Monitoring Tools

# Install htop for resource monitoring
apt install htop -y

# To use: just type 'htop' (F10 to exit)

# Install netdata for web-based monitoring (optional)
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
# Access at: http://your_vps_ip:19999

C. Set Up Automated Backups

Install UpdraftPlus:

  1. Plugins → Add New → “UpdraftPlus”
  2. Install & Activate
  3. Settings → UpdraftPlus Backups
  4. Settings tab → Schedule backups
  5. Files: Weekly, Database: Daily
  6. Choose remote storage (Dropbox, Google Drive, etc.)

Or use VPS-level backups:

# Create backup script
nano /root/backup-wordpress.sh
#!/bin/bash
BACKUP_DIR="/root/backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR

# Backup files
tar -czf $BACKUP_DIR/wordpress_$DATE.tar.gz /var/www/html

# Backup database
mysqldump -u wordpress_user -pYOUR_PASSWORD wordpress_db > $BACKUP_DIR/db_$DATE.sql

# Keep only last 7 days
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete

echo "Backup completed: $DATE"
# Make executable
chmod +x /root/backup-wordpress.sh

# Add to crontab (daily at 2 AM)
crontab -e

Add line:

0 2 * * * /root/backup-wordpress.sh >> /var/log/wordpress-backup.log 2>&1

D. Security Hardening

Install Security Plugin:

  • Wordfence Security (Free)
  • Sucuri Security (Free)
  • iThemes Security (Free)

Basic Security Measures:

  1. Change default admin username (not “admin”)
  2. Use strong passwords (20+ characters)
  3. Limit login attempts (via security plugin)
  4. Disable XML-RPC if not needed:// Add to functions.php add_filter('xmlrpc_enabled', '__return_false');
  5. Hide WordPress version:// Add to functions.php remove_action('wp_head', 'wp_generator');
  6. Configure firewall (UFW):apt install ufw -y ufw allow 22/tcp # SSH ufw allow 80/tcp # HTTP ufw allow 443/tcp # HTTPS ufw enable ufw status

E. Performance Testing

Test your site speed:

  1. GTmetrix:https://gtmetrix.com
    • Enter your domain
    • Check Performance Score
    • Review recommendations
  2. Google PageSpeed Insights:https://pagespeed.web.dev
    • Test mobile and desktop
    • Aim for 90+ score
  3. Load Testing:# Install Apache Bench apt install apache2-utils -y # Test with 100 requests, 10 concurrent ab -n 100 -c 10 https://yourdomain.com/

Expected results after optimization:

  • Page load time: < 2 seconds
  • Time to First Byte (TTFB): < 600ms
  • PageSpeed score: 85-95+

F. Monitor Resource Usage

# Check Apache processes
ps aux | grep apache2 | wc -l

# Check memory usage
free -h

# Check disk usage
df -h

# Check MySQL processes
mysqladmin -u root -p processlist

# Real-time monitoring
htop

Troubleshooting Common Issues

Issue 1: Site shows “Error establishing database connection”

Solution:

# Check MySQL is running
systemctl status mysql

# Verify database credentials in wp-config.php
nano /var/www/html/wp-config.php

# Test database connection
mysql -u wordpress_user -p wordpress_db

Issue 2: Apache won’t start after changes

Solution:

# Check Apache configuration
apache2ctl configtest

# View error logs
tail -f /var/log/apache2/error.log

# Restart Apache
systemctl restart apache2

Issue 3: 500 Internal Server Error

Solution:

# Check .htaccess file
nano /var/www/html/.htaccess

# Check PHP error logs
tail -f /var/log/apache2/error.log

# Check file permissions
ls -la /var/www/html/

Issue 4: Images not loading after migration

Solution:

# Fix file permissions
cd /var/www/html
find wp-content/uploads -type d -exec chmod 755 {} \;
find wp-content/uploads -type f -exec chmod 644 {} \;
chown -R www-data:www-data wp-content/uploads

# Update image URLs in database
mysql -u wordpress_user -p wordpress_db

UPDATE wp_posts SET post_content = REPLACE(post_content, 'old-domain.com', 'new-domain.com');
UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, 'old-domain.com', 'new-domain.com');

Issue 5: Slow site after migration

Solution:

  1. Clear all caches (caching plugin + browser)
  2. Regenerate thumbnails (use plugin)
  3. Check MySQL slow query log
  4. Disable plugins one by one to find culprit
  5. Run database optimization:mysql -u wordpress_user -p wordpress_db OPTIMIZE TABLE wp_posts; OPTIMIZE TABLE wp_postmeta; OPTIMIZE TABLE wp_options;

Performance Benchmarks

Before Optimization (Typical Shared Hosting):

  • Page Load: 4-8 seconds
  • TTFB: 1-3 seconds
  • Concurrent users: 10-20
  • PageSpeed Score: 40-60

After Optimization (Optimized VPS):

  • Page Load: 1-2 seconds
  • TTFB: 200-600ms
  • Concurrent users: 100-500+
  • PageSpeed Score: 85-95+

Resource Usage (4GB VPS):

  • Idle: 500MB RAM, 5% CPU
  • Medium Load (50 users): 1.5GB RAM, 30% CPU
  • High Load (200 users): 3GB RAM, 70% CPU

Leave a Comment