Back to tutorials
Tutorial

VPS Migration Tutorial (2026): Rsync + DNS TTL Cutover for Low‑Downtime Website Moves

VPS migration tutorial for low‑downtime moves using rsync, staged syncs, DNS TTL planning, and rollback checks in 2026.

By Anurag Singh
Updated on Jun 21, 2026
Category: Tutorial
Share article
VPS Migration Tutorial (2026): Rsync + DNS TTL Cutover for Low‑Downtime Website Moves

A smooth migration comes down to discipline. Copy the right data, keep it consistent, and don’t flip DNS until you’ve proven the new VPS behaves as expected. This VPS migration tutorial shows an rsync-based approach with staged syncs, sensible TTL timing, and a rollback option you can actually execute.

The workflow fits common stacks (Nginx/Apache + PHP-FPM + MariaDB/MySQL, plus WordPress). It also works well for “multi-site” VPS setups with several virtual hosts on one server.

What you’ll migrate (and what you should not)

Before you touch DNS, decide what must match on the destination VPS and what can safely change. Most “bad migrations” aren’t mysterious. They usually come from missing cron jobs, broken ownership, or different PHP extensions.

  • Website files: document roots, uploads, custom code, .env files, wp-config.php
  • Web server config: Nginx server blocks, Apache vhosts, SSL vhosts, includes
  • App runtime: PHP version, PHP-FPM pools, extensions, OPCache settings
  • Database: dump/restore or replication-based cutover (we’ll use dump/restore here)
  • System glue: cron, systemd services, logrotate snippets, firewall rules, Fail2Ban (if used)

What you usually shouldn’t copy wholesale includes your entire /etc, old cache directories, and security-sensitive host keys. Only copy those when you have a clear reason and a plan.

Pre-flight checklist (do this first)

Run these checks on the source VPS and save the output. You’ll use it to confirm the new server matches the old runtime.

  • OS: cat /etc/os-release
  • Web server: nginx -v or apache2 -v
  • PHP: php -v and php -m
  • Disk usage: df -h and du -sh /var/www (adjust to your paths)
  • Databases: list DBs and sizes; know your biggest tables
  • Outbound email: if the server sends mail, confirm rDNS/PTR and SPF/DKIM plans

If email deliverability matters, plan it now. Moving sites is straightforward. Moving “mail reputation” isn’t.

If you’re sending from the VPS, review rDNS/PTR setup and keep your DNS authentication records aligned.

VPS migration tutorial: the rsync + staged sync method

This approach keeps downtime low by syncing files twice. First, do a bulk copy while the site stays live. Then run a quick delta sync right before cutover.

The database moves via export/import during a short maintenance window.

  1. Provision the destination VPS and harden access
  2. Install the same stack versions (or knowingly compatible versions)
  3. Initial rsync of web roots and configs
  4. Initial database dump/restore
  5. Test on a temporary hostname or hosts-file override
  6. Lower DNS TTL, schedule maintenance, freeze writes
  7. Final rsync + final DB sync
  8. DNS cutover + verification + rollback window

Step 1: Provision the destination VPS with safe access

For migrations, size the destination a bit larger than “minimum viable.” Extra CPU and RAM reduces guesswork when you’re chasing timeouts or tuning PHP workers.

If you want predictable performance and root-level control, start with a HostMyCode VPS.

If you don’t want to juggle patching, firewall rules, and service restarts during the move, use managed VPS hosting and keep the cutover calmer.

On the destination VPS, set up SSH keys and disable password logins. If you want a step-by-step, follow this SSH key setup tutorial before continuing.

Step 2: Match your web stack (versions matter)

Don’t jump to a different PHP major version just because it’s newer. If you haven’t tested the app, this is where “low downtime” plans turn into long nights.

Quick parity commands:

  • Ubuntu/Debian packages: apt-cache policy nginx php-fpm
  • Service status: systemctl status nginx, systemctl status php8.3-fpm (example)

Bring over only what you need. For Nginx, that typically means:

  • /etc/nginx/nginx.conf
  • /etc/nginx/sites-available/ and /etc/nginx/sites-enabled/
  • /etc/nginx/snippets/ (if used)

If you run Apache, you’ll usually migrate:

  • /etc/apache2/sites-available/ and /etc/apache2/sites-enabled/
  • /etc/apache2/conf-available/ and /etc/apache2/conf-enabled/

Step 3: Prepare rsync and a migration user

Rsync is fast, predictable, and safe to repeat. On the destination, you can create a dedicated user to receive the data. This is optional, but it keeps things tidy.

