Servers & Hosting

Migrating a website without downtime: the pre-flight checklist

Migrations fail in predictable ways, and every one of them is preventable at the planning stage. Here is the checklist I work through before anything moves.

Website migrations have a reputation for going wrong, which is unfair. They go wrong in a small number of entirely predictable ways, and all of them are prevented at the planning stage rather than fixed afterwards.

The four classic failures: email keeps arriving at the old server for two days because nobody lowered the TTL; redirects lived on the old host and were not copied; the database was exported while it was still being written to; and SSL was not reissued, so every visitor gets a browser warning. None are difficult to avoid. All are painful to discover afterwards.

Step 1: inventory everything the old host does

Not just the website. This is the step that gets rushed, and the forgotten item is almost always the thing that breaks a week later.

  • Domains and subdomains, including staging, and any parked or addon domains.
  • All DNS records, export the full zone. This is your rollback.
  • Mailboxes, aliases, forwarders and autoresponders, with their sizes, which determines how long sync takes.
  • Cron jobs. the classic post-migration bug. Nobody notices until the monthly invoice run does not happen.
  • SSL certificates, issuer, expiry, and whether any are paid certificates that need reissuing rather than reissued automatically.
  • PHP version and enabled extensions, an extension missing on the new server produces a fatal error on one obscure page.
  • Redirect rules, in .htaccess, nginx config, or a plugin. Miss these and search rankings suffer.
  • Anything outside the web root: upload directories, generated PDFs, log archives, SSH keys.
  • Third-party allowlists, payment gateways and APIs that restrict by IP will reject the new server's address.

Step 2: build and verify before anything public changes

Everything moves to the new server and gets fully tested while the old one is still serving live traffic. The trick is testing under the real domain name without changing DNS. You do that with a local hosts file entry:

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

Now your machine resolves the domain to the new server while the rest of the world still sees the old one. This matters because a great many applications behave differently depending on the hostname in the request, absolute URLs, cookie domains, OAuth callbacks and CDN configuration all care. Testing via a temporary IP-based URL proves much less than people assume.

What to verify while staged: the homepage and key templates, a login, a form submission, a checkout if there is one, the admin area, image and asset paths, and any scheduled task run manually.

Step 3: lower TTLs, days ahead, not hours

TTL tells resolvers how long to cache a DNS record. If yours is 86400 seconds, a change can take up to 24 hours to be seen everywhere, and critically, that applies to reverting too. A high TTL means a mistake is a day long.

So drop TTLs to 300 seconds at least 48 hours before the migration, long enough that the old high TTL has expired everywhere and the new low value is in effect.

bash
# Confirm the current TTL is actually low before cutting over
dig example.com A +noall +answer
# example.com.  300  IN  A  203.0.113.10   ← the 300 is what you are checking

Raise TTLs back to something sensible about a week after the move.

Step 4: email first, with a dual-delivery window

Email is the highest-risk part of any migration because a lost message is not recoverable and often nobody realises for days.

The safe sequence is: create the mailboxes on the new server first, sync the contents with IMAPSync, then change the MX records, then run IMAPSync again to catch anything that arrived in the meantime.

bash
imapsync \
  --host1 old.example.com --user1 user@example.com --password1 "***" \
  --host2 new.example.com --user2 user@example.com --password2 "***" \
  --automap --skipsize --useheader Message-Id

Leave the old server accepting mail for several days after the MX change. Some sending servers cache MX records aggressively, and a message delivered to the old host during that window is only recoverable if the mailbox is still there. The final sync is what reconciles it.

Step 5: the database, consistently

For a small database, a dump taken during a short maintenance window is fine, nothing changes underneath you.

bash
mysqldump --single-transaction --quick --routines --triggers --events \
  -u user -p dbname | gzip > dbname.sql.gz

--single-transaction is doing the important work there: on InnoDB it gives a consistent snapshot without locking tables, so the site stays up during the export.

For a large or busy database, do not dump at cutover time. Set up replication from old to new in advance, let it catch up, then at cutover stop writes for a few seconds, confirm the replica has caught up, and promote it. Downtime becomes seconds rather than however long a restore takes.

Step 6: cut over deliberately

Pick a genuinely quiet window, for most business sites that is early morning at the weekend, and be careful with timezones if the audience is international. Both you and someone who can make decisions should be available.

The switch itself is: put the old site into read-only or maintenance mode, run the final file and database sync, update the DNS records, then verify. With TTLs already low, propagation is minutes.

One thing not to do: do not cancel the old hosting on the day. Keep it running and untouched for at least a fortnight. It is your only rollback, and it is also where you will find the file nobody remembered.

Step 7: verify properly afterwards

Crawl the site before the migration and save the results. URLs and status codes. Crawl again after and compare. This catches the quiet failures: a page that now returns 404, a redirect that turned from 301 into 302, a canonical tag pointing at the old hostname.

  • Crawl comparison, old URL list against new responses.
  • SSL check from an external tool, including the full certificate chain.
  • Send and receive a test email in both directions, and read the headers to confirm SPF and DKIM pass.
  • Confirm cron jobs ran, by checking their output rather than assuming.
  • Submit the sitemap in Search Console and watch coverage for a couple of weeks.
  • Check server error logs daily for the first week, some issues only surface on the first weekly or monthly scheduled task.

What about rankings?

A same-domain host change should be invisible to search engines. URLs do not change, content does not change, and status codes stay the same. There is nothing for a crawler to react to.

Ranking drops after a migration are nearly always caused by something incidental: pages returning 404 because a rewrite rule was missed, a noindex left over from the staging environment, canonical tags still pointing at the old server, or a robots.txt that was copied from staging and disallows everything. The before-and-after crawl catches all four, which is why it is worth the twenty minutes.

If you would rather not run this yourself, it is what the server and website migration service covers.


Written by Irfan Bhat, web developer & server engineer, currently technical support & development at ElySpace. More about me, or get in touch if this raised a question.

Read next

Running into this yourself?

If any of the above matches what you are dealing with, describe the situation and I will tell you what I think is going on.

Get in touch