Kubernetes Does NOT Make You Cloud Native: Replacement Does
Two conditions decide it, and neither one mentions a cluster: a workload converges by replacing failed instances rather than repairing them, and that the outcome the migration was justified by actually arrives.
Published on:
Last updated on:
Nobody walks into an architecture review asking whether the team is cloud native. What they usually ask about is Kubernetes.
That is the term which budget is attached to, the one in the board deck and the job spec, and by the time anyone looks at the platform bill, the decision to run a cluster was already made somewhere upstream.
Cloud native is what arrives afterwards, doing a different job. It is the justification, the word that makes a cluster sound like a strategy as opposed to a choice somebody made. But the trait that separates the two is actually narrower than most definitions allow.
That is because a cloud-native workload converges by replacement: when an instance fails, the platform underneath will destroy it and stand up a fresh one, with no human in the loop. Conversely, regardless of what or where it happens to be running on, a workload that converges by repair is not cloud native. Kubernetes doesn’t appear anywhere in that test.
That is the first condition, and it is only half of the answer. Nobody moves to Kubernetes for the pleasure of owning a cluster; they do it because they were promised a faster route to production, a smaller bill, or capacity that pegs against demand without anyone getting paged. A workload that converges by replacement and delivers none of these has taken on the mechanism, but missed the point of it. That is not cloud native either, and delivering what was promised is the second condition.
Which is why a team on Cloud Run can be cloud native, and a cluster full of hand-tended monoliths is not. The label justifies nothing on its own; the cluster should earn its place on its own merits.
We have argued in a previous blog post that cloud native is a way of work and not a destination, and that value is not conferred by a technology. That post takes an organisational view, where mindset, capability and the measures a team holds itself to decide whether a modernisation programme was worth doing.
This blog post will take the workload view and ask whether one particular implementation is cloud native. It is the same argument, but pointed at something smaller: the two conditions set out above are what that organisational view looks like when applied to a single workload.
The second condition is where the two views meet. That earlier post asks whether a modernisation programme returned what it promised; the outcome condition asks the same of a single workload. An implementation that never delivers what the move was justified by never becomes cloud native, however the architecture diagram reads.
We at LiveWyer build and run these platforms as part of our platform engineering work, and a fair share of that work involves matching the amount of complexity with the team who will be running it.
The CNCF definition never names Kubernetes
If Kubernetes were what made a workload cloud native, the definition everyone points to would say so, and it does not.
The CNCF definition of cloud native lists containers, service meshes, microservices, immutable infrastructure and declarative APIs, and says these “exemplify” the approach (Exemplify, as opposed to require). The weight sits on properties: implementations that are loosely coupled, resilient, manageable and observable, with automation that lets engineers make high-impact changes with little toil.
That points away from a fixed toolset without settling much, because “resilient and observable” is broad enough to bless anything with a dashboard. Our What is Cloud Native? whitepaper walks through those properties in full, and we have landed on an image which the definition leaves implicit: servers stop being pets and become cattle that change dynamically. We keep coming back to that image, because it draws a line the listed properties never manage to draw.
Those properties are the right things to be reasoning about, and the definition built on them falls short in two directions.
The first direction is that it cannot classify an implementation either way.
Picture two applications on the same cluster: one packaged so that any instance can be thrown away and recreated, and the other is nursed by hand and patched in place. Both are containerised, both are declared through the same API, and both are as observable and as manageable as whoever runs them has chosen to make them. Every property on the list fits both, but only one of them is cloud native.
A definition that cannot tell those two apart cannot settle an argument about either.
The second is that it asks nothing of the result. A team can satisfy every property on the list, and still end up releasing at the same pace, paying the same bill (or possibly paying more), and getting paged as often as they did before the migration. Even though none of the promises hold, the properties however do, and so the definition blesses the implementation regardless.
What this establishes: the foundation that owns the term treats Kubernetes as an example, but never a requirement. It also leaves two gaps. Nothing in it sorts one implementation from another, and nothing in it asks whether the implementation delivered what it was built for.
Neither delegation nor portability defines cloud native
Two answers come up more than any others when engineers are pressed on what cloud native requires, and neither of them appears in the CNCF text.
The first is delegation: a workload is cloud native to the degree that it can hand its operational concerns, scheduling, healing, service discovery, secrets, ingress, to a platform it consumes and does not maintain.
It is a good instinct, but it breaks on Kubernetes itself.
Kubernetes implements scheduling; it does not hand it off. It provides healing and service discovery to everything above it, not to services it consumes from below. Under a delegation-first definition, the most cloud-native technology in the industry scores close to zero. A definition which throws out etcd and CockroachDB is one which is measuring the wrong thing.
Netflix breaks it from the other side. The architecture which everyone studied in the mid 2010s put circuit breakers, client-side load balancing and service discovery inside the application as libraries, because the platform of the day did not offer them. That design delegated almost nothing, and remains one of the clearest examples of cloud native anyone has yet built.
The second is portability: that is, how easily the contracts a workload depends on could be served by a different implementation. It certainly captures why lock-in feels un-cloud-native, and it breaks on serverless.
An AWS Lambda function delegates almost everything, and is about as portable as wet cement since the event contracts, identity model and surrounding services are specific to one provider. Most engineers, however, would still call it cloud native.
Both answers describe something true about cloud-native systems, and neither holds up as the test. Seeing why takes more than one example at a time.
Only convergence by replacement sorts the implementations
Let’s take a handful of implementations, pick the boundary each is evaluated at, and score them on delegation and portability. Let’s also see if the implementation converges by destroying and recreating instances, or by repairing the ones it has:
| Implementation | Boundary | Delegation | Portability | Converges by | Verdict |
|---|---|---|---|---|---|
| 12-factor app on Kubernetes | app to cluster | High | High | Replacement | Cloud native |
| Containerised pets on Kubernetes | app to cluster | High | High | Patched in place | Not cloud native |
| AWS Lambda function | function to provider | High | Low | Replacement | Cloud native |
| Netflix OSS on EC2 (mid 2010s) | app to IaaS | Low | Medium | Replacement | Cloud native |
| Kubernetes itself | cluster to IaaS | Medium | High | Replacement | Cloud native |
| Managed monolith on pet servers | app to provider | Medium | Low | Manual repair | Not cloud native |
| Puppet-managed VM fleet | app to VMs | Low | Low | Repair, in place | Not cloud native |
| Classic vSphere with HA and DRS | VM to hypervisor | Low | Low | Relocation and revival | Not cloud native |
Looking at this, we can’t rely on either the delegation score nor the portability score to predict the verdict on whether an implementation is “cloud native”. Netflix is low on delegation and cloud native; Lambda is low on portability and cloud native all the same.
Containerised pets scores high on both, but is not cloud native at all. Interestingly, it runs on Kubernetes at the same boundary as the 12-factor row directly above it, and gets the opposite verdict. It is also the case we meet most often with our clients.
In practice, it could be composed of:
- A
Deploymentpinned to one replica, because a second copy would run every scheduled job twice; - Configuration written into the container after it started;
- A
PersistentVolumeClaimholding state that exists nowhere else.
Everything about it is containerised, but none of it is disposable. Deleting a pod is a change-controlled event as opposed to how the implementation heals, and the team will tell you, correctly, that they are running Kubernetes. The cluster is not what decides.
That leaves the “converges by” column doing all of the work, and it is the column that needs the most care. Plenty of platforms converge continuously without ever replacing an instance.
Replacement is not the same as reconciliation
Convergence covers two behaviours that look alike from a distance; That is, if a platform can hold a declared state by repairing what it has, or by replacing it, and only the second of which is cloud native. Both of these can be described as reconciliation, which is where the confusion starts.
A compute workload which is cloud native runs as fungible instances, and the platform underneath holds their number and placement at a declared state by standing up fresh ones and destroying failed ones, with no human in the loop. These instances are often described as cattle. The mechanism keeping the herd at its stated size is reconciliation, which we have written about separately in the context of Crossplane reaching past the cluster into cloud infrastructure.
On its own, reconciliation is not the test, which is where most summaries go wrong. Puppet and Chef reconcile continuously, converging each machine to a declared manifest every time the agent runs. If reconciliation were sufficient, then configuration management from 2010 would qualify. What Puppet actually reconciles is the internal state of named, long-lived machines. It grooms what could be described as pets.
Classic vSphere lands in the same spot from the infrastructure side. HA restarts a failed VM on a surviving host; DRS then vMotions a running one to rebalance load. In both cases, it is the same VM, the same VMDK, the same MAC address and the same hostname, moved or revived.
The features vSphere makes first-class say the same thing. Reverting a snapshot restores a broken VM to an earlier state, which is repair with a button on it. Cloning does exist, but it is how a new VM gets provisioned, not how a sick one gets fixed. Nothing in day-to-day operation swaps a machine out for a fresh copy.
The distinction: Puppet and classic vSphere both reconcile continuously, and what each of them reconciles is the internal state of named, long-lived machines. That is why reconciliation on its own settles nothing: the test is what the reconciliation runs over.
The muscle memory that comes with years of repairing named machines outlasts the platform it was learned on. When a VM misbehaves, the runbook says, “log in and look”, and that runbook survives a migration intact.
We have written about the operational gap between vSphere and KubeVirt, and the habit of repairing instead of replacing is what opens that gap. Moving the workloads is tractable, but moving from repair to replacement takes longer. Deciding which workloads are worth taking through that change, and which should be lifted across as they stand, is the question we work through in our Leaving VMware blog.
That is ultimately the clause the CNCF definition needs: reconciliation over resources that are fungible and disposable. It carries immutability with it, because you do not patch a head of cattle in place, you stand up a new one from the same template and retire the old.
All of this applies to compute. A static site on object storage behind a CDN has no running instances to reconcile, but most people would call it cloud native anyway. Where there are instances to keep alive, replacement is the mechanism that matters.
The outcome has to arrive
The mechanism is necessary, but a team can build all of it and still have nothing to show for it. This highlights the second gap in the CNCF definition, and is arguably the more expensive one.
Whatever was promised when the migration was signed off is the benchmark. A workload can run the mechanism perfectly, and still ship no faster and cost no less than it did before. A project that ends there has ultimately paid for the machinery without the benefit. The second condition is that what was promised will turn up.
That is also the limit of the table above. It scores archetypes on the theoretical build, which is only as far as a table can go, as any of those rows can also be implemented badly by comparison. If a particular 12-factor application on a particular cluster can converge by replacement all day and return nothing anybody was promised, then that deployment is not cloud native, regardless of how its row reads.
The second condition is the one that can come back negative. A team can hold the platform up against its own business case and find the answer is no, which is exactly what a definition needs to allow.
So does it have to be Kubernetes?
Nothing about convergence by replacement points at Kubernetes in particular. It is a pattern, and you already have convergence if you are running:
- an autoscaling group with health checks;
- a GitOps controller applying declared state from a repository;
- a serverless platform holding capacity against demand;
- a scheduler such as Nomad;
- a managed container platform like Cloud Run or Fargate.
None of these are Kubernetes.
What Kubernetes does have is reach. Off the shelf, it applies the reconciliation model to more of the operational surface than anything else, exposing the same convergence behaviour for a Deployment, a StatefulSet and any custom resource an operator introduces. That breadth is mostly why the Kubernetes API surface looks as large as it does to anyone meeting it for the first time.
That in itself is a strong case for reaching for it. It is not the case that a team on Cloud Run is somehow not cloud native, nor that installing Kubernetes is what makes a team in itself cloud native. A cluster running pets with manual deploys clears none of the bar the label was meant to set.
What this establishes: [Kubernetes}(/) is the widest-reaching implementation of the pattern, which makes it a sound default for anyone who needs that reach. Autoscaling groups, GitOps controllers, Nomad, Cloud Run and Fargate clear the same bar without a cluster in sight.
The two conditions settle what the word means: that failed instances get replaced instead of repaired, and that the migration delivers what it promised. These do not settle or provide guidance on what to run it on, which is a separate decision, weighed on the standing cost of a cluster against the ceiling of what a managed platform will do for a team.
The first condition is cheap to check. Pick the workload that matters most, delete one of its instances during working hours, and watch what happens next. If something human has to happen before the implementation is whole again, the mechanism is not there, whatever the manifest says.
The second condition takes a bit longer to answer, and it starts by rereading the business case that justified the move.
Frequently asked questions
Is an implementation only cloud native if it runs on Kubernetes?
No. What defines cloud native is convergence by replacement: something underneath destroying and recreating fungible instances to hold a declared state. Plenty of things do that with no cluster involved, and Kubernetes is the widest-reaching implementation of the pattern, which makes it a strong default. A workload still has to deliver the outcome the move was justified by, so running Kubernetes settles neither half of the question.
Databases have stable names and persistent volumes. Can they be cloud native?
Yes, and this is where the mechanism takes its hardest hit. A StatefulSet pod has a stable name, a stable network identity and a PersistentVolumeClaim that outlives it, all of which reads like a pet. A test which excluded databases would have the same problem as a delegation-first definition, which throws etcd and CockroachDB out of the category it was meant to describe.
Identity and data are not the same thing as the instance. When a Postgres pod under CloudNativePG fails, the operator does not open a shell and repair it. It destroys the pod and schedules a new one, re-attaching the volume where that is safe and rebuilding from a base backup and the write-ahead log where it is not. The name survives, the data survives, but the running instance does not. A volume does not make a workload a pet, but being unable to rebuild from it does.