sudo adduser migrate
sudo usermod -aG www-data migrate

On the source VPS, confirm rsync is installed:

rsync --version

If you need to copy over SSH on a non-standard port:

rsync -e "ssh -p 2222" ...

Step 4: Initial file sync (live site, no downtime)

Use your real web roots. Common locations include /var/www or /home/USERNAME/public_html in panel-based setups.

Example: sync two common paths to the destination. Run these from the source server:

# 1) Website files
sudo rsync -aHAX --numeric-ids --delete --info=progress2 \
  /var/www/ \
  migrate@DEST_IP:/var/www/

# 2) Web server config (Nginx example)
sudo rsync -aHAX --numeric-ids \
  /etc/nginx/ \
  migrate@DEST_IP:/etc/nginx/

Flags explained (briefly):

  • -a preserves permissions/times; -HAX keeps hardlinks, ACLs, xattrs where relevant
  • --numeric-ids avoids user/group name mismatches between servers
  • --delete helps keep the destination clean (use carefully)

Pitfall: don’t rsync private keys from /etc/letsencrypt on autopilot if you plan to re-issue certificates after cutover.

If you do copy them, lock down permissions and limit access.

Step 5: Database export/import with a controlled window

For most VPS workloads, dump/restore is the simplest cutover. Do an initial import early. Then repeat a final dump during a short maintenance window.

On the source VPS (MySQL/MariaDB), dump a single database:

mysqldump --single-transaction --routines --triggers \
  -u DBUSER -p DBNAME | gzip > /root/DBNAME.sql.gz

Copy it to the destination:

scp /root/DBNAME.sql.gz migrate@DEST_IP:/tmp/

On the destination, create the database and import:

mysql -u root -p -e "CREATE DATABASE DBNAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

gunzip -c /tmp/DBNAME.sql.gz | mysql -u DBUSER -p DBNAME

If this is WordPress and you’re not sure about utf8mb4 settings, keep them aligned with the source.

Mismatches often show up later as broken emoji, truncated indexes, or collation errors.

Step 6: Test the site on the new VPS before touching DNS

Don’t cut over and “see what happens.” Validate the site end-to-end while you still have time to fix issues.

Three practical options:

  • Hosts file override on your laptop: map the domain to the destination IP
  • Temporary subdomain like staging.example.com pointing to the destination
  • Direct IP test (least reliable if your app depends on the Host header and SSL)

For hosts-file testing (macOS/Linux):

sudo nano /etc/hosts
# Add:
DEST_IP   example.com www.example.com

Then validate:

  • Homepage loads with correct CSS/JS
  • Login works
  • Forms submit
  • Search works (if your CMS uses it)
  • Permalinks and redirects behave

If TLS blocks testing, fix it before you go any further.

If Let’s Encrypt renewals fail on the new host, use this SSL renewal troubleshooting guide to resolve the usual causes (bad DNS, port 80 blocked, wrong webroot, or broken challenge routing).

Step 7: Plan the DNS cutover with TTL and rollback in mind

DNS is where “low downtime” becomes real—or falls apart. You want a short TTL before the switch so caches refresh quickly.

You also don’t want it so short that you create noisy queries days ahead of time.

Typical plan for a production domain:

  • 24–48 hours before: lower A/AAAA TTL to 300 seconds
  • Cutover day: switch A/AAAA to the destination IP
  • After stable: raise TTL back to 1800–3600 seconds

If you want a more detailed TTL + verification flow, follow this DNS propagation tutorial.

It includes rollback checks for resolvers that cling to old answers longer than they should.

Step 8: Short maintenance window: freeze writes, final sync, cut over

This is the only part that needs a real window. Schedule it for low traffic.

Then put the site into maintenance mode so you don’t create “split” data during the final database export.

WordPress quick freeze options:

  • Enable a maintenance plugin, or
  • Restrict wp-admin and login temporarily, or
  • As a blunt tool: stop PHP-FPM briefly (not ideal if you host multiple sites)

Run a final file sync from the source:

sudo rsync -aHAX --numeric-ids --delete \
  /var/www/ \
  migrate@DEST_IP:/var/www/

Then do the final DB dump and import. If you can tolerate a few minutes of downtime, repeat the same dump command and import it again on the destination.

You can overwrite the DB after dropping/recreating it. Or restore into the same DB if you know what you’re doing.

Once the destination is ready, switch DNS A/AAAA to the new IP. Keep the old VPS online for at least 24–72 hours so you have a real rollback path.

