You have already decided that shared hosting is not enough. Maybe you read our breakdown of when a VPS becomes necessary, or you hit the wall yourself: a long-running process that will not stay alive, a background queue that shared hosting cannot support, root access you genuinely need.

The decision is made. The question now is how to get your live site onto a VPS without taking it offline. The answer is straightforward, but it requires doing things in the right order. Skip a step and you risk a window where visitors see an error page, or worse, a stale version of your site that silently serves old content for hours.

This guide covers the full sequence from inventory through cutover for a typical web application: files, a database, DNS, SSL, and a rollback plan.

If you are moving between two VPS providers rather than from shared hosting, the process is similar but the source environment is different. The VPS-to-VPS migration guide covers that scenario specifically.

Take a Full Inventory First

Before touching the VPS, document everything that runs on your current host. Migrations fail most often because something was missed in this step, not because the transfer itself went wrong.

Work through this checklist:

  1. Website files. Where do they live? Is the document root /home/user/public_html, or something else? Are there files outside the web root that the application depends on (config files, .env files, upload directories)?
  2. Database. MySQL, MariaDB, PostgreSQL? How many databases? Note the database names, usernames, and approximate sizes. If you are on shared hosting, these are usually visible in cPanel or Plesk.
  3. Cron jobs. Does anything run on a schedule? Shared hosting control panels often have a cron manager. Export every entry, including the exact schedule and command.
  4. Email. If the domain's MX records point to the shared host, moving DNS will break email delivery unless you plan for it. This single item derails more migrations than any technical step.
  5. DNS records. Export your full zone file. Your registrar or the hosting control panel should have a list. Note every A, AAAA, CNAME, MX, TXT, and SRV record. You will need to recreate them at the new DNS host if you are switching providers.
  6. SSL certificate details. Are you using the host's auto-SSL, or did you install a certificate manually? On the VPS you will set up Let's Encrypt, so the existing cert is not transferable, but note which domains and subdomains are covered.

Write all of this down in a plain text file. You will refer to it at every subsequent step.

Prepare the VPS in Parallel

The key to a downtime-free migration is running both environments simultaneously until the new one is verified. You are not tearing down the old host and building the new one. You are building the new one alongside it, testing it, then switching traffic.

If you have not set up a VPS before, the first-time setup guide walks through the full process: SSH access, system updates, user creation, and basic hardening. Complete that first. Then come back here.

On the VPS, install and configure the same stack your site needs:

  • Web server. Install Nginx or Apache, whichever your site requires. Configure the virtual host or server block with the correct document root and any rewrite rules your application depends on.
  • Runtime. PHP (with the same version and extensions your site uses), Node.js, Python, or whatever your application needs. Version mismatches cause subtle bugs that are hard to trace after the cutover, so match the major version.
  • Database server. Install the same database engine. Create the database and user with matching names and permissions.
  • SSL. You will want a Let's Encrypt certificate on the VPS. There is a catch: Certbot's default HTTP challenge needs port 80 reachable and DNS already pointing to the VPS. Since your DNS still resolves to the old host, use the DNS challenge method instead: sudo certbot certonly --manual --preferred-challenges dns -d yourdomain.com -d www.yourdomain.com. Certbot will ask you to create a TXT record to prove domain ownership, and it works regardless of where the A record currently points. Alternatively, skip SSL until after the DNS cutover and run certbot --nginx then (you will have a brief window where HTTPS is not available).

One thing that catches people migrating from shared hosting to Nginx on a VPS: Nginx does not read .htaccess files. If your site depends on Apache rewrite rules (WordPress permalink rewrites are the classic example), those rules need to be translated into Nginx location and rewrite directives in your server block configuration. Check the old .htaccess before the cutover and convert anything your application relies on.

Harden the VPS before putting it into production. The security checklist covers the essentials: firewall rules, fail2ban, SSH key authentication, and disabling root login.

Sync Your Data

With the VPS configured and the stack installed, it is time to transfer the actual site data. Two things need to move: files and the database.

Files

rsync is the standard tool for this. It copies only what has changed, which means you can run it multiple times: an initial full copy, then incremental syncs as you get closer to the cutover.

From your local machine or from the shared host (if it provides SSH access):

rsync -avz --progress /path/to/site/ user@YOUR_VPS_IP:/var/www/yoursite/

That trailing slash on the source path matters. rsync /path/to/site/ copies the contents of the directory into the destination. rsync /path/to/site (no slash) copies the directory itself, creating /var/www/yoursite/site/. If your files end up nested one level too deep after the transfer, the trailing slash is usually the reason.

