How to Migrate a WordPress Site from Shared Hosting to a VPS

WordPress blue logo

Moving a WordPress site from shared hosting to a VPS sounds straightforward until you hit the usual problems: broken media, mixed URLs, plugin issues, bad file permissions, or a DNS cutover that happens before the new box is actually ready.

The good news is that this kind of migration is very manageable if you treat it as an infrastructure change rather than a quick copy-and-paste job.

This guide walks through a practical way to migrate a WordPress site from a shared host to a self-managed VPS using Nginx, PHP-FPM, MariaDB, and UpdraftPlus. It is based on a real migration, but all identifying details, addresses, credentials, hostnames, and internal environment data have been removed.

Quick Answer

To migrate WordPress from shared hosting to a VPS with minimal downtime:

  1. Build and harden the VPS first.
  2. Install your web stack and deploy a clean WordPress install.
  3. Create a full backup of the live site.
  4. Restore that backup onto the VPS.
  5. Fix URLs, ownership, and permissions.
  6. Test the migrated site locally before changing DNS.
  7. Cut DNS only after the new site is fully verified.
  8. Finish with cron, backups, monitoring, and cleanup.

If you do those steps in that order, the migration is usually predictable.

What This Guide Assumes

This procedure assumes:

  • Your current WordPress site lives on shared hosting. My original server was hosted on cPanel.
  • The new server is a Linux VPS. Mine is hosted at www.atlantic.net
  • You are using Nginx, PHP-FPM, and MariaDB. (LAMP Stack)
  • You are comfortable with SSH, basic Linux administration, and WP-CLI.
  • You can create a full WordPress backup with a plugin such as UpdraftPlus.

You do not need to migrate email if your mail routing is already handled elsewhere.

Step 1: Prepare and Harden the VPS First

Do not start by moving WordPress data.

Start by making sure the VPS is ready to host it.

That means:

  • Updating the system
  • Enabling a firewall
  • Setting up swap if the server is small
  • Installing fail2ban
  • Disabling password-based SSH access
  • Enabling automatic security updates

On a modest VPS, swap can help stop memory pressure from turning into a hard crash when MariaDB, PHP-FPM, and WordPress all compete for RAM.

Example:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

A minimal UFW setup is usually enough to start:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

The point here is simple: build a stable destination before you even think about restoring the site.

Step 2: Install the WordPress Stack

Once the VPS is hardened, install the stack WordPress will run on.

For a lean VPS setup, that usually means:

  • Nginx
  • MariaDB
  • PHP-FPM
  • Required PHP extensions
  • WP-CLI

Your exact PHP package list will vary, but most WordPress migrations will need the usual extensions for MySQL, XML, ZIP, image handling, and multibyte strings.

After that, tune PHP for WordPress restores. The defaults are often too small for real-world backups.

Typical values to review:

  • upload_max_filesize
  • post_max_size
  • memory_limit
  • max_execution_time
  • max_input_vars

If you skip this step, large backup restores often fail for boring reasons.

Step 3: Create a Clean Database and Fresh WordPress Install

Before restoring the old site, create a fresh database and a fresh WordPress install on the VPS.

Example database setup:

CREATE DATABASE wordpress_site CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'replace-with-a-strong-password';
GRANT ALL PRIVILEGES ON wordpress_site.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;

Then deploy WordPress to your new document root:

SITE_DOMAIN="example.com"
SITE_ROOT="/var/www/$SITE_DOMAIN"sudo mkdir -p "$SITE_ROOT"
sudo chown -R "$USER":"$USER" "$SITE_ROOT"
cd "$SITE_ROOT"wp core download
wp config create \
  --dbname=wordpress_site \
  --dbuser=wp_user \
  --dbpass='replace-with-a-strong-password'wp core install \
  --url="https://$SITE_DOMAIN" \
  --title="Example Site" \
  --admin_user="adminuser" \
  --admin_password="replace-with-a-strong-password" \
  --admin_email="[email protected]"sudo chown -R www-data:www-data "$SITE_ROOT"

That clean install gives you a known-good base before you overlay the real content.

Step 4: Configure Nginx Before the Restore

Get your web server working before restoring the backup.

For WordPress on Nginx, the basics usually include:

  • HTTP to HTTPS redirect
  • PHP handling through the PHP-FPM socket
  • try_files for permalinks
  • Blocking access to hidden files
  • Sensible client upload limits
  • Static asset caching

A simplified example looks like this:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}server {
    listen 443 ssl http2;
    server_name example.com www.example.com;    root /var/www/example.com;
    index index.php index.html;    client_max_body_size 64M;    location / {
        try_files $uri $uri/ /index.php?$args;
    }    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }    location ~ /\.(ht|git) {
        deny all;
    }    location = /xmlrpc.php {
        deny all;
    }
}

You can use a temporary certificate for testing, but do not forget to replace it before public cutover.

If you use Cloudflare or another reverse proxy, finish that SSL configuration before switching traffic.

Step 5: Take a Full Backup of the Live Site

Now create a fresh backup from the live site.

If you are using UpdraftPlus, take a full backup that includes:

  • Database
  • Plugins
  • Themes
  • Uploads
  • Other WordPress files

Download the backup files and transfer them to the VPS.

If the backup is large, expect uploads to be split into multiple archive files. That is normal.

Place the backup where your restore plugin can see it. For UpdraftPlus, that is usually inside the wp-content/updraft directory on the new site.

Step 6: Restore the Site on the VPS

This is the stage where migrations usually stop being “easy.”

