Minimizing Downtime During eCommerce Migrations

Empty boxes piled up in the corner of a room

Some downtime during a platform migration is inevitable. Data, sometimes huge amounts of data, has to be transferred. Domain records need to be updated. The final checklist before launching is extensive. 

However, any reputable agency will minimize this inevitable downtime, because every hour an eCommerce site is offline costs money. A migration that drags on for days is unacceptable, causing a direct hit to revenue and customer trust.

We've migrated eCommerce operations ranging from several thousand orders to several million, with product catalogs spanning a few hundred to several hundred thousand SKUs and customer records well into the hundreds of thousands. Through that work, we've developed a set of practices that consistently keep actual site downtime to a few hours, even on the most complex projects.

Here’s how to minimize your downtime.

Start migration planning as soon as the architecture is set

When do you start thinking about data migration? As soon as the architecture is nailed down. As soon as entities and fields are defined, old data stores can be mapped to the new database.

On a large-scale auction platform migration, we began planning and running migrations well before the frontend was even designed. The initial data load ran for three to four days. However, by the time for the final cutover, we only had a small incremental migration to run, and the launch was straightforward. The heavy lifting had already been done.

Don’t compress all of that work and risk into the final few weeks of a project. Start early.

Use incremental migrations with high water marks

When you're moving a million records, you don't want to move a million records every time you run the migration. Set up incremental migrations using high water marks, so the next migration continues from where the last one left off. Only migrate what’s changed.

This approach pays off throughout the project. During development, it speeds up testing cycles. Closer to launch, it means we can run a large migration against the pre-production environment and then run smaller incremental syncs to keep it current. By the time we enter the maintenance window for go-live, the migration queue is small and fast.

For the auction platform, where new orders and products were generated in monthly cycles throughout the new site’s build phase, we ran incremental migrations after each auction closed, and none of that translated to downtime.

Segment what must happen during the launch…and what can wait

Not everything needs to migrate while the site is unavailable and in maintenance mode. We deliberately separate migrations into what's required for launch and what can sync afterward.

Data that must be current at launch—active products, customer accounts, recent orders—goes into the maintenance window. But historical data like product enrichment notes, old log messages, or records that are stable and won't change can be deferred. That data completes the historical record, but it doesn't need to be there on day one. It can be brought over during a later phase.

Orders that have been placed on the old system but not yet fulfilled need extra attention to determine the best way to migrate them. The correct way to do this depends on how you fulfill orders. For example, if you authorize and capture payment at the time of order, it's usually simpler to complete those orders in the old system and then run a small post-launch migration to bring the final records into the new system. This avoids trying to capture payment from a new system that doesn't have the original transaction context.

Preserve entity IDs

This one seems obvious, but it bears repeating because it can bite you if you aren’t paying attention. Wherever possible, preserve the original IDs on migrated entities: customer IDs, order IDs, shipment IDs, product IDs, variation IDs.

Everything links to those IDs. Customer service teams have bookmarks and workflows built around order URLs that use the order ID. Accounting systems link directly to orders. Customer dashboards display payment history and profiles keyed to user IDs. Third-party integrations reference product and variation IDs.

When IDs aren't preserved, all of those links break. You can't fix this easily after go-live. Writing redirects or mapping old IDs to new ones is time-consuming and error-prone. Better to treat ID preservation as a best practice from the start.

Leave ID gaps for safety

Increment the starting IDs in the new system to leave a gap above the old system's IDs. For example, if the old system has 537,000 orders, start the new system's order IDs at 700,000.

This serves several purposes. First, it creates clear separation between migrated data and new data, making test orders easy to identify and remove. Second, and more importantly, it provides a safety net.

Clients often tell us upfront that they only want to bring over certain data, thinking it will save on costs. They think they won't need old shipment records, or they're fine without historical payment details. Then they get into the new system and realize they actually do need that information.

If we've already incremented the IDs, we can run a post-launch migration to bring that data over without collisions. A simple fix.

Perform dry runs against actual production procedures

Set up the pre-production environment to mirror actual production deployment procedures, and then do dry runs. We do multiple dry runs. Record what happens, note the lessons, and repeat until the process runs cleanly.

These tests catch problems that don't surface in development environments: Let's Encrypt certificate provisioning issues, permission problems preventing certificates from being applied to all required domains, CORS configuration gaps, and environment-specific deployment failures.

Moving to a managed hosting platform (like Upsun) helps here significantly. Discrete environments for dev, staging, and production are available from the start, CI/CD pipelines are in place, environment variables can be preset and tested, and certificate provisioning is handled automatically. Much of what used to require manual intervention during cutover is simply ready to go.

Test with production payment credentials

Always run at least one real order through production payment credentials before cutover. Payment gateway sandboxes don’t always achieve full parity with their production environments. We've encountered issues with multiple gateways:

  • AVS and fraud tools can be significantly more permissive in sandbox mode. Settings that pass in a sandbox will reject transactions in production without specific configuration changes.
  • Digital wallets like Apple Pay and Google Pay may work automatically in sandbox but require specific setup in production.
  • Fraud screening services can flag a business unexpectedly. On one project, a client's PayPal payments were being sandboxed at launch because a similarly named company was on a blocklist, and the client was getting caught in the filter. This wasn't an issue in sandbox, but it meant no orders were completing in production because webhook confirmations weren't coming through.

Sweat the small details

Some of the most painful launch-day problems come from details that seem trivial… until they aren't.

DNS resolution

Know what the resolving domain should be: www, a subdomain, or the bare apex domain. This seems minor, but we know of one project that, when they tried to go live, their load balancer couldn't handle an apex domain. The fix required an expensive Cloudflare plan for CNAME flattening, and it was a complication that could have been identified on day one. Check everything. Verify everything.

DNS TTL

Before cutover, verify the TTL on DNS records. Most modern setups don't have aggressive caching, but some name servers still default to four- or eight-hour TTLs. A 30-day TTL discovered at cutover means the new site won't resolve for a month.

What to expect on launch day

We typically prepare for a six-hour launch-day window. That doesn't mean six hours of downtime. It means six hours of coordinated activity: taking database backups, pausing automated processes, running final incremental migrations, performing DNS cutover, placing test orders, running spot checks with the site in maintenance mode, and then bringing it back up.

Actual site downtime within that window is usually a fraction of the total. On our most recent migration, the site was offline for under two hours. The client had been expecting multiple days of downtime based on previous experience.

Two hours felt like an entirely different universe.

Downtime isn't minimized by luck. It's minimized by the months of preparation that precede it.

Add new comment