Skip to content

Frankfurt and Singapore regions now on 400 Gbps backbone — see the latency map

All articles
Engineering12 min read

Migrating 340 WordPress sites in a weekend, without a single support ticket

An agency moved their entire client book to us over 48 hours. Here is the runbook we used, including the two things that went wrong and how we caught them before the client did.

Tom Aldridge

Migrations lead

SharePostShare

In July an agency in Bristol asked us to move 340 WordPress sites off a host that had been steadily degrading for a year. They had 4.1 TB of uploads, eleven different PHP versions in use, and a hard constraint: no client was to notice, and no client was to be told in advance.

We finished on the Sunday afternoon with zero customer-facing incidents and two internal ones. This is the runbook, including the parts that did not go to plan.

Thursday: inventory before anything else

The single biggest cause of failed bulk migrations is discovering a dependency after the DNS has already moved. So nothing gets copied until we have a complete inventory, built by reading the source filesystem rather than asking the customer.

bash
$ srvd migrate inventory --ssh legacy-host.example --deep340 sites · 4.11 TB uploads · 812 GB databasesphp     7.4 (18)  8.0 (41)  8.1 (152)  8.2 (118)  8.3 (11)mysql   5.7 (296)  8.0 (44)cron    1,204 entries across 287 sites⚠ 6 sites reference an absolute path under /home/olduser⚠ 2 sites use a plugin with a hard-coded database host⚠ 1 site has a 41 GB uploads directory (video)

Those nine warnings are the whole value of the step. Six sites with hard-coded absolute paths would have thrown 500s the moment the document root changed. We fixed them on the source host, on the Thursday, while everything was still live and rollback was free.

Friday: seed the data while the old host still serves

We copy everything twice. The first pass is a bulk rsync that runs for as long as it takes — in this case fourteen hours — with the old host still serving all traffic. The second pass, at cutover, is a delta of whatever changed since. That second pass took under four minutes per site.

  • Uploads copied with rsync, checksummed, then verified by count and total bytes per site.
  • Databases dumped per-site with consistent snapshots, restored, then row-counted against the source.
  • PHP version pinned per site to exactly what the source was running. Nobody gets 'upgraded' during a migration.
  • Cron entries recreated but left disabled until after cutover, so nothing fires twice.

Saturday: cutover in batches of forty

TTLs were dropped to 60 seconds on the Wednesday, which is the one thing you cannot do on the day. Then eight batches of roughly forty sites, each with a fifteen-minute verification window before the next started.

BatchSitesDelta syncVerificationResult
1403m 12s14mClean
2402m 51s14mClean
3403m 40s22m1 rollback
4403m 02s15mClean
5454m 18s14mClean
6453m 55s14mClean
7453m 31s16m1 rollback
8454m 02s18mClean

The two rollbacks

In batch three, a site using a commercial booking plugin failed its verification because the plugin phones home to a licence server that allowlists by IP. Our verification caught it — the booking form returned a 402 — and we rolled that single site back inside four minutes. We got the new IP allowlisted on Monday and moved it separately.

In batch seven, a site with a 41 GB video uploads directory had a delta sync that ran long enough for two new uploads to land on the old host after the delta started. Verification caught the byte-count mismatch. Rolled back, re-synced with the site in maintenance mode, moved successfully forty minutes later.

“Both failures were caught by automated verification, not by a human noticing, and not by a client emailing. That is the only part of this runbook I would call non-negotiable.”

— Tom Aldridge, migrations lead

What we would do differently

  1. Put any site over 20 GB into its own batch from the start, rather than discovering the problem at delta time.
  2. Ask specifically about licence servers that allowlist by IP. 'Do you have any commercial plugins' is too vague a question.
  3. Keep the rollback window at 14 days rather than 7. Two clients found unrelated pre-existing bugs in week two and it was useful to be able to prove the migration was not the cause.

If you are sitting on a client book you want to move, the process above is what you get, and it costs nothing regardless of how many sites you have. We do them in batches specifically so you can watch the first forty before committing the rest.

TaggedmigrationsWordPressrunbookagencies

Tom Aldridge

Migrations lead

Tom Aldridge works on the migrations lead side of SrvDaddy. Questions about anything in this article go straight to the team that wrote it — reply to hello@srvdaddy.com and it lands with an engineer, not a queue.

SharePostShare

Ready when you are

Reading about uptime is not the same as having it

Migrate free, keep your current host running in parallel, and cancel within 7 days for a full refund if we do not beat it.

7 Day money-back guarantee. T&C appliedFree migration from any hostNo setup fees, ever