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.
Author
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
- Server migration tutorial (2026): Move a Website to a New VPS with rsync, DNS cutover, and rollback | HostMyCode
- VPS Hosting Cutover Plan for Zero-Surprise Launches (2026) - Hostperl
- Website migration checklist | HostStack India
- VPS Migration Checklist: Data, DNS, Cutover, and Rollback | Raff
- Cutover runbook template | AWS
Sources
Related reading
Enjoyed this article?
Back to Blog