Elsewhere On TurboGeek:  Migrate WordPress Site to Another Server

A practical approach is:

  1. Temporarily make the new site reachable for admin access.
  2. Log in to the fresh WordPress install on the VPS.
  3. Rescan the backup location.
  4. Restore all components.
  5. Let the restore complete before changing anything else.

If the restore fails, the usual causes are:

  • PHP limits are too low
  • File ownership is wrong
  • The restore directory is not writable
  • A plugin restore hits a timeout

One common issue is a root-owned directory inside wp-content blocking the restore process.

If that happens, fix ownership first:

sudo chown -R www-data:www-data /var/www/example.com/wp-content

Then rerun the restore.

Step 7: Fix URLs and Permissions After Restore

Even when a restore succeeds, the site may not be fully restored.

The two things I always check next are:

  • URL values
  • File ownership and permissions

If the site was temporarily restored using a different URL or direct server address, run a search-replace to correct internal links and stored references.

Example:

cd /var/www/example.comwp search-replace 'https://temporary-test-url.example' 'https://www.example.com' --all-tables

Then reset file ownership and permissions:

sudo chown -R www-data:www-data /var/www/example.com
sudo find /var/www/example.com -type d -exec chmod 755 {} \;
sudo find /var/www/example.com -type f -exec chmod 644 {} \;

This step matters more than people think. A migration can look fine on the homepage and still be quietly broken in uploads, plugin updates, or theme editing because ownership is wrong.

Step 8: Test the Site Before DNS Cutover

Do not flip DNS yet.

Test the migrated site first from your own machine by mapping the domain to the VPS in your local hosts file.

Example entry:

203.0.113.10 example.com www.example.com

Then check the site in a browser and in WP-Admin.

My minimum checklist would be:

  • Homepage loads correctly
  • Menus and navigation work
  • Several posts render properly
  • Images and media load
  • Plugin-driven content still works
  • SEO metadata is intact
  • The admin dashboard is responsive
  • You can create and save a draft post
  • The REST API responds normally

If the site uses custom blocks, gallery plugins, syntax highlighters, analytics plugins, or anything theme-specific, test those explicitly.

The safe time to change DNS is after testing, not after hope.

Step 9: Perform the DNS Cutover

Once the migrated site is working, do the cutover.

If you are behind Cloudflare or another proxy, this usually means updating the DNS records for the apex domain and www to point at the new VPS.

Before that, make sure:

  • The correct certificate is installed
  • The proxy SSL mode matches your origin setup
  • Any final content changes from the old host have been synced

If new posts or comments went live after your first backup, perform a final database-only sync before pointing traffic to the VPS.

That final sync keeps the content up to date without overwriting your server-specific configuration.

Step 10: Verify the Site Immediately After Cutover

Once DNS is changed, verify everything straight away.

Useful checks include:

curl -I https://www.example.com
curl -s https://www.example.com/wp-json/wp/v2/posts?per_page=1
curl -w "TTFB: %{time_starttransfer}s\n" -o /dev/null -s https://www.example.com

Then test the site manually in a browser.

Look for:

  • Correct SSL behaviour
  • No mixed content warnings
  • Working images and media
  • Fast page load times
  • Working login page
  • No obvious plugin failures

If you enabled any caching at the Nginx or application level, confirm it is not caching admin pages or logged-in sessions.

Step 11: Finish the Hardening After Migration

A WordPress migration is not finished just because the homepage loads.

Once traffic is on the VPS, clean up the operational side:

Disable WP-Cron and Use Real Cron

WordPress cron is page-load driven. On a VPS, a real system cron job is more reliable.

Add this to wp-config.php:

define('DISABLE_WP_CRON', true);

Then create a cron job:

*/5 * * * * /usr/bin/php8.3 /var/www/example.com/wp-cron.php > /dev/null 2>&1

Configure Proper Backups

Set up scheduled backups immediately.

A good baseline is:

  • Daily database backups
  • Weekly full backups
  • Remote backup storage
  • Retention rules you actually review

Do not rely on the shared host’s old backups once the site has moved.

Watch Logs for the First 48 Hours

Keep an eye on:

  • Nginx error logs
  • PHP-FPM logs
  • WordPress debug logs if you temporarily enable them

A migration can look clean at first and still reveal issues once real users start hitting search, forms, media, and plugin-heavy pages.

Common Mistakes That Break WordPress Migrations

These are the failure points I see most often:

Changing DNS Too Early

If you flip traffic before testing, you are debugging live.

That is avoidable.

Forgetting Search-and-Replace

If WordPress still holds the old URL internally, media paths, canonical tags, and plugin data can all behave oddly.

Bad File Ownership

Wrong ownership breaks restores, uploads, updates, and sometimes cache directories.

Incomplete SSL Setup

A migration behind a proxy still requires the origin to be configured correctly.

No Final Content Sync

If the live site changed after the first backup, you need one last catch-up before cutover.

Final Thoughts

A WordPress migration from shared hosting to a VPS is not complicated because WordPress is mysterious. It is complicated because there are several moving parts, and small mistakes tend to show up late.

The safest pattern is:

  • Build the new stack first
  • Restore to a controlled destination
  • Fix URLs and permissions
  • Test locally
  • Cut DNS last
  • Harden after the move

That sequence removes most of the drama.

If you treat the migration as an infrastructure change rather than a “website task,” you will usually end up with a cleaner, faster, and more supportable WordPress setup on the other side.

Want more of this kind of guide?

Use the blog and category routes to keep moving through the archive, or support TurboGeek if the site saves you time regularly.

Translate »