DevOps
Is Kubernetes IaC? Clearing Up a Common DevOps Misconception
Is Kubernetes IaC? No. It's an orchestration platform with a declarative API. See how it differs from Terraform and Ansible, with a model interview answer.
No. Kubernetes is not an Infrastructure as Code tool. Kubernetes is a container orchestration platform with a declarative API that keeps workloads converging on the desired state stored in the cluster. Kubernetes manifests become infrastructure as code when you version them in Git and route every change through them, but that discipline comes from your tooling, not from Kubernetes.
The mix-up is easy to fall into. You write YAML, commit it, run kubectl apply, and controllers keep reality matching the file. It feels like Terraform with a different syntax. The difference only shows when someone changes the cluster without touching the repository, and that moment is what the interview question is really testing.
Is Kubernetes an IaC tool?
Kubernetes is not an IaC tool because it does not care where your desired state comes from. The Kubernetes documentation says Kubernetes “does not provide nor mandate a configuration language/system” and instead “provides a declarative API that may be targeted by arbitrary forms of declarative specifications.” A reviewed manifest merged from Git and a kubectl edit typed at 2 a.m. reach the same API and get the same treatment.
That design choice decides the question. Infrastructure as Code is a discipline about the source of truth: definitions live in version-controlled files and changes go through those files. Kubernetes accepts declarative input from any client, stores it in etcd (described in the docs as a “consistent and highly-available key value store for all API server data”), and reconciles the cluster against that stored state. Whether the stored state matches anything in Git is outside what Kubernetes does. If you want the control-plane mechanics behind this, the Kubernetes architecture explainer traces a kubectl apply from the API server through etcd to the kubelet.
The strongest case for calling Kubernetes IaC
The case for calling Kubernetes IaC gets the mechanics right, and a senior answer should say so. Kubernetes manifests are declarative definition files. They sit comfortably in Git. Some of them create real infrastructure: on cloud providers that support external load balancers, setting a Service’s type to LoadBalancer “provisions a load balancer for your Service,” and dynamic volume provisioning “allows storage volumes to be created on-demand” without an administrator calling the storage provider by hand. Projects such as Crossplane and Cluster API go further and use Kubernetes-style APIs to create cloud resources and entire clusters.
Vendor pages about Kubernetes infrastructure as code reflect the split. Mirantis calls Kubernetes “a container orchestration tool, not infrastructure as code” and then names manifest files as “a common example of Kubernetes IaC.” Pulumi defines infrastructure as code for Kubernetes as “the practice of defining a cluster, the workloads that run on it, and the supporting cloud resources around it in version-controlled code.” Both can be true at once because they answer different questions. One is about the tool. The other is about a practice applied to the tool.
So the answer that holds up in a design review is this: Kubernetes is the platform, and IaC is the practice you apply to what runs on it.
What is Infrastructure as Code?
Infrastructure as Code (IaC) is the practice of defining, provisioning and changing infrastructure through machine-readable code kept under version control, instead of through manual changes. No standards body owns the term. The working definition comes from the practitioners and vendors who popularised it, and they agree on more than they disagree:
- Martin Fowler
- (2016, based on Kief Morris’s writing): “Infrastructure as code is the approach to defining computing and network infrastructure through source code that can then be treated just like any software system.” The same article is blunt about the rule that follows: “At no time should anyone log into a server and make on-the-fly adjustments.”
- AWS:
- IaC “is the ability to provision and support your computing infrastructure using code instead of manual processes and settings.”
- Microsoft:
- IaC “uses DevOps methodology and versioning with a descriptive model to define and deploy infrastructure,” and “to make changes, the team edits the source, not the target.”
- Red Hat:
- IaC “is the managing and provisioning of infrastructure through code instead of manual processes.”
Two details in those definitions matter for the Kubernetes question.
First, declarative style is not the test. AWS and Red Hat both describe imperative IaC, where you write the steps instead of the end state. “Kubernetes is declarative, so it is IaC” does not follow from any of these definitions.
Second, every definition puts the truth in the code. Microsoft’s phrasing is the sharpest version: edit the source, not the target.
How Kubernetes measures up against the IaC criteria
These criteria are distilled from the IaC definitions published by Martin Fowler, AWS, Microsoft and Red Hat. The distillation is this article’s reasoning, not a vendor checklist.
| IaC criterion | What Kubernetes does on its own | Meets it? |
|---|---|---|
| Infrastructure is defined in machine-readable files | Accepts YAML or JSON manifests, but does not provide or mandate a configuration language | Consumes the files; does not define the practice |
| Definitions live in version control | Configuration files “can be stored in a source control system such as Git”; Kubernetes itself never reads Git | No: optional and external |
| Applying the same definition gives the same result | kubectl apply works out create, patch or delete per object and resets the fields the file sets; fields the file omits keep whatever value they have live | Partly |
| Changes go through the source, not the target | Accepts writes from kubectl edit, kubectl scale or any API client without checking them against any file, and retains those live changes even when they are never merged back into configuration files | No |
| Provisions the underlying infrastructure | Needs machines and networking to exist first, and “does not provide nor adopt any comprehensive machine configuration, maintenance, management, or self-healing systems”; can request load balancers and volumes through cloud and storage integrations | Partly |
Kubernetes does reasonably well on the criteria about applying declared state, with caveats, and fails the criteria about owning the source of truth. Owning the source of truth is what makes something Infrastructure as Code.
The test that settles it: someone edits the live object
Suppose Git holds a Deployment manifest for checkout with replicas: 3. During an incident, the on-call engineer runs:
kubectl scale deployment/checkout --replicas=6What happens next depends on which layer you ask.
- Kubernetes records
replicas: 6in the Deployment’s spec in the API server. Controllers bring the Pod count up to six and keep it there. From Kubernetes’ point of view nothing has drifted, because the desired state now is six. - Git still says three. Nothing inside Kubernetes notices or cares.
kubectl diff -f checkout.yamlshows the gap. The next plainkubectl apply -f checkout.yamlputs replicas back to 3, because client-side apply sets every field that is present in the configuration file and differs from the live object. Withkubectl apply --server-side, the Kubernetes docs note that areplicasvalue changed bykubectl scaleraises a field-ownership conflict instead, which you resolve explicitly.- Argo CD, if it manages the application, reports it as
OutOfSync. By default, a change made directly to the live cluster does not trigger an automated sync. WithselfHeal: truein the automated sync policy, Argo CD syncs the cluster back to what Git says.

