Table of Contents
1. The 48-Hour Problem
When you change a domain’s DNS records, the old IP address is cached by thousands of DNS resolvers worldwide. Each resolver holds the old record until its TTL (Time To Live) expires. If the TTL was 86400 (24 hours) when you made the change, some visitors will hit the old server for up to 24 hours. If the TTL was 3600 (1 hour), they’ll see mixed content for up to 1 hour.
This means some visitors see the old WordPress site on Flywheel while others see the new site on prod-hetzner. Both sites are live, both are serving pages, and there is no way to force every DNS resolver on the internet to update simultaneously.
2. Migration Architecture
Every migration from external hosting to prod-hetzner follows this pattern. The old host and new host run simultaneously during the transition window.
What Controls the Transition Speed
| Factor | You Control It? | Impact |
|---|---|---|
| TTL on current A record | Yes — lower it days before cutover | The single biggest factor. Low TTL = fast propagation |
| Resolver cache behavior | No — each ISP’s resolver follows its own rules | Some resolvers honor TTL exactly, others cache longer. 300s TTL means ~5–15 min in practice |
| Browser DNS cache | No — browsers cache DNS 1–3 minutes independently | Minor. Hard refresh or incognito clears it |
| CDN / proxy cache | Partial — purge if you use Cloudflare, etc. | If the old site used a CDN, its edge nodes cache the old content |
3. Pre-Deployment Checklist
Everything in this section happens before the DNS change. The goal is to have the new site fully operational on prod-hetzner, verified, and waiting — so the cutover is just a DNS record change, not a build-and-pray.
| # | Task | Owner | Details |
|---|---|---|---|
| 1 | Lower TTL on current DNS | Human | At the current DNS provider (Flywheel, registrar, or Cloudflare), set the A record TTL to 300 (5 minutes). Do this 3–7 days before cutover so the old high TTL has time to expire everywhere. §7 TTL Strategy |
| 2 | Verify domain exists in dpanel | Human | Log into cp.dominionport.com. Confirm thehighroadmanufacturing.com is listed under the correct account. If not, add it. dpanel auto-creates DNS zone, MX, SPF, DKIM, DMARC records. |
| 3 | Deploy site code to prod-hetzner | AI | Run ship.sh from DevHub. This packages, uploads, deploys via devhub-receive, runs migrations (creates both thr_* and ths_* tables), and runs post-deploy.php (pushes product data, regenerates JS). Doc 10 §4 |
| 4 | Install nginx vhost | AI + Human | AI prepares the config (in deploy/nginx-thehighroad.conf with redirects baked in). Human installs it on prod: cp deploy/nginx-thehighroad.conf /etc/nginx/sites-available/, then nginx -t && systemctl reload nginx. Register with Guardian. |
| 5 | Configure production .env | AI + Human | AI provides the template (in .env.production). Human places it at shared/.env with real credentials: DB_HOST, DB_USER, DB_PASS, DB_PREFIX=thr_, SITE_PREFIX=ths_, Stripe keys, MAIL_METHOD=sendmail. |
| 6 | Run post-deploy verification | AI | Run php api/post-deploy.php on prod. Confirms both table tiers exist, pushes product data if needed, regenerates products-data.js. Must report “PASSED.” |
| 7 | Test new site via direct IP | Human | Before DNS changes, verify the new site works on prod-hetzner: curl -H "Host: thehighroadmanufacturing.com" https://46.225.85.236/ -k. Or add 46.225.85.236 thehighroadmanufacturing.com to your local /etc/hosts file to preview in a browser. |
| 8 | Prepare Flywheel transition page | Human | On Flywheel, install a simple redirect or “We’ve moved” page so visitors who still hit the old server during propagation get redirected to the new site. §10 Flywheel Bridge |
| 9 | Archive old site | Human | Export WordPress database + files from Flywheel. Store the backup. This is the rollback safety net. No archive = no cutover. |
| 10 | Verify TTL has propagated low | Human | Check dig thehighroadmanufacturing.com A and confirm the TTL shown is 300 (or close). If the TTL is still high, wait longer before proceeding to cutover. Use dnschecker.org for global verification. |
4. Cutover Execution
With pre-deployment complete and TTL verified low, the cutover is a single DNS change. This phase takes 2–5 minutes of human work, followed by 5–15 minutes of propagation.
| # | Task | Owner | Details |
|---|---|---|---|
| 11 | Change DNS A record | Human |
Option A (recommended): At the domain registrar, change nameservers to ns1.host.dominionport.com and ns2.host.dominionport.com. dpanel then controls all DNS (A, MX, SPF, DKIM, DMARC, CAA — all auto-configured).Option B: Keep external DNS. Update A record to 46.225.85.236. Update www CNAME to thehighroadmanufacturing.com. Manually configure MX, SPF, DKIM records for email.
|
| 12 | Provision SSL certificate | Human | Once DNS resolves to prod-hetzner, issue Let’s Encrypt cert via dpanel. Covers thehighroadmanufacturing.com + www + mail. §8 SSL Timing |
| 13 | Monitor propagation | Human | Check dig thehighroadmanufacturing.com A +short — should return 46.225.85.236. Check dnschecker.org for global status. With 300s TTL, expect full propagation within 15 minutes. |
5. Post-Cutover Verification
| # | Task | Owner | When | Details |
|---|---|---|---|---|
| 14 | Smoke test suite | Human | T+5 min | Homepage loads, shop shows products, contact form submits, API health check returns 200, SSL padlock shows, no mixed content warnings |
| 15 | Verify product data | AI | T+5 min | Hit /api/public-products.php?action=list — should return products. Hit /api/health.php — should return 200 with version. If products missing, run post-deploy.php again. |
| 16 | Test WordPress redirects | AI | T+10 min | Verify old WordPress URLs return 301 to new pages: curl -I https://thehighroadmanufacturing.com/products/ should redirect to /shop.html. Test /product/some-old-slug/, /manufacturing/, /wp-admin/. |
| 17 | Test email sending | Human | T+15 min | Submit contact form. Verify email arrives. If using dpanel nameservers, MX/SPF/DKIM are automatic. If using external DNS, verify MX records point to mail.thehighroadmanufacturing.com. |
| 18 | Submit sitemap to search engines | Human | T+30 min | Google Search Console: submit sitemap.xml. Bing Webmaster Tools: submit sitemap. Optional but speeds up re-indexing. |
| 19 | Monitor 404s and error logs | AI | Day 1–3 | Parse /var/log/nginx/thehighroadmanufacturing.com.error.log and access.log for 404 patterns. Add missing redirects to nginx config. Common misses: pagination URLs, attachment pages, random query strings. |
| 20 | Decommission Flywheel | Human | Day 7–30 | After 7 days of stable operation with no DNS-related issues, cancel the Flywheel hosting plan. Keep the WordPress backup archive for 90 days minimum. |
6. AI vs. Human Task Matrix
This is the definitive reference for what can be delegated to AI (Claude) inside a DevHub container versus what requires a human with browser/SSH access to external services.
What AI Can Manage (Inside DevHub)
| Task | How |
|---|---|
| Prepare nginx vhost + redirect map | Generate deploy/nginx-thehighroad.conf with all redirects baked in |
Prepare .env.production template | Generate template with correct variable names, prefixes, comments |
| Package and deploy site code | Run ship.sh (SSH to prod via devhub-receive) |
| Run database migrations | Included in ship.sh deployment pipeline |
| Run post-deploy verification | Run php api/post-deploy.php via SSH to prod |
| Generate products-data.js | Part of post-deploy, or php api/generate-products-js.php |
| Verify redirects via curl | curl -I commands to test 301s after cutover |
| Parse error logs for 404s | SSH to prod, parse access logs, suggest redirect additions |
| Generate sitemap.xml | Already in the codebase, deployed with the site |
| Prepare Flywheel redirect snippet | Generate the PHP/HTML code for the transition bridge |
What Requires a Human
| Task | Why AI Cannot Do This |
|---|---|
| Log into domain registrar | Requires browser + credentials to GoDaddy / Namecheap / etc. |
| Change DNS records / nameservers | Registrar web UI — no API access from DevHub |
| Lower TTL on current DNS | Must be done at current DNS provider’s control panel |
| Log into dpanel | Browser access to cp.dominionport.com |
| Verify domain exists in dpanel | dpanel admin UI — no API access from DevHub |
| Issue SSL certificate | Triggered via dpanel after DNS resolves to prod-hetzner |
| Install nginx config on prod | Requires root SSH to prod-hetzner + Guardian registration |
| Place production .env with real secrets | Secrets must never transit through DevHub or be stored in code |
| Log into Flywheel | Flywheel admin panel — browser + credentials |
| Archive old WordPress site | Flywheel export or manual backup from Flywheel dashboard |
| Deploy Flywheel redirect/bridge page | Flywheel file manager or SFTP |
| Submit sitemap to Google Search Console | Google account + Search Console access |
| Cancel Flywheel hosting | Billing action in Flywheel dashboard |
| Test email from customer perspective | Requires sending/receiving real email |
Update /etc/hosts for preview | Local machine modification |
7. TTL Strategy — The 5-Minute Cutover
This is the most important section of this document. TTL management is the difference between 48 hours of customer confusion and a 5-minute seamless transition.
The Timeline
Day -7: Check Current TTL Human
Run dig thehighroadmanufacturing.com A. Note the TTL value in the answer section. Common defaults: 3600 (1 hour), 14400 (4 hours), 86400 (24 hours).
Day -7 to -3: Lower TTL to 300 Human
At the current DNS provider, change the A record TTL to 300 seconds (5 minutes). Do not change the IP address yet. Keep it pointing to Flywheel. This step only changes how long resolvers cache the record. Wait for the OLD TTL to expire (e.g., if it was 86400, wait 24 hours).
Day -3 to -1: Verify Low TTL Human
Run dig thehighroadmanufacturing.com A again. The TTL in the answer should now show ≤300. Check from multiple locations using dnschecker.org. If any resolver still shows a high TTL, wait longer.
Day -1: Complete All Pre-Deployment AI + Human
All 10 pre-deployment tasks must be complete. New site verified on prod-hetzner. Flywheel bridge page ready. WordPress backup archived. Nothing left to do except flip the switch.
Day 0: Change A Record Human
Change the A record IP from Flywheel to 46.225.85.236. Because TTL is 300s, every resolver worldwide will pick up the new IP within ~5–15 minutes. Issue SSL cert as soon as DNS resolves.
Day 0 + 1 hour: Raise TTL Back Human
Once propagation is confirmed and the site is stable, raise TTL back to 3600 (1 hour) or 86400 (24 hours). Low TTL increases DNS query load and slightly increases latency for first-visit users.
8. SSL Certificate Timing
Let’s Encrypt validates domain ownership by hitting http://yourdomain.com/.well-known/acme-challenge/. This means the domain must resolve to prod-hetzner before the cert can be issued. There is a brief window after DNS cutover where the site has no valid SSL cert.
Mitigation Options
| Option | How | Downtime |
|---|---|---|
| A. Fastest — dpanel auto-cert | dpanel monitors new domains and auto-issues certs. Just add the domain and wait for DNS to resolve. | 30–120 seconds between DNS propagation and cert issuance |
| B. Manual — certbot | SSH to prod, run certbot certonly --nginx -d thehighroadmanufacturing.com -d www.thehighroadmanufacturing.com immediately after DNS resolves |
60–90 seconds |
| C. Pre-provision — DNS-01 challenge | Issue cert BEFORE DNS cutover using DNS TXT record validation instead of HTTP. Requires adding a _acme-challenge TXT record at the current DNS provider |
Zero — cert exists before cutover |
9. Email Continuity
If the domain has email (it probably does), DNS migration affects mail delivery. During the propagation window, some mail goes to the old server and some to the new server.
If Using dpanel Nameservers (Option A)
dpanel auto-creates MX records, SPF, DKIM, and DMARC when a domain is added. Email will work as soon as DNS propagates. Helsinki acts as backup MX — any mail that arrives during the transition is queued and delivered when prod is reachable.
If Keeping External DNS (Option B)
You must manually create these DNS records:
If Old Host (Flywheel) Handled Email
Flywheel is WordPress-only hosting — it does not provide email. Email is likely handled by Google Workspace, Microsoft 365, or another provider. In this case, do not change MX records during migration. Only change the A record. Email continues to work through the existing provider regardless of where the website points.
dig thehighroadmanufacturing.com MX +short. If MX points to Google (aspmx.l.google.com) or Microsoft (outlook.com), leave MX alone during the website migration.
10. Flywheel Transition Bridge
During DNS propagation, some visitors will still hit Flywheel. Instead of showing them the old WordPress site (creating confusion), redirect them to the new site. This eliminates the “mixed content” problem even during propagation.
Option A: WordPress Redirect Plugin
Install a simple redirect plugin on the Flywheel WordPress site that sends all traffic to the new domain. Or add this to the WordPress theme’s functions.php:
Option B: .htaccess Redirect
If Flywheel allows .htaccess modifications:
11. Rollback Procedure
If the new site has critical failures after cutover, revert DNS back to Flywheel. With 300s TTL, this takes effect within 5–15 minutes.
Rollback Steps
- Remove the Flywheel redirect — Undo the transition bridge so Flywheel serves WordPress again
- Change A record back to Flywheel IP — At the DNS provider, revert the A record
- Wait 5–15 minutes — TTL is still low, so propagation is fast
- Verify —
dig thehighroadmanufacturing.com A +shortreturns Flywheel’s IP
When to Rollback
- Site-wide 500 errors not resolvable within 30 minutes
- Payment processing completely broken (Stripe keys, webhook misconfigured)
- SSL cannot be provisioned (cert issuance failing)
- Database connection failures
When NOT to Rollback
- Minor styling issues
- Individual product pages not loading (fix data, don’t rollback)
- Temporary ranking fluctuations in search
- Missing redirects for obscure old URLs (add them, don’t rollback)