Migration recovery for a B2B SaaS platform
A cloud migration completed on schedule, but stability got worse. Incidents increased, delivery slowed, and the team lost confidence in the platform.
- Routing and latency inconsistencies after the move.
- Permissions drift and unclear ownership boundaries.
- State and config changes done manually to survive.
- Deployments became risky and unpredictable.
The work started with one question: what is actually breaking the revenue path?
This was not a migration retrospective. It was a recovery engagement built to stop repeat instability without forcing a second re-platform.
The first retained view tied routing, identity, and runtime dependencies to concrete failure paths so the team could stop guessing which post-migration issues were actually coupled.
The platform team did not need abstract "optimization." They needed a bounded recovery sequence that contained live risk first, then restored predictable delivery.
Contain, trace, then repair
The sequence mattered. The job was to stop more risky changes from landing while the actual failure chain was mapped.
First 72 hours
Freeze unsafe deploy changes, isolate the unstable traffic path, and reconcile the highest-risk permission and runtime overrides.
Next 2 weeks
Remove the legacy route coupling, normalize ingress behavior, and restore a release path that did not depend on tribal knowledge.
Context
Growth-stage SaaS platform, lean platform team, minimal downtime tolerance, and no appetite for another full redesign.
Success criteria
Critical-path latency stabilized, rollback confidence restored, and ownership became clear enough for the internal team to operate safely.
What the team kept after the recovery work
The retained assets were designed to survive after the immediate incident pressure dropped.
Symptom: latency spikes after cutover Root cause: mixed ingress + legacy route ownership Impact: checkout errors during peak demand First fix: normalize ingress + remove legacy fallback Guardrail: release gating + owner sign-off
Why this was useful internally
- Business impact was tied to named technical causes, not vague migration anxiety.
- The platform team had a safe sequence instead of parallel uncoordinated fixes.
- Ownership boundaries were explicit enough for follow-through after handoff.
Verifying a post-migration cluster nobody could vouch for
Anonymized specifics from a real engagement. Included because the useful part of a migration readout is what was actually checked.
The situation. A Kubernetes cluster running roughly nine microservices came back online after a billing pause and a node recycle. The client’s position was that production was healthy. Nobody could produce anything to support that, and the platform had already been through a migration from self-managed Kubernetes to a managed service.
What was actually wrong. Nothing was on fire, and that is the part worth writing up: the claim was unverified, not false. There was no evidence either way, and after a node recycle that is a real risk, because pods reschedule, volumes rebind, certificates approach expiry and controllers restart without anyone watching. The audit did surface one genuine anomaly. The cloud provider’s own cloud-controller-manager and CSI controller Deployments were not present where they should have been. On a managed Kubernetes service those are normally provider-managed, so their absence is either a serious fault or an artifact of the provider running them outside user-visible namespaces.
What I changed. Rather than guess, I audited the running cluster layer by layer: nodes and their pressure signals, kube-system components, ingress, cert-manager, the GitOps applications, storage and persistent volume claims, and the workloads. On the missing controllers I reasoned it out instead of escalating: load balancers were provisioning, volumes were attaching and ingress was routing, and those three facts can only be true if the controllers are running somewhere. The correct conclusion was that the provider hides them, and the correct action was to record it and move on rather than manufacture an incident.
The result. A written health report the client could hand to someone. Three worker nodes Ready with no memory, disk, PID or network pressure, and node memory between roughly 52% and 73%. Cilium CNI running 3/3. CoreDNS 2/2 with seven restarts about fifteen hours earlier, consistent with the node recycle rather than a fault. Ingress-NGINX healthy at 2/2 available. TLS certificates valid, persistent volume claims bound with no data loss, GitOps applications synced.
And the gaps, named rather than smoothed over: no resource guardrails, no external uptime probes, no PodDisruptionBudgets. The client got a defensible answer instead of a shrug, in both directions.