Kubernetes reconciles the cluster to the desired state held in the cluster. An IaC or GitOps workflow is what ties that desired state to reviewed code. The OpenGitOps principles spell out the missing pieces: desired state must be declarative, “versioned and immutable,” “pulled automatically” by software agents and “continuously reconciled.” Kubernetes supplies the declarative API and the reconciliation. It does not supply the versioning or the pull.
spec.replicas from the manifest. Otherwise every kubectl apply resets the Pod count to the file’s value, which can cause thrashing. Doing IaC well on Kubernetes means deciding who owns each field, not only each object.Is Terraform an IaC tool, and how is it different from Kubernetes manifests?
Yes. Terraform is an Infrastructure as Code tool: HashiCorp describes it as “an infrastructure as code tool that lets you build, change, and version cloud and on-prem resources safely and efficiently.” Terraform provisions what Kubernetes assumes already exists, such as networks, compute and managed clusters, and it keeps a state file recording what it manages. Terraform changes infrastructure when you run it, not continuously.
Terraform talks to platforms through providers, which call each platform’s API. Its configuration files are declarative and “describe the end state of your infrastructure.” The workflow has three stages: write the configuration, run a plan that describes what Terraform will create, update or destroy, then apply those changes on approval. Before any operation, Terraform refreshes its state against the real infrastructure. HashiCorp’s state documentation says Terraform uses that state “to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.” By default the state sits in a local terraform.tfstate file. HashiCorp recommends a remote backend or HCP Terraform instead, since keeping state locally limits collaboration and risks losing it along with the file.
If your team runs OpenTofu, the same comparison applies. OpenTofu is a Terraform fork under the Linux Foundation, created after HashiCorp moved Terraform from an open-source licence to the Business Source License, and it describes itself as a drop-in replacement for Terraform.
| Aspect | Terraform | Kubernetes manifests (applied to Kubernetes) |
|---|---|---|
| What it is | An IaC tool: CLI plus providers | Declarative API objects submitted to a container orchestration platform |
| Language | HCL, declarative | YAML or JSON describing API objects (apiVersion, kind, metadata, spec) |
| Typical scope | Compute, storage, networking, DNS, SaaS features and Kubernetes clusters, through provider APIs | Workloads and cluster objects inside a running cluster |
| Record of what exists | State file mapping real resources to configuration | The live objects themselves, held by the API server and backed by etcd |
| Preview of changes | terraform plan | kubectl diff |
| When changes happen | On terraform apply, after approval | As soon as the API server accepts a write; controllers keep converging afterwards |
| Drift | Detected on the next plan; HCP Terraform health assessments can check for it periodically | Controllers correct running resources against the stored spec; a change to the spec itself is not drift to Kubernetes |
| CNCF Landscape placement (September 2026) | Provisioning › Automation & Configuration | Orchestration & Management › Scheduling & Orchestration |
The row that matters most in a design review is “record of what exists.” Terraform’s state file is Terraform’s memory of resources that live somewhere else. Kubernetes has no equivalent file, because Kubernetes objects live in the API server. Calling etcd “Kubernetes’ state file” is half right: etcd holds the cluster’s objects, but it knows nothing about your repository.
Where Terraform and Kubernetes overlap
Terraform is not an alternative to Kubernetes; Terraform can drive Kubernetes. The Terraform Registry lists Kubernetes and Helm providers, and HashiCorp’s Kubernetes provider tutorial makes the case for it: if you already provision Kubernetes clusters with Terraform, you can deploy into them with the same language, get full lifecycle management, and let Terraform’s dependency graph order related resources.
The rough edge in Terraform’s Kubernetes provider is custom resources. The provider’s kubernetes_manifest resource can manage CRDs and custom resources, but at plan time Terraform asks the Kubernetes API for the schema of the kind you declared. If the CRD is not installed yet, the plan fails. You need two apply steps, and HashiCorp’s tutorial recommends putting the CRD in its own workspace in production. Any stack that installs an operator and its custom resources in one go runs into this ordering.
A rule worth adopting: give every Kubernetes object, and ideally every field, exactly one owner. The Kubernetes docs warn that an object “should be managed using only one technique” and that mixing techniques “results in undefined behavior.” That warning is about kubectl’s own management styles, but the logic carries over when Terraform and Argo CD both believe they own a Deployment: each one’s next run can undo the other’s change. Server-side apply, stable since Kubernetes 1.22, helps by tracking field managers in managedFields and raising a conflict when an apply tries to change a field another manager claims. It surfaces the ownership question. It does not answer it for you.
Is Ansible considered IaC, and how does it compare to Kubernetes YAML?
Yes. Ansible counts as Infrastructure as Code under the published definitions of IaC, in a configuration-management style rather than Terraform’s provisioning style. Martin Fowler’s definition names “Ansible playbooks” as an example of IaC definition files, and Red Hat lists Ansible Automation Platform among its IaC tools. Red Hat is just as clear about how Ansible differs from Terraform: Ansible “takes a mostly procedural (or imperative) approach,” is “largely stateless,” and “is built for mutable infrastructure,” meaning in-place changes to running systems. Red Hat contrasts that with Terraform, which it says “prefers immutable” infrastructure and “often favors tearing down the old resource and replacing it with a new, correctly configured resource.”
Ansible’s own introduction says you “declare the desired state of a local or remote system in your playbook.” In execution, a playbook is an ordered procedure: “A playbook runs in order from top to bottom,” and tasks inside each play run top to bottom too. What keeps that procedure safe to re-run is idempotency. The Ansible docs say most modules check whether the desired final state already exists and do nothing if it does, and they add the caveat that matters in production: “not all playbooks and not all modules behave this way.” Ansible is agentless, reaching machines over SSH with existing credentials from a control node; the Ansible architecture and inventory guide covers that model in detail.
One misconception to drop: “Ansible isn’t IaC because it’s imperative.” AWS defines imperative IaC explicitly and says the imperative approach “becomes necessary in complex infrastructure deployments,” especially “when the order of events is critical.”
Ansible YAML vs Kubernetes YAML: same syntax, different meaning
An Ansible playbook and a Kubernetes manifest share YAML syntax and very little else. A Kubernetes manifest is a serialised API object. An Ansible playbook is a program: an ordered list of tasks that a control node executes against hosts.
The contrast is clearest when both describe the same object. Here is a Namespace as a plain Kubernetes manifest:
apiVersion: v1
kind: Namespace
metadata:
name: paymentsAnd here is the same Namespace as an Ansible task, using the k8s module from the kubernetes.core collection:
- name: Create the payments namespace
kubernetes.core.k8s:
name: payments
api_version: v1
kind: Namespace
state: presentBoth end with the same Namespace in the cluster. The manifest is data: hand it to the API server by any route and Kubernetes stores it. The Ansible task is a step: when the playbook reaches it, the module creates the Namespace only if it does not already exist (state: present), and the playbook moves on to the next task. Nothing in a plain playbook run keeps watching the cluster afterwards. For how plays, variables, handlers and roles fit together, see the Ansible playbook tutorial.
| Aspect | Ansible playbook | Kubernetes manifest |
|---|---|---|
| What the YAML represents | Ordered plays and tasks, each calling a module | One API object and its desired state |
| How it runs | Top to bottom from a control node, over SSH or APIs, with no agent on targets | Stored by the API server; controllers act on it continuously |
| State | Largely stateless; modules inspect the target at run time | Object spec and status held by the API server, backed by etcd |
| Re-running it | Safe when the modules used are idempotent, which most are | Re-applying an unchanged manifest to an unchanged object changes nothing |
| Typical use | Configuring hosts, patching, deploying software, multi-step procedures; Kubernetes objects through kubernetes.core | Declaring workloads and cluster objects |
Ansible still has a job next to Kubernetes, and on self-managed infrastructure that job is the machines. Kubernetes does not provide machine configuration or maintenance, so on self-managed nodes something has to install packages, harden and patch the OS, and prepare hosts. The kubernetes.core collection also reaches into the cluster: its README says it helps “automate the management of applications in Kubernetes and OpenShift clusters, as well as the provisioning and maintenance of clusters themselves.”
If Kubernetes isn’t IaC, what category does it belong to?
Kubernetes is a container orchestration platform. The CNCF Cloud Native Landscape places Kubernetes under Orchestration & Management, in the Scheduling & Orchestration subcategory, while Terraform, OpenTofu and Ansible sit under Provisioning, in Automation & Configuration. Architecturally, Kubernetes is best described as a declarative control plane: a set of control loops that keep moving current state toward desired state.
| Project | CNCF Landscape category | Subcategory |
|---|---|---|
| Kubernetes | Orchestration & Management | Scheduling & Orchestration |
| Crossplane | Orchestration & Management | Scheduling & Orchestration |
| Terraform | Provisioning | Automation & Configuration |
| OpenTofu | Provisioning | Automation & Configuration |
| Ansible | Provisioning | Automation & Configuration |
| Helm | App Definition and Development | Application Definition & Image Build |
| Argo | App Definition and Development | Continuous Integration & Delivery |
| Flux | App Definition and Development | Continuous Integration & Delivery |
Landscape placements as listed in the CNCF landscape.yml in September 2026. Categories are occasionally reorganised.
The Kubernetes documentation adds a twist worth knowing. It says Kubernetes “is not a mere orchestration system. In fact, it eliminates the need for orchestration,” and defines orchestration narrowly as “execution of a defined workflow: first do A, then B, then C.” Kubernetes instead “comprises a set of independent, composable control processes that continuously drive the current state towards the provided desired state.” So “container orchestration platform” is the conventional category name, consistent with the CNCF Landscape’s Scheduling & Orchestration placement, and “declarative control plane” describes how it works. By that narrow definition, an Ansible playbook, which runs task A, then B, then C, is closer to orchestration than Kubernetes is.
When Kubernetes becomes the engine for infrastructure as code
The line between Kubernetes and infrastructure as code blurs when the Kubernetes API is extended beyond containers. Crossplane describes itself as “a control plane framework for platform engineering,” and its managed resources extend Kubernetes to manage external resources such as AWS RDS instances. Cluster API is “a Kubernetes sub-project focused on providing declarative APIs and tooling to simplify provisioning, upgrading, and operating multiple Kubernetes clusters.” In both cases a Kubernetes control plane really does provision infrastructure from declarative definitions. Keep those definitions in Git behind a GitOps controller and you have IaC with continuous reconciliation.
Crossplane and Cluster API still don’t make Kubernetes itself an IaC tool. They use Kubernetes’ API machinery as the reconciliation engine for an IaC workflow, with the provisioning logic supplied by the add-on; Crossplane, for example, installs into an existing Kubernetes cluster, so something else still has to build that cluster. Without such add-ons, Kubernetes reaches outside the cluster mainly through cloud-provider and storage integrations, such as external load balancers and dynamically provisioned volumes.
Where each tool usually sits in a real stack
The table shows one common division of ownership. It is a pattern with trade-offs, not a rule.
| Layer | Examples | Typical owner |
|---|---|---|
| Cloud foundation | VPCs, subnets, IAM, the managed Kubernetes cluster, node groups | Terraform or OpenTofu (or Pulumi, CloudFormation) |
| Hosts, where you manage them yourself | OS packages, hardening, patching on self-managed or bare-metal nodes | Ansible |
| Cluster add-ons and workloads | Deployments, Services, Ingress, Helm releases | Manifests and charts in Git, applied by Argo CD or Flux |
| Cloud resources requested from inside the cluster | Databases or buckets exposed as custom resources | Crossplane, where a platform team offers it |

