Nobody moves a live site for entertainment. It happens because the current host suddenly wants a photograph of your passport, or forwards a complaint with a twenty-four hour deadline, or because the country its datacentre sits in stopped looking like a sensible place to keep your data. Whatever pushed you, the move itself is the dangerous part — it is the one moment the site can go dark, and the one moment a careless step can staple the new server to the identity you were trying to leave behind.
Both risks have the same cure, and it is not a tool. It is ordering. A migration run in the right sequence has no window in which the site is unreachable, because both servers are alive at the same time and DNS is the last thing to move. A migration run in the wrong sequence produces an outage and a trail at once. What follows is that sequence, written for someone moving to an offshore, no-KYC host rather than shuffling between two mainstream providers — the mechanics are the same, but the cleanup afterwards is not.
What “zero downtime” actually means
The phrase is used loosely, and the looseness is where people get hurt. Serving HTTP from two machines at once is easy. Keeping state consistent while two machines serve at once is the hard part, and it is the only part that ever loses data. So before planning anything, decide which of these you are actually running, because the answer sets the shape of the whole night.
| What you are moving | The part that actually bites | What the plan should be |
|---|---|---|
| Static site, brochure site, generated output | Nothing. There is no state to split | Copy, verify, cut over. Genuinely zero downtime |
| CMS with a database — WordPress, Ghost, a forum | Comments, logins and posts landing on two databases at once | A read-only freeze measured in minutes, at your quietest hour |
| A store, or anything that takes orders | A split brain silently loses paid orders | Take the short maintenance window. It is cheaper than reconciliation |
| Anything with cron jobs or background workers | The same job firing on both servers — double emails, double charges | Disable the schedule on the old host before the new one starts |
| Mail on the same domain | MX records expire from caches on their own clock, unrelated to your A record | Move mail on a separate night, and keep the old MX accepting for a week |
Notice that only the first row is truly free. Everywhere else, “zero downtime” means “a write freeze so short nobody files a ticket about it”. Two minutes of read-only at 04:00 is a rounding error; two hours of split writes across two databases is a weekend of reconciliation. Choose the freeze.

