A Kubernetes migration does not become safe because a manifest passes YAML validation or because the first Pod reaches Running. It is complete when the application handles real traffic within its objectives, data remains correct, the receiving team can operate it, and there is a practicable exit if behaviour degrades.
The common mistake is to treat cutover as the last infrastructure task. It is a risk decision connecting application behaviour, data, networking, identity, observability and operations. If any element lacks an acceptance criterion, the production window depends on judgement under pressure.
This guide structures the work as decision gates. Each gate requires evidence before the migration advances. It does not assume EKS, AKS, GKE, OKE, OpenShift or any particular provider.
1. Define the migration unit before choosing a wave
“Migrate the application” often hides several units:
- web processes, workers and scheduled jobs;
- databases, queues, caches and object storage;
- DNS, certificates, ingress and external dependencies;
- secrets, workload identities and cloud permissions;
- dashboards, alerts, logs, traces and runbooks;
- build, deployment and rollback pipelines.
The first wave must be representative enough to expose incompatibilities while keeping the blast radius manageable. A stateless service using the same identity, network path and observability stack as critical workloads usually teaches more than an isolated demo.
| Field | Required evidence |
|---|---|
| Owner | Person accepting behaviour and risk |
| Dependencies | Inputs, outputs, DNS, ports, certificates and third parties |
| State | Persistent data, consistency, RPO and RTO |
| Profile | CPU, memory, concurrency, startup and shutdown |
| Operations | SLI/SLO, alerts, runbook and escalation |
| Change | Window, traffic strategy and point of no return |
Gate 1: no workload enters a wave without an owner, known dependencies and acceptance criteria.
2. Validate compatibility against the real cluster
A manifest valid for one cluster may fail in another. Served API versions, admission policies, StorageClasses, IngressClasses, controllers, identity, DNS, CNI and security policy can all differ.
# Use the destination cluster schema and admission policy
kubectl apply --server-side --dry-run=server -f k8s/
# Check what the application identity can actually do
kubectl auth can-i --as=system:serviceaccount:payments:api \
--list --namespace=payments
kubectl get storageclass
kubectl get ingressclass
kubectl get crd
Use the official Deprecated API Migration Guide to find resources the destination no longer serves. kubectl auth can-i queries the authorization layer through SelfSubjectRulesReview, as described in the authorization documentation.
Server-side dry-run does not prove that an image can be pulled, a CSI driver can mount a volume, external DNS resolves, or a private dependency is reachable. Test those conditions from inside the destination.
Gate 2: manifests accepted by the API server, least-privilege access verified, and platform dependencies resolved.
3. Make the workload declare when it can receive traffic
Running describes container state. It does not prove that the application has loaded configuration, connected to dependencies or warmed its caches.
Kubernetes uses a readinessProbe to decide whether a Pod should receive Service traffic. A startupProbe delays readiness and liveness checks until startup completes, according to the official probe documentation.
Illustrative example: replace REPLACE_ME with your real image digest and implement the health endpoints shown. Resource values and timings require validation in your environment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: payments-api
spec:
replicas: 3
selector:
matchLabels:
app: payments-api
minReadySeconds: 30
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
metadata:
labels:
app: payments-api
spec:
terminationGracePeriodSeconds: 45
containers:
- name: api
image: registry.example/payments-api@sha256:REPLACE_ME
ports:
- containerPort: 8080
startupProbe:
httpGet: { path: /health/startup, port: 8080 }
periodSeconds: 5
failureThreshold: 24
readinessProbe:
httpGet: { path: /health/ready, port: 8080 }
periodSeconds: 5
failureThreshold: 2
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { memory: 512Mi }
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: payments-api
spec:
minAvailable: 2
selector:
matchLabels:
app: payments-api
The trade-offs matter:
maxUnavailable: 0requires capacity for the extra Pod; without headroom the rollout may stall.- A shallow readiness check admits traffic too early; one that depends on every third party may remove all replicas during an external failure.
- Resource
requestsaffect scheduling and must come from measurements. Kubernetes explains this in Resource Management for Pods and Containers. - A PodDisruptionBudget limits concurrent voluntary disruptions. It does not prevent node failures or guarantee availability, as the disruptions guide makes clear.
Gate 3: startup, readiness, shutdown and resource behaviour tested under representative load.
4. Separate application rollback from data rollback
kubectl rollout undo can restore a Deployment revision. It cannot reverse a schema migration, consumed messages, writes produced in the destination or changes to an external service.
| Change | Possible reversal | Condition |
|---|---|---|
| Application image | Restore previous digest | Data contract remains compatible |
| Configuration | Restore previous version | Secrets and dependencies remain valid |
| Traffic | Return weight to origin | Origin remains healthy and synchronized |
| Database schema | Do not assume automatic reversal | N/N+1 compatible changes |
| Written data | Reconcile or restore | Tested RPO/RTO procedure |
For databases, prefer expand/contract changes: add compatible structures first, deploy code that can coexist with both versions, migrate or reconcile data, and remove the old representation in a later release. Write down the point of no return. It may be when exclusive writes begin in the new system, not when DNS changes.
VolumeSnapshot objects represent storage snapshots, as described in the Kubernetes storage documentation. Do not turn that capability into a promise of application consistency: a database may require coordination, logical backups, replication or database-native mechanisms. The meaningful test is a complete restore measured against the agreed RPO and RTO.
Gate 4: rollback rehearsed up to the last reversible point, with an explicit treatment for later writes.
5. Drive cutover with user-facing signals
CPU, memory and available Pods are necessary signals, but they do not tell you whether the service works for a customer. Define these before the window:
- success rate for the primary user journey;
- latency by percentile;
- errors by class, not only total 5xx responses;
- worker backlog or lag;
- data consistency;
- dependency saturation;
- a synthetic check from outside the cluster.
Create three criteria: advance, pause and rollback.
Observation window: 30 minutes per step
Advance: success and latency within objective; no data divergence
Pause: degradation without crossing the agreed limit
Rollback: sustained breach, data loss or unexplained error
Steps: internal traffic -> 5% -> 25% -> 50% -> 100%
The percentages and times are not universal. They must reflect traffic volume, seasonality and the ability to detect a regression. With low traffic, five minutes may produce no useful sample; for a critical transaction, waiting thirty minutes may be too long.
Gate 5: dashboards, queries and decision owners ready before traffic is sent.
6. Keep the origin operable through stabilization
Blue/green only makes rollback easier while the previous environment remains healthy and compatible. Turning it off immediately after reaching 100% removes the pattern’s main safety property.
During stabilization:
- freeze unrelated changes;
- retain images and configuration by digest or version;
- name who can order a pause or rollback;
- distinguish origin and destination in logs and traces;
- monitor queues, scheduled jobs and processes that could run twice;
- document when returning to the origin stops being valid.
Kubernetes controls availability during a rolling update through maxUnavailable and maxSurge, documented under Deployments. That protects a rollout inside one cluster. A migration between environments still needs an external traffic mechanism and an explicit data policy.
Gate 6: reaching 100% traffic starts stabilization; it does not close the migration.
7. Close with an operational test
Before retiring the origin, the receiving team should be able to:
- deploy and roll back without the migration team;
- locate logs, metrics and traces for one request;
- drain a node while respecting availability;
- renew certificates and credentials;
- restore data or a volume from backup;
- explain capacity, cost and the next limits;
- run the procedure for a failed dependency.
Topology spread constraints can distribute replicas across zones or nodes, but the policy must match actual topology and capacity.
Gate 7: operations accepted, restoration demonstrated, and residual debt recorded with an owner.
Go/no-go checklist
- The wave has a technical owner and a business owner.
- Dependencies, certificates, DNS, identity and egress are verified.
- The destination API server accepts the manifests.
- Probes, resources, shutdown and distribution have been tested.
- The plan treats application, schema and data separately.
- The origin remains available through the rollback window.
- Advance, pause and rollback criteria are observable.
- One named person is authorized to stop the change.
- Backup and restore meet the agreed objective.
- The receiving team can deploy, diagnose and reverse.
If a critical box depends on “we will check during the window,” there is no migration plan yet—only a bet.
A good migration reduces uncertainty in every wave
Kubernetes can standardize delivery and operations, but it does not remove decisions about data, dependencies and ownership. A good plan uses the first wave to convert unknowns into evidence and makes every later change more predictable.
Before execution, confirm that Kubernetes is the right destination. Nubyron’s guide on when not to migrate to Kubernetes covers that earlier decision. Once the destination is justified, the goal is not to “move Pods”; it is to create an observable, reversible and operable cutover.
See Nubyron’s Cloud and Kubernetes migration approach for turning inventory, dependencies and constraints into verifiable waves.