In that pattern, the layers meet at a single hand-off: the provisioning tool builds the cluster and installs the GitOps controller, and the controller takes over everything inside. Pulumi’s guide describes the same pattern, where the IaC tool manages the cluster, IAM and surrounding cloud resources and “seeds the cluster with the GitOps controller.”
Why the distinction matters for a DevOps career
The difference between Kubernetes and an IaC tool is not pedantry. “Is Kubernetes IaC?” is really a question about where the source of truth lives, and that answer drives real decisions.
In design reviews, you are deciding whether Terraform or Argo CD owns in-cluster objects, and who owns spec.replicas once an autoscaler exists. You cannot draw those lines without knowing which system reconciles against what.
During incidents, a kubectl scale is often the right call. Leaving it unrecorded is the mistake: the next sync or apply can quietly undo it, possibly while the incident is still running. Engineers who understand the layering either commit the change back to Git or deliberately pause auto-sync, which Argo CD supports.
For audit and compliance, “every production change is reviewed in Git” is only true if nothing can write to the cluster outside the pipeline. That is an RBAC and GitOps design question. Kubernetes will not enforce it for you.
In interviews for senior DevOps, SRE and platform roles, the question checks whether you can separate provisioning, configuration management and orchestration, and explain each tool’s state model. If you are preparing for a job switch or building a case at appraisal time, a precise, sourced answer is far stronger than “yes, because it uses YAML.” For how these skills build on each other over a career, see the DevOps career roadmap.
A model interview answer to “Is Kubernetes IaC?”
“Not on its own. Kubernetes is a container orchestration platform with a declarative API. Controllers keep reconciling the cluster toward the desired state stored in the API server, which is backed by etcd. Infrastructure as Code is a practice: infrastructure is defined in version-controlled code, and changes go through that code. Kubernetes doesn’t enforce that part. A
kubectl editis accepted exactly like a reviewed manifest, and Kubernetes never looks at Git. So manifests in Git are IaC, and a GitOps tool like Argo CD or Flux closes the loop by reconciling the cluster to the repo. Terraform is an IaC tool for provisioning, with a state file and a plan-and-apply cycle. Ansible is IaC in the configuration-management style: ordered tasks, idempotent modules, no state file. In practice I’d use Terraform for the cloud and the cluster, GitOps for what runs inside it, and Ansible wherever we manage hosts.”
Said at a normal pace, that answer takes about a minute. Adjust the last sentence to match the stack you have actually run.
Follow-up questions that test whether the answer was memorised
What is Kubernetes’ equivalent of the Terraform state file? Kubernetes has no direct equivalent of the Terraform state file. Terraform state maps configuration to resources that exist outside Terraform, while Kubernetes objects exist inside the API server, backed by etcd, so the store is the record. The closest link back to your files is the kubectl.kubernetes.io/last-applied-configuration annotation that client-side kubectl apply writes, or managedFields with server-side apply.
What happens if someone runs kubectl edit on a Deployment that Argo CD manages? If the edit changes a field that the manifest in Git sets, Argo CD marks the application OutOfSync, and what happens next depends on the sync policy. With automated sync but no self-heal, a change made to the live cluster does not trigger a sync, so the edit survives until the next sync, for example one triggered by a new commit. With selfHeal: true, Argo CD reverts the edit to match Git.
Frequently asked questions
Is Kubernetes YAML infrastructure as code?
Kubernetes YAML is infrastructure as code when the manifests are kept in version control and changes to the cluster go through them, whether through a CI pipeline or a GitOps controller such as Argo CD or Flux. A manifest applied by hand and never committed is simply an API request written in YAML.
Is Helm infrastructure as code?
Helm is the package manager for Kubernetes. A Helm chart is a collection of files describing related Kubernetes resources, and its templates combine with values to generate Kubernetes manifests. Charts and their values files kept in Git are part of an infrastructure-as-code workflow. Helm is not a provisioning tool, and the CNCF Landscape lists it under Application Definition & Image Build.
Can Terraform replace Kubernetes?
Terraform cannot replace Kubernetes, because they do different jobs. Terraform provisions and changes infrastructure through provider APIs when you run plan and apply. Kubernetes schedules and runs containers and keeps reconciling them. Terraform can create a Kubernetes cluster and manage objects inside it through its Kubernetes provider, but Terraform does not run workloads.
Is GitOps the same as infrastructure as code?
GitOps applies infrastructure-as-code principles and adds stricter rules about how changes reach the system. The OpenGitOps principles require desired state to be declarative, versioned and immutable, pulled automatically by software agents, and continuously reconciled. Terraform, by contrast, changes infrastructure when a person or pipeline runs apply, so a Terraform workflow can be IaC without being GitOps.
Does Kubernetes replace Ansible?
Kubernetes does not replace Ansible. The Kubernetes documentation says Kubernetes does not provide or adopt machine configuration, maintenance or management systems, and Red Hat describes Ansible as an automation engine “built to configure systems, deploy software, and orchestrate complex tasks”. Ansible can also manage Kubernetes objects through the kubernetes.core collection, so the two tools can work together.
Is Crossplane infrastructure as code?
Crossplane is a control plane framework that extends Kubernetes to manage external resources, such as cloud databases, through custom resources. When those resource definitions live in Git and Crossplane reconciles them, the result is infrastructure as code that uses the Kubernetes API as its engine. Crossplane installs into an existing Kubernetes cluster, so something else still has to provision that cluster.