Moving from shared hosting to a VPS is one of the highest-impact upgrades you can make for a growing WordPress site. More control, better performance, and often lower cost for the same resources. But it also means you are now responsible for the server. This guide covers the entire migration from start to finish.

Before you start

Is a VPS right for your site?

A VPS makes sense when:

  • Your shared hosting is slow, especially during traffic spikes
  • You need custom server software (Redis, specific PHP versions, Elasticsearch)
  • You hit arbitrary limits (file count, inodes, cron jobs, concurrent connections)
  • You want to run multiple sites on one server more efficiently
  • Your hosting bill is >$30/month for shared/reseller hosting

A VPS might NOT make sense when:

  • You do not want to manage a server (managed VPS is an option — see below)
  • Your site is static or nearly static (consider static hosting)
  • You are not comfortable with SSH and the Linux command line

Choosing a VPS provider

For a single WordPress site starting out:

ProviderStarting priceRAMCPUNotes
Hetzner€4.50/mo4 GB2 vCPUBest value in Europe
DigitalOcean$6/mo1 GB1 vCPUBest docs, easy interface
Linode$5/mo1 GB1 vCPULong-running, reliable
Vultr$6/mo1 GB1 vCPUMany global locations

For WordPress: minimum 1 GB RAM with Nginx, 2 GB RAM with Apache/cPanel. More RAM gives you room for object caching (Redis) and future growth.

Step 1: Set up the new VPS

# Update and install essentials
apt update && apt upgrade -y
apt install -y nginx mariadb-server php8.3-fpm php8.3-mysql \
  php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip \
  php8.3-imagick redis-server certbot python3-certbot-nginx

# Secure MariaDB
mysql_secure_installation

# Create the site database
mysql -u root -e "CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -u root -e "CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'strong-password';"
mysql -u root -e "GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';"
mysql -u root -e "FLUSH PRIVILEGES;"

Step 2: Configure Nginx and PHP-FPM

# /etc/nginx/sites-available/example.com
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;

    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_read_timeout 300;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt { access_log off; log_not_found off; }

    location ~ /\. {
        deny all;
    }
}
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Step 3: Migrate the files

On the old shared host, create an archive:

# On the old server (via SSH or cPanel File Manager)
cd /home/username/public_html
tar -czf /home/username/site-backup.tar.gz .

Transfer to the new VPS:

# From your local machine or the new VPS
scp username@old-server:/home/username/site-backup.tar.gz /tmp/
# Or if cPanel: download from File Manager, then scp to new VPS

On the new VPS:

mkdir -p /var/www/example.com
cd /var/www/example.com
tar -xzf /tmp/site-backup.tar.gz
chown -R www-data:www-data /var/www/example.com

Step 4: Migrate the database

On the old host:

# Via SSH
mysqldump -u username -p database_name > /home/username/db-backup.sql

# Or via phpMyAdmin: Export → Custom → Add DROP TABLE → Go

Import on the new VPS:

mysql -u root wordpress < /tmp/db-backup.sql

Step 5: Update wp-config.php

// /var/www/example.com/wp-config.php
define('DB_NAME', 'wordpress');
define('DB_USER', 'wpuser');
define('DB_PASSWORD', 'strong-password');
define('DB_HOST', 'localhost');

// Add these for the migration test
define('WP_HOME', 'http://new-server-ip');
define('WP_SITEURL', 'http://new-server-ip');

Step 6: Test before DNS cutover

Access your site via the VPS IP to test everything works before switching DNS. Edit /etc/hosts on your local machine:

123.123.123.123  example.com www.example.com

Clear your browser cache and visit example.com. It will load from the new VPS without any DNS changes.

Things to check:

  • Homepage loads correctly
  • Login to /wp-admin/ works
  • Permalinks work (re-save in Settings → Permalinks)
  • Plugin functionality works
  • Media library images load
  • Contact forms send email

Step 7: DNS cutover

Once tested, update your domain’s A record:

  1. Get the new VPS IP address
  2. Log into your DNS provider (Cloudflare, registrar, etc.)
  3. Update the A record for example.com and www.example.com
  4. Set TTL to 300 (5 minutes) if you plan to test and potentially roll back

Wait for DNS propagation (typically minutes with low TTL, up to 48 hours for old TTL values).

Step 8: SSL certificate

certbot --nginx -d example.com -d www.example.com

Certbot auto-renews via a systemd timer. Verify:

systemctl status certbot.timer

Step 9: Post-migration cleanup

Redirect www to non-www (or vice versa)

# In the server block for the non-preferred domain
server {
    listen 80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

Set up backups (see the backup guide)

Install monitoring (see the monitoring guide)

Remove the test defines from wp-config.php

// Remove or comment out these lines
// define('WP_HOME', 'http://new-server-ip');
// define('WP_SITEURL', 'http://new-server-ip');

What breaks during migration

Email delivery: If your old host handled email (cPanel email, etc.), you need a separate email solution. Options: Google Workspace, MXroute, Migadu, or set up your own mail server.

Absolute URLs in content: Some plugins and themes hardcode the domain in serialized data. Use WP-CLI search-replace:

wp search-replace 'http://old-domain.com' 'https://new-domain.com' --all-tables

Custom PHP configs: Your old host may have had custom php.ini values. Check max_execution_time, upload_max_filesize, post_max_size, and memory_limit on the new server.

Cron jobs: Recreate any cron jobs that were set up on the old host.

Managed VPS: the middle ground

If you want VPS performance without managing the server:

  • Cloudways: Managed cloud VPS on DigitalOcean/Linode/Vultr/AWS. Server management included, ~$14/month for 1 GB.
  • RunCloud: Server management panel for your own VPS (~$8/month + VPS cost).
  • SpinupWP: WordPress-focused server management for your own VPS (~$12/month + VPS cost).

These give you VPS flexibility with someone else handling updates, security, and server configuration.