Step 9: Verify with real diagnostics (not just a browser refresh)

After the DNS change, check from more than one network (for example, mobile data and office broadband). Use CLI tools so you can see what DNS is actually returning.

DNS checks:

dig +short A example.com
dig +short AAAA example.com

# Check a specific resolver
dig @1.1.1.1 +short A example.com
dig @8.8.8.8 +short A example.com

HTTP checks:

curl -I https://example.com
curl -s https://example.com | head

Log checks on the destination:

  • Nginx access/error: /var/log/nginx/access.log, /var/log/nginx/error.log
  • Apache: /var/log/apache2/access.log, /var/log/apache2/error.log
  • PHP-FPM: often /var/log/php8.3-fpm.log or pool logs (varies by distro)

If the site feels slower after migration, measure before you change things.

Use this slow response troubleshooting tutorial to track where the latency moved.

Step 10: Email and SSL checks after migration (common “gotchas”)

Two problems tend to show up later. Outbound mail starts landing in spam. SSL renewals fail quietly until the next renewal date.

Post-migration email checks:

  • Confirm the sending IP matches SPF (or adjust SPF)
  • Confirm DKIM still signs correctly
  • Verify PTR/rDNS for the new IP if you send mail from the VPS

For hands-on DKIM setup, use HostMyCode’s DKIM setup tutorial.

If you hit reverse DNS mismatches, this PTR troubleshooting guide helps you spot them quickly.

Migration hardening pass: a short checklist

Once traffic is landing on the new VPS, do a quick hardening sweep. Keep it focused.

Cover the basics, confirm stability, then move on.

  • Patch OS packages: sudo apt update && sudo apt -y upgrade
  • Confirm firewall allows only needed ports (typically 22, 80, 443)
  • Rotate app secrets if they were exposed during the move
  • Enable 2FA for admin panels (if you use one)
  • Set up monitoring and disk alerts

If you don’t already have monitoring, add it right after cutover.

Netdata + alerts is a practical baseline for most hosting servers.

Rollback plan (keep it simple)

A rollback is usually just DNS pointing back to the old IP. The real question is what happened to writes during the cutover window.

You also need to decide which server is authoritative.

  • Keep old VPS online for 72 hours
  • Don’t delete old data immediately
  • Document which server is currently accepting writes
  • If you must roll back, switch DNS A/AAAA back and re-enable the old site

For busy e-commerce sites, order consistency is the tricky part. That usually means a stricter freeze window or application-level queuing.

If you’re unsure, choose a longer maintenance window rather than risking split-brain writes.

Summary: the migration flow you can repeat

The rsync + staged sync approach is intentionally boring. Copy files early and verify on the destination.

Freeze writes briefly, run a final delta sync, and cut over DNS with a rollback plan ready.

If you want the migration to stay predictable under load, run it on a HostMyCode VPS sized with headroom.

If you’d rather hand off the server-side prep and post-cutover checks, HostMyCode migrations helps avoid the classic “it worked on the old box” surprises.

If you’re moving a production website and can’t afford an extended outage, use a VPS that gives you consistent CPU, dedicated RAM, and full root control. Start with HostMyCode VPS, or choose managed VPS hosting if you want help with the stack, security, and post-cutover validation.

FAQ

How much downtime should I expect with this VPS migration tutorial?

If you stage the file sync and only freeze writes for the final database dump/import, typical downtime is 2–15 minutes for small to medium sites. Very large databases can take longer.

Should I migrate SSL certificates or re-issue them?

For most setups, re-issuing on the destination is cleaner, especially if you’re also changing web server configuration. If you copy /etc/letsencrypt, keep permissions intact and validate renewals immediately.

What if I have multiple domains on the same VPS?

Rsync the parent web directory (for example /var/www), migrate each vhost config, and keep per-site PHP-FPM pools consistent. Cut over DNS per domain to reduce blast radius.

Can I do this from shared hosting to a VPS?

Yes, but you may not have root access on shared hosting to run rsync and database tools the same way. In that case, export via the hosting panel and use SFTP on the VPS as the transport. If you’re unsure, using a managed migration service is often faster than piecing it together.

What’s the fastest way to verify DNS is actually pointing to the new server?

Use dig against multiple resolvers (1.1.1.1, 8.8.8.8) and confirm the A/AAAA answers. Then check your destination web logs to see real requests arriving.

VPS Migration Tutorial (2026): Rsync + DNS TTL Cutover for Low‑Downtime Website Moves | HostMyCode