How we do zero-downtime migrations (at 2am, while you sleep)
11 min read · 02-Apr-2026
villagehosting.in team
2 April 2026
The only safe migration window is early morning IST
Most Indian websites see their lowest traffic between 1–5 AM IST. Schedule DNS cutovers in this window. Even with zero-downtime migration, there's always a brief period where some users hit the old server and some hit the new one — minimising concurrent load on both servers during this window reduces the risk of either showing errors.
A good migration should feel like nothing happened. Your visitors never see a 500. Your TLS never breaks. Your DNS never gets stuck on stale caches. Your email keeps arriving. This is not a nice-to-have — it's the entire promise.
Here's the runbook our migration team uses, essentially unchanged for the last two years.
48 hours before
We email the customer a pre-migration checklist. We ask for:
- The old host's control panel credentials (cPanel, Plesk, DirectAdmin, etc)
- Domain registrar access (or DNS zone export)
- A list of email accounts and approximate mailbox sizes
- Any cronjobs or background workers
- Database sizes
Then we shave DNS TTLs to 300 seconds. This is the single biggest lever nobody uses. If your DNS TTL is currently 3600 (one hour) or 14400 (four hours), dropping it to 300 means on cutover day the world finds your new IP in five minutes instead of four hours.
# For each A/AAAA record that's about to change:
# example.com A 1.2.3.4 (TTL: 14400 → 300)
# www A 1.2.3.4 (TTL: 14400 → 300)
24 hours before
We do a dry-run migration to a throwaway subdomain on the new server — something like migrate-example.villagehosting.in. We sync the files, import the database, point the subdomain, and load the site. If anything breaks — a hardcoded absolute URL, a PHP extension that's missing, a mod_rewrite rule in the wrong place — we find it here, not on cutover night.
2–3 AM IST, cutover night
We pick 2–3 AM IST because traffic is at its lowest for Indian sites. For global sites we coordinate around the customer's geography.
Step 1. Final incremental sync.
We already have a full copy from the dry run. We do an rsync -av --delete to pull only what changed.
rsync -av --delete --exclude='wp-content/cache' \
old-host:/home/user/public_html/ /home/user/public_html/
Step 2. Final database dump. On a WordPress site this is usually 5–60 seconds.
# On old host
mysqldump --single-transaction --quick --lock-tables=false \
user_db > /tmp/final.sql
# On new host
mysql user_db < /tmp/final.sql
Step 3. Put the old site in read-only mode (for sites with forms/comments/orders). For WooCommerce we temporarily disable checkout for 10–15 minutes. For contact forms, we queue submissions to a mailbox that we forward later.
Step 4. Update DNS. Because we shaved TTLs to 300s yesterday, the world picks up the new IP in under 5 minutes. We monitor DNS propagation from 10 global resolvers.
Step 5. Validate. We run an automated checklist:
- Homepage returns 200 in under 800ms from Mumbai, Bengaluru, Chennai, Singapore, Frankfurt, London, New York
- /wp-admin (or the admin path) returns 200 or a redirect to HTTPS
- A test order / form submission / comment works
- SSL certificate is valid (we pre-install Let's Encrypt on the new host and temporarily use
/etc/hoststo issue the cert before DNS cutover) - Email MX records are correct and an outgoing test email arrives
- Cron jobs are scheduled and running
Step 6. Raise TTLs back up to 3600. This keeps DNS servers from hammering the authoritative nameservers.
After migration
We keep the old host's snapshot for 30 days. If anything surfaces later — a missing file, a forgotten subdomain, a broken cron — we can pull from the archive. The customer doesn't have to ask.
We also proactively email a migration report with:
- Before/after TTFB for the three closest regions
- Before/after Google Lighthouse score
- A list of everything we migrated (files, databases, email accounts, cronjobs)
- Any warnings we silently fixed (deprecated PHP functions, hardcoded absolute URLs, etc.)
The edge cases we've hit
- Email still pointing at old MX for 48 hours. If the customer's registrar is slow to propagate, we set up a temporary SMTP relay from the old host forward to the new host.
- Hardcoded absolute URLs in a WordPress database. We run
wp search-replacewith--dry-runfirst, then live. - Custom Apache modules. We pre-install them on the new server during the dry run, not at 3am.
- Expired SSL cert on the old host. We issue Let's Encrypt on the new host using DNS-01 challenge, which doesn't need a live site.
- Corrupt MyISAM table. We run
mysqlcheck --repairon our side before importing.
Why we do this for free
Because if the migration goes badly, you'll tell twenty people. If it goes well, you'll tell five. Either way, word gets around. The best marketing we have is a customer saying "their migration team did it at 2am while I slept, no downtime". We're happy to keep earning that one sentence at a time.
— the villagehosting.in team