Webv3 cutover runbook
Read this before you prepare, run or roll back the switch of tadoku.app from
Webv2 to Webv3, or when a Webv3 auto-revert fires after it.
The production manifests live in the maintainer's GitOps repository; every step there is a reviewed pull request that Argo CD syncs. Never change production by hand.
Phases
- Flagged release. Webv3 serves
next.tadoku.app, and accounts in therelease-webv3-accesssegment are sent there from Webv2, as Webv3 flagged release describes. This is the step before the host switch: grow the segment until the accounts on it use Webv3 without problems. Keep therelease-webv3flag through and after the switch; it is the fastest way to send accounts back to Webv2 if a rollback restores Webv2. - Before the switch. Make Webv3 able to carry all traffic.
- Host switch. Route
tadoku.appto Webv3 in one change. - Afterwards. Keep Webv2 running until the rollback window closes, then
retire it and redirect
next.tadoku.app.
Before the switch
- Replicas and disruption budget. Webv3 runs two replicas spread across
nodes, with a PodDisruptionBudget of
minAvailable: 1and a 5-secondpreStopdelay, so a node drain, an eviction or a rollout never leaves it without a ready pod. Keep all three while Webv3 servestadoku.app. - Environment. Production already sets
TADOKU_WEB_URL=https://tadoku.appandTADOKU_API_URL=https://tadoku.app/api/internal, sotadoku.appis Webv3's web host and becomes indexable once it serves Webv3. Nothing else changes; Webv3 production container lists the variables. - Health checks. Webv2 answers
/api/health; Webv3 answers/healthz. After the switch,https://tadoku.app/api/healthreaches Webv3 and answers 404. Point every monitor or probe of that path at/healthzin the same change. The homelab blackbox probe readshttps://tadoku.app/and needs no change. - Canary.
.github/workflows/webv3-production-canary.yamlsigns in on the host Webv3 serves for everyone:WEBV3_BASE_URLishttps://tadoku.appwhile Webv3 serves it, andhttps://next.tadoku.appotherwise. Change it in the same step as the host, because a canary on Webv2 fails and reverts Webv3. - Request IDs. The tadoku HTTPS listeners and the internal Gateway must keep
an incoming
X-Request-Id, or Webv3's and Tadoku API's log lines will not join oncorrelation_id; see Webv3 server logs.
Host switch
Two GitOps pull requests, with a check between them. Both HTTPRoutes are
attached to tadoku.app in between; for the same path match, Envoy Gateway
routes to the route created first, which is frontend-webv2, so traffic stays
on Webv2 until its route is removed.
-
Admit and attach Webv3.
- Add
tdk-prod-frontend-webv3to thehttps-apexlistener'sallowedRoutesnamespace list on Gatewaytadoku. Without it the listener rejects Webv3's route, and removing Webv2's would leavetadoku.appwithout a page route. - Add
tadoku.appand thehttps-apexlistener to thefrontend-webv3HTTPRoute. Keep the route's name: theTadokuWebv3ServerErrorBurstalert reads the edge access logs of thefrontend-webv3HTTPRoute, sotadoku.appmust stay on it. The Oathkeeper HTTPRoute keeps serving/api/internal, a longer path prefix. - No edge
X-Robots-Tagheader. Thefrontend-webv3HTTPRoute must not setX-Robots-Tag: HTTPRoute filters cannot match a hostname, so withtadoku.appon the same route every page would be noindex. Webv3 itself sendsX-Robots-Tag: noindexfrom every host that is not its web host, as headers, indexing and sharing describes, sonext.tadoku.appstays out of search results without it. The route that servestadoku.appmust add noX-Robots-Tag.
- Add
-
Check admission. After Argo CD syncs, both conditions must be
Truefor thehttps-apexparent before you go on:kubectl -n tdk-prod-frontend-webv3 get httproute frontend-webv3 \-o jsonpath='{range .status.parents[?(@.parentRef.sectionName=="https-apex")].conditions[*]}{.type}={.status}{"\n"}{end}'It prints
Accepted=TrueandResolvedRefs=True. Anything else, or no output, means the route is not admitted onhttps-apex; fix that first. -
Remove Webv2's route. Delete the
frontend-webv2HTTPRoute, which serves onlytadoku.app, and keep thewww-redirectroute beside it.tadoku.appnow reaches Webv3.
Leave the www.tadoku.app redirect and the account and admin hosts as they are.
Verify
After Argo CD syncs:
curl -sI https://tadoku.app/answers 200 with Webv3's headers,X-Content-Type-Options: nosniffand aContent-Security-Policy-Report-Only, which Webv2 does not send, and noX-Robots-Tag.https://tadoku.app/robots.txtallows crawlers, a page links its canonical URL onhttps://tadoku.app, andhttps://tadoku.app/healthzanswers 200.next.tadoku.appanswersX-Robots-Tag: noindexand itsrobots.txtanswersDisallow: /.- An
/api/internalread and signing in and out through the account host work. - Run the canary's signed-in journey against
https://tadoku.appwith aworkflow_dispatch, once its base URL points there, and watch the edge 5xx ratio of thefrontend-webv3HTTPRoute and Webv3'sssr_errorandupstream_failurelines in Loki for the next rollout.
Rollback
-
Host switch. Mirror the switch, again in two pull requests with a check between them. A restored
frontend-webv2HTTPRoute is created afterfrontend-webv3, so Webv3 keeps the traffic until it leaveshttps-apex.-
Restore the
frontend-webv2HTTPRoute. Its namespace is still on the listener's list. -
Check that it is admitted for the
https-apexparent:kubectl -n tdk-prod-frontend-webv2 get httproute frontend-webv2 \-o jsonpath='{range .status.parents[?(@.parentRef.sectionName=="https-apex")].conditions[*]}{.type}={.status}{"\n"}{end}'It must print
Accepted=TrueandResolvedRefs=True. -
Remove
tadoku.appand thehttps-apexlistener from thefrontend-webv3HTTPRoute, and restore itsX-Robots-Tagfilter fornext.tadoku.app.tadoku.appreturns to Webv2. Webv3's namespace can then leave the listener's list.
Point the canary's
WEBV3_BASE_URLback athttps://next.tadoku.appat once, or its next run reaches Webv2, fails and reverts Webv3's image.This needs Webv2 still deployed at its last image, so do not retire it while a rollback is possible. If Webv3 itself is at fault, also empty the
release-webv3-accesssegment, or flagged accounts are sent back to it. -
-
A broken release. The Webv3 auto-revert rolls back the image, not the host; the homelab's Tadoku web alerts runbook describes it for each phase.
When an auto-revert fires
- Check Tadoku API's health first. An API outage makes Webv3's resource pages
answer 503 (status codes), and those 503s count toward
TadokuWebv3ServerErrorBurst, so an API failure can revert a healthy Webv3 release. Reverting Webv3 does not fix the API; restore the reverted release once the API recovers. - A gateway 504 with no Webv3 log line. Nothing on the production path times
out within Webv3's 8-second upstream deadline, so a 504 with no
upstream_failureorssr_errorline means Webv3 did not answer the edge, not that the deadline was skipped. Read that request's edge access log:UTmeans the edge gave up on Webv3, so read Webv3's stderr, where framework errors go, around that time;UCmeans an established connection to the pod was terminated, so check restarts, OOM kills and rollouts at that time;UFmeans the edge could not establish a connection to the pod, so read the line'supstream_transport_failure_reasonandresponse_code_details, such as a refused connection or a TLS error, and the pod's readiness and endpoints; a 504 on the API route means the API timed out, which Webv3 logs as anhttp_status504. A single one is noise; take a pattern to the API owners with the request ID.
After the rollback window
- Retire Webv2's Deployment and HTTPRoute, and the
/api/healthentry of the production config contract. - Redirect
next.tadoku.appwith a route of its own: movenext.tadoku.appand thehttps-nextlistener off thefrontend-webv3HTTPRoute onto a new HTTPRoute fornext.tadoku.apponly, whose rule answers a301totadoku.appas thewww-redirectroute does. Keepfrontend-webv3, under that name, as thetadoku.appbackend route; a redirect on it would sendtadoku.appto itself. The identity API keeps thenext.host on its app allowlist, so sign-ins that return there keep working until then.