If your shared host does not offer SSH, download the files locally first (via SFTP, cPanel File Manager, or the host's backup tool), then rsync from your machine to the VPS.

After the transfer, verify file ownership and permissions on the VPS:

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

If your application writes to specific directories (upload folders, cache directories), make sure those are writable by the web server user.

Database

Export from the source and import to the VPS. The commands depend on the engine.

MySQL or MariaDB:

# On the source (or locally after downloading)
mysqldump -u dbuser -p dbname > dbname.sql

# On the VPS
mysql -u dbuser -p dbname < dbname.sql

PostgreSQL:

pg_dump -U dbuser -Fc dbname > dbname.dump
pg_restore -U dbuser -d dbname dbname.dump

For databases larger than a few hundred megabytes, the dump and restore can take minutes. During that window, any writes to the source database will not be reflected on the VPS. This is fine for now. You will do a final sync right before the DNS cutover to close the gap.

The most common mistake at this stage: forgetting to update the application's database connection string. Your site's configuration file (.env, wp-config.php, settings.py, or similar) still points to the old database host. Update it to localhost or 127.0.0.1 on the VPS.

Test the VPS Before Touching DNS

Do not skip this. Edit your local machine's hosts file to point your domain to the VPS IP:

# Windows: C:\Windows\System32\drivers\etc\hosts
# macOS/Linux: /etc/hosts
203.0.113.50  yourdomain.com  www.yourdomain.com

Now open https://yourdomain.com in your browser. The request bypasses public DNS and goes straight to the VPS. Walk through every critical page. Test the login flow, form submissions, file uploads, and any dynamic functionality. Check the web server error logs on the VPS for anything unexpected:

sudo tail -f /var/log/nginx/error.log

If you obtained the SSL certificate via the DNS challenge earlier, HTTPS should work normally during this test. If you skipped SSL and plan to run Certbot after the cutover, test over HTTP for now and note any mixed-content issues that will need fixing once HTTPS is live.

Only proceed to DNS cutover after the site works correctly through the hosts file test. Remove the hosts entries afterward.

Lower the DNS TTL (Do This Early)

This step should happen 24 to 48 hours before your planned cutover, not the day of the switch.

Log into your DNS provider and lower the TTL on your domain's A record (and AAAA record if applicable) to 300 seconds (5 minutes). The existing TTL might be 3600 (one hour) or even 86400 (one day). If you change the A record without lowering the TTL first, some DNS resolvers will continue serving the old IP for the duration of the cached TTL. That means visitors hitting those resolvers will see the old shared hosting site for hours after you think the migration is done.

A low TTL means resolvers will re-check frequently. When you finally update the A record, the propagation window shrinks from hours to minutes.

Do not forget the www subdomain. If www.yourdomain.com has its own A record (rather than a CNAME pointing to the apex), lower its TTL too.

The DNS Cutover

This is the step that actually moves traffic. Everything before this was preparation.

  1. Run a final rsync to catch any files that changed since your last sync.
  2. Run a final database dump and import to capture any writes since the last transfer. For busy sites with constant writes, consider putting the old site into read-only or maintenance mode for the 5 to 10 minutes this takes. For most sites being migrated from shared hosting, traffic is low enough that the gap is negligible.
  3. Update the A record for your domain (and www if applicable) to the VPS IP address.
  4. Wait. DNS propagation with a 300-second TTL typically completes within 5 to 15 minutes for most resolvers, though some ISP resolvers ignore TTL hints and may take longer.

During propagation, some visitors will hit the old host and some will hit the VPS. Both environments have the same content at this point, so the experience is identical.

Post-Migration Verification

Once DNS has propagated (use a tool like dig yourdomain.com from the VPS itself, or check from a mobile device on a different network), verify:

  • [ ] The site loads correctly over HTTPS
  • [ ] SSL certificate is valid and not a self-signed placeholder
  • [ ] All pages render without errors
  • [ ] Database-driven content (blog posts, user accounts, dynamic pages) displays correctly
  • [ ] Forms submit successfully
  • [ ] Cron jobs are recreated and running on schedule (crontab -l)
  • [ ] Email delivery still works (test by sending to and from) if you changed anything related to MX records
  • [ ] There are no 404s or 500s in the web server error log

Set up a basic backup schedule on the VPS immediately. Your shared host was probably handling backups without you thinking about it. The VPS will not do that unless you configure it.

Keep the Old Host Running

Do not cancel your shared hosting account the moment DNS switches. Keep it active for at least two weeks.

The primary reason is rollback. If something surfaces that you missed during testing (a cron job, a subdomain, a redirect rule nobody documented), you can point DNS back to the old host in under five minutes. You also need the overlap if email was running through the shared hosting provider, because verifying that new mail routing handles every case takes longer than most people expect. Two weeks of overlap costs very little compared to the cost of discovering a problem with no way to revert.

Once you are confident everything works on the VPS, raise the DNS TTL back to a normal value (3600 or higher). Then cancel the shared hosting plan.

What Could Go Wrong (and How to Recover)

Scenario Symptom Fix
Database connection string still points to old host Site loads but shows no dynamic content, or shows an error page Update the DB host in your config file to localhost and restart the web server
File permissions wrong after transfer 403 errors, blank pages, or upload failures Run the chown and chmod commands from the file sync section
PHP version mismatch White screen of death, function-not-found errors, deprecation warnings Check with php -v and install the matching version; sudo apt install php8.x-fpm
DNS still pointing to old host after cutover Cannot reproduce the issue locally (works via hosts file) but visitors report old site Wait longer; check TTL with dig +short yourdomain.com; verify the A record was actually saved
Let's Encrypt certificate not issued Browser shows "connection not secure" warning Run sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com now that DNS points to the VPS
Forgot to migrate cron jobs Scheduled tasks stop running (backup scripts, cache clearing, report generation) Export from old host, add to crontab -e on VPS

For any of these, you can revert DNS to the old host's IP, fix the issue on the VPS, and try again. That is the entire point of keeping the old host alive during the overlap period.

Moving Forward

A migration is only as reliable as the preparation that goes into it. The sequence matters more than any individual step: inventory everything, build the VPS in parallel, sync data, test with a hosts file override, lower the TTL in advance, switch DNS, verify, and keep the old host as a rollback.

If you are still choosing a VPS provider for the target server, browse the provider directory and read user reviews to find one that fits your workload.

Once the migration is done and stable, the VPS becomes your environment to manage. Set up automated backups if you have not already. Keep your SSL certificates renewing on schedule. And stay on top of security maintenance now that you are responsible for the server.