Lower the DNS TTL days before you plan to move
This is the single step with a lead time, which is why it is first and why it is the one people skip. Your TTL — time to live — tells every resolver on the internet how long it may cache your record before asking again. If your A record has a TTL of 86400, a resolver that looked it up an hour ago will keep handing out the old IP for another twenty-three hours, no matter what you change at the registrar.
The critical detail is that lowering the TTL is itself subject to the old TTL. Resolvers only learn about the new, shorter value when the old cached copy expires. So drop the TTL to 300 seconds at least one full old-TTL period before the cutover — with a day-long TTL, that means doing it 24 to 48 hours ahead. Then the world converges on your new record within five minutes of the change, and the cutover stops being a suspenseful event.
Put the TTL back up to something sane a few days after the move. A 300-second TTL is a fine tool and a poor permanent setting: it multiplies your query volume and makes your DNS provider a much sharper single point of failure.
Inventory what you are moving, not what you remember
Every failed migration has the same post-mortem: something nobody listed was not copied. The web root and the database are the two things everyone remembers; the list below is the rest, and it is worth walking it literally rather than from memory.
- Scheduled work.
crontab -lfor every user, plus systemd timers. Renewal hooks and nightly jobs hide here. - Service definitions. Custom systemd units, the web server vhosts, PHP-FPM pools, any supervisor config.
- Secrets and environment.
.envfiles, API keys, database passwords, application salts — and note that these should be rotated, not merely copied. - TLS material. Certificates and, more importantly, the ACME account and renewal configuration.
- Mail identity. DKIM private keys, SPF and DMARC records. A mismatch here does not break loudly; it just quietly sends your mail to spam.
- Uploaded media. Frequently outside the web root, frequently the largest thing you own.
- Everything external that trusts your IP. Payment gateway allowlists, webhook destinations, database firewalls, third-party APIs with IP restrictions. This is the number one cause of “the site works but checkout is broken” at 03:00.
- The package list.
dpkg --get-selectionsor the equivalent, so the new box has the same extensions and libraries rather than nearly the same ones.
Write the list down before you start copying. The inventory is also your test plan later — every line on it is something to verify on the new server before DNS knows it exists.
Build the new server first, and harden it before it holds anything
Order the destination early and leave it running empty for a day or two. There is no cost to overlap — a small offshore VPS is a few dollars a month — and a great deal of value in not doing the build under time pressure with a frozen database waiting.
Match the old environment deliberately: the same distribution and major version, the same PHP or Node or Python major version, the same database major version. The temptation to modernise while you are in there is enormous and should be resisted completely. If the site breaks after the cutover, you want exactly one variable to have changed. Upgrade the stack a fortnight later, on a boring afternoon, with the ability to roll back.
Harden it while it is still empty. Keys-only SSH, a default-deny firewall, automatic security updates — the first-hour hardening checklist is exactly this list, and it is far easier to apply to a machine with nothing on it. If the data is sensitive enough that you are moving jurisdictions over it, this is also the right moment to decide about encryption at rest, because retrofitting it later means another migration.
Copy the data twice: a slow pass, then a fast one
The instinct is to copy everything during the maintenance window. Do the opposite. Run a full copy days ahead while the old site is happily serving traffic, then run a second pass at cutover that only moves what changed. The first pass can take six hours and nobody notices. The second takes ninety seconds, and that is your entire downtime budget.
For files, rsync -aHAX --numeric-ids preserves permissions, ownership, hard links and extended attributes; the --numeric-ids flag matters because UIDs rarely line up between two freshly built machines. Run it once early, then again immediately before the cutover with the same arguments — the second run transfers only the delta.
Databases need the same two-phase treatment but different tools. A mysqldump --single-transaction or pg_dump gives you a consistent early snapshot to build and test against. At cutover, either take a second dump during your brief write freeze, or — for a large database where even a short freeze hurts — set the new server up as a replica of the old one days in advance, let it catch up, and then promote it. Replication turns the freeze into seconds. It also turns a two-hour migration into a two-day project, so use it only when the size genuinely demands it.
Pull, do not push, and never through your laptop. Initiate the copy from the new server so the transfer runs host to host at datacentre speed. Routing gigabytes through your home connection is slow, and it puts your residential IP in the access logs of both machines — which is precisely the link a privacy-motivated migration exists to avoid. If even the old host learning your new IP is unacceptable, do not copy directly at all: restore the new server from your own off-site encrypted backup instead, and the two machines never speak.
Test the new server before DNS knows it exists
You can serve the real hostname from the new IP without changing a single public record, and you should — this is what makes the cutover uneventful. Add a line to your local /etc/hosts pointing the domain at the new IP, or skip that and let curl do it for one request:
curl -I --resolve example.com:443:203.0.113.10 https://example.com/
Now walk the inventory. Load the front page and three deep pages. Log in. Submit a form. Upload a file. Check that the database connection is the new local one and not still pointing at the old host over the internet — a mistake that works perfectly right up until you cancel the old server. Run the cron jobs by hand and read their output. Verify redirects and that a missing URL still returns 404 rather than 200.
Issue the TLS certificate now, before the cutover, not after. Use a DNS-01 challenge, which proves control of the domain through a TXT record and therefore works while the A record still points at the old server. If you wait for HTTP-01 validation after the DNS change, every early visitor gets a certificate warning during the gap — a self-inflicted outage in the one window you were trying to protect.
The cutover, in order
By this point the new server is built, hardened, populated, tested under the real hostname and holding a valid certificate. The cutover itself is now a short, dull list, which is the goal.
- Announce the window if anyone else depends on the site, then put the old site into read-only or maintenance mode.
- Disable cron and background workers on the old host. Do this before starting them on the new one, never after.
- Run the final
rsyncdelta pass and the final database dump, and import it. - Start the application on the new server and re-run your smoke tests through
--resolve, against the final data. - Change the A and AAAA records to the new IP. With a 300-second TTL, the world follows within five minutes.
- Enable cron and workers on the new host.
- Watch both access logs side by side. Traffic drains from the old server and appears on the new one; when the old one goes quiet, the cutover is complete.
- Leave the old server running, serving, and untouched for a week. It is your rollback.
Step eight is the one people cut, and it is the cheapest insurance in the list. For the price of a few dollars you keep the ability to point DNS back — a five-minute recovery — for as long as it takes to be sure.
What the migration leaves behind
Here is the part generic migration guides omit, and the part that matters most if you moved for privacy rather than for price. Moving a site does not erase its history. Several public and semi-public records of the old arrangement survive the move permanently, and knowing which ones is the difference between a clean break and a false sense of one.
| What records the move | Who can read it | What you can actually do |
|---|---|---|
| Passive DNS — historical A records | Anyone, through commercial history services | Nothing. The old IP is permanently associated with the name. Plan as if it is public, because it is |
| Certificate Transparency logs | Anyone, permanently, searchable by domain | Every certificate ever issued is listed — including the internal-sounding subdomains you forgot. Prefer a wildcard to descriptive names |
| The old host’s account records | The old host, and anyone who can compel them | Card details, signup email, login IPs. A no-KYC destination protects the future, not the past |
| WHOIS history | Commercial WHOIS-history archives | If the domain was ever registered under real details, that snapshot is captured. Privacy applied later does not retract it |
| Analytics and ad identifiers | The vendor, and anyone reading your page source | Carrying the same tracking ID across links the two sites conclusively. Issue a new one, or drop it |
| Dumps and backups left on the old disk | Whoever is allocated that storage next | Delete and overwrite before cancelling. On shared storage, assume deletion is a hint rather than a guarantee |
Received: headers in sent mail | Every recipient, forever | Nothing retroactive. Only the mail you send after the move carries the new path |
| Your own connections during the copy | Your ISP, and both hosts’ access logs | This one is entirely under your control. Never touch either machine from an IP that identifies you |
The honest summary is that a migration cannot rewrite the past — it can only stop adding to it. That is still worth a great deal, but it changes the decision: if your threat model requires that no observer can link the new site to the old one, moving the same domain to a new host does not achieve it, and no amount of care during the cutover will. That case needs a new name and a clean start, discussed next. If your goal is instead to stop generating identifying records from today onward, and to move the legal centre of gravity to a jurisdiction you chose, the move does exactly that. Our server OpSec guide covers the habits that keep it clean afterwards.
The domain question: carry it over, or start fresh?
The site and the domain are independent decisions, and conflating them is common. You can move the hosting today and leave the registrar alone forever; nothing about the server change requires touching the domain. Whether you should depends entirely on what the domain already knows about you.
- Keep the domain, change the registrar. Sensible when the domain has value — links, rankings, a name people type. It fixes the future of the WHOIS record, not its history, and it keeps every ranking signal intact. This is the right answer for most commercial sites.
- Keep the domain, change nothing but the host. Perfectly reasonable when you moved for jurisdiction, uptime or DMCA posture rather than anonymity. Simplest possible move, zero SEO risk.
- New domain, redirect the old one. Preserves rankings, and publicly and permanently links the two names. Choose it for continuity, never for privacy — the redirect is the link.
- New domain, clean break. The only option that genuinely severs the association, and it costs you every ranking and inbound link you had. Register it privately from the start, because a domain is only as anonymous as its first registration. Our guide to anonymous domain registration with crypto covers doing that properly.
Pick deliberately, and pick before the cutover rather than during it. Changing your mind about the domain after the DNS has moved means doing the delicate part twice.
Decommission the old host properly
A week or two after the cutover, when the new server’s logs are boring and the old one’s are empty, it is time to close the old account. Do it in this order, because the tempting shortcut — hitting cancel — is the one that leaves your data on someone else’s disk.
- Confirm nothing still points at the old IP: check for hardcoded addresses in third-party webhooks, allowlists, monitoring, and any DNS record you forgot, such as a stray
mailorcpanelsubdomain. - Rotate every secret that ever lived on that machine — database passwords, API keys, application salts, DKIM keys, SSH keys. Do not copy them forward.
- Remove your SSH public keys and any support access from the old server.
- Delete the application, the dumps and the backups, then overwrite the free space so a casual read of the recycled volume yields nothing.
- Only then terminate the service, and remove any stored payment method from the old account.
Anything that lived on hardware you no longer control is compromised by definition. Not because your old host is malicious, but because that disk is going back into a pool and you will never know what survived the wipe. Rotating a database password takes two minutes. Discovering months later that a key from a decommissioned server still opens something takes considerably longer.
The whole sequence on one page
Strip out the reasoning and a host migration is nine steps, of which only two have any urgency:
- Two days ahead: lower the DNS TTL to 300 seconds.
- Two days ahead: order and harden the destination server, matching the old stack version for version.
- Days ahead: write the inventory — cron, secrets, TLS, mail keys, media, IP allowlists, packages.
- Days ahead: run the first full data copy, host to host.
- Before the window: issue the certificate by DNS-01 and test everything through
--resolve. - The window (minutes): freeze writes, disable old cron, run the delta copy and final dump, start the new app.
- The window (seconds): change the A record, then enable cron on the new host.
- The following week: keep the old server alive as a rollback, watch both log files, then raise the TTL again.
- Afterwards: rotate secrets, wipe, cancel — and remember what the move could not erase.
Nothing in that list is difficult. Every step that hurts is a step done out of order — a TTL lowered on the night, a certificate issued after the DNS change, a cron job left armed on a machine that is no longer authoritative. Get the sequence right and the interesting part of a migration is choosing where to put the server, not the move itself. If you have not settled that yet, the jurisdiction guide is the place to start.