VPS Cutover Checklist: Inventory, Rehearsal, Rollback Steps cover image
Back to Blog
TechnologyPublished 24 August 2026· Updated 24 August 2026· 6 min read

VPS Cutover Checklist: Inventory, Rehearsal, Rollback Steps

Field notes from Agentic Academy Labs on the VPS cutover checklist we run with startups, including inventory, rehearsal, and rollback steps that prevent 3 AM pages.

The 3 AM Pager That Was Not a Migration Failure

Last month, a 12-person SaaS team in Pune moved off a random VPS that had been running their order pipeline for three years. The cutover looked clean on paper. DNS flipped at 22:00 IST. By 01:47 IST, the pager fired: writes were diverging between the old server and the new one. Orders placed after 23:15 were landing on the old box while the storefront pointed at the new one. The root cause was not a failed migration. It was a skipped rehearsal.

This is the field note we now hand to every intern who touches a VPS cutover. It is not a generic tutorial. It is the inventory, rehearsal, and rollback steps we walk through with startups moving off a random VPS.

What We Tried First (and Why It Failed)

Attempt 1: live rsync while traffic still pointed to the old server

We ran rsync -avz /var/www/ new-vps:/var/www/ while the old server kept serving live traffic. The sync completed, but uploads kept flowing to the old server. When we finally flipped DNS, the new server was missing the last 90 minutes of uploads and the last 47 orders.

Attempt 2: a single final sync without a content freeze

We stopped the web server, ran one final rsync, and flipped DNS. That worked for files. It did not work for the database. MySQL kept accepting writes during the 4-minute sync window. The new server came up with a stale dataset and a broken checkout flow.

The failure mode: uploads and orders landing on two servers

The old server stayed warm. Cron jobs kept running. Background workers kept polling the queue. For 72 minutes, the system had two writers. That is the failure mode we now rehearse against.

The Working Cutover Checklist We Now Run

Pre-T-48h: inventory the old server like a build sheet

Before we copy anything, we capture what the old server is doing. We treat this as a build sheet for the new VPS.

Cron jobs, systemd timers, and background workers
crontab -l > /tmp/old-crontab.txt
systemctl list-timers --all > /tmp/old-timers.txt
ps aux | grep -E 'worker|queue|celery' > /tmp/old-workers.txt
Database writers, connection strings, and extensions
mysql -e "SHOW DATABASES;" > /tmp/old-dbs.txt
mysql -e "SHOW VARIABLES LIKE 'version';" >> /tmp/old-dbs.txt
grep -r 'DB_' /var/www/.env >> /tmp/old-env.txt
Network: IPs, firewall rules, allowlists, and outbound destinations
ip addr show > /tmp/old-ip.txt
ufw status verbose > /tmp/old-firewall.txt
netstat -tulnp | grep LISTEN > /tmp/old-ports.txt
DNS records: A, AAAA, MX, TXT, SRV, and reverse DNS

We dump every record tied to the domain. We check SPF, DKIM, and reverse DNS for mail deliverability.

Security: SSH keys, users, sudo, certificates, and secrets
cat /etc/passwd | grep -v nologin > /tmp/old-users.txt
ls /home/ > /tmp/old-homes.txt
ls /etc/ssh/authorized_keys/ > /tmp/old-keys.txt

T-24h: lower DNS TTL to 300 seconds and verify backups exist

We change TTL at least 24 hours before cutover. Lowering it 10 minutes before does not help if resolvers already cached the old value Hostperl.

T-2h: final sync rehearsal and SSL readiness check

We run a dry-run final sync. We verify the new server can serve over TLS with the real hostname.

T-15min: content freeze and stop write-heavy cron on the old server

systemctl stop mysql
systemctl stop nginx
crontab -l | sed 's/^/#/' | crontab -

T: change DNS A/AAAA records and validate from multiple networks

We flip DNS and validate from at least two networks using curl --resolve.

T+60min: confirm orders, forms, and emails landed on the new server

We check the database for new rows. We send a test order. We verify email delivery.

T+24-72h: raise TTL back to 3600-14400 and decommission old services

We keep the old server live for 72 hours. We raise TTL only after stability is confirmed.

Real Commands and File Paths We Use

rsync flags that preserve permissions, ACLs, and extended attributes

rsync -aHAX --numeric-ids --delete /var/www/ new-vps:/var/www/

The -HAX flags preserve hard links, ACLs, and extended attributes. --numeric-ids prevents UID/GID mapping issues.

Freezing writes with systemctl stop and crontab disabling

systemctl stop mysql nginx php-fpm
crontab -l | sed 's/^/#/' | crontab -

Validating TLS with openssl sclient and curl --resolve

openssl s_client -connect new-vps:443 -servername example.com
curl --resolve example.com:443:new-vps https://example.com/health

Smoke-testing the new server via /etc/hosts override

echo 'new-vps example.com' >> /etc/hosts
curl -I https://example.com

Pitfalls We Warn Interns About

Lowering TTL 10 minutes before cutover does not help

DNS TTL reduces expected cache duration but does not create an instant switch Raff.

Forgetting to re-create mail accounts and SPF/DKIM records

We lost two hours once because the new server did not have the old mail accounts. SPF/DKIM records were missing from the new DNS zone.

Not testing the app with the real hostname and TLS behavior

Redirects, cookies, allowed-host settings, CORS, and certificate chains can fail even when the service responds by IP AWS.

Leaving old cron jobs running and causing double execution

Billing, backups, and imports ran twice for 47 minutes. We now disable cron before the final sync.

Deleting the old server before backups and monitoring are verified

We keep the old VPS isolated and read-only for 72 hours minimum.

Rollback Steps We Rehearse Before Going Live

DNS rollback: point A/AAAA back to the old IP with low TTL

# Revert A record to old IP
# Keep TTL at 300 during confidence period

Traffic steering: re-point CDN/proxy origin to the old server

If using a CDN, we re-point the origin back to the old server.

Confirming the old server still serves correctly after rollback

We run the same smoke tests against the old server.

Tracking where writes went during the cutover window

We compare order IDs and upload timestamps between old and new databases.

What We Would Do Differently Next Time

Run a full cutover runbook with task IDs and dependencies

We are adopting the AWS cutover runbook template with task IDs AWS.

Use staged synchronization with one authoritative writer per phase

We will name one authoritative writer at every phase Raff.

Keep the old VPS isolated and read-only for 72 hours minimum

No exceptions.

Attach rollback steps to ticket updates, not WhatsApp voice notes

WhatsApp pings are conversational. We open a correlated ticket when finance requires an SLA clock.

Budget CDN/WAF for parallel application-layer mitigation tiers

We are budgeting for a CDN layer that can absorb traffic while we fix issues.

Sources

Enjoyed this article?

Back to Blog