Navigate

Where to?
Pick your path.

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.

By SkillfymePublished 15 min read

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 criterionWhat Kubernetes does on its ownMeets it?
Infrastructure is defined in machine-readable filesAccepts YAML or JSON manifests, but does not provide or mandate a configuration languageConsumes the files; does not define the practice
Definitions live in version controlConfiguration files “can be stored in a source control system such as Git”; Kubernetes itself never reads GitNo: optional and external
Applying the same definition gives the same resultkubectl 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 livePartly
Changes go through the source, not the targetAccepts 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 filesNo
Provisions the underlying infrastructureNeeds 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 integrationsPartly

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:

Bash
kubectl scale deployment/checkout --replicas=6

What happens next depends on which layer you ask.

  1. Kubernetes records replicas: 6 in 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.
  2. Git still says three. Nothing inside Kubernetes notices or cares.
  3. kubectl diff -f checkout.yaml shows the gap. The next plain kubectl apply -f checkout.yaml puts replicas back to 3, because client-side apply sets every field that is present in the configuration file and differs from the live object. With kubectl apply --server-side, the Kubernetes docs note that a replicas value changed by kubectl scale raises a field-ownership conflict instead, which you resolve explicitly.
  4. 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. With selfHeal: true in the automated sync policy, Argo CD syncs the cluster back to what Git says.
An on-call engineer runs kubectl scale to set the checkout Deployment to six replicas. Kubernetes records replicas: 6 and runs six Pods, so from the cluster's point of view nothing has drifted. Git still says replicas: 3. kubectl diff shows the gap, plain kubectl apply resets it to 3, and kubectl apply --server-side raises a field-ownership conflict instead. Argo CD marks the application OutOfSync but does not sync automatically unless selfHeal is set to true.
The same event, read four ways. Kubernetes is not confused and nothing inside it has failed — the desired state it stores genuinely is six now. Only Git, and a controller that continuously compares the cluster against Git, can call that a drift.

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.

One production detail belongs next to this example. If a HorizontalPodAutoscaler manages the Deployment, the Kubernetes documentation recommends removing 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.

AspectTerraformKubernetes manifests (applied to Kubernetes)
What it isAn IaC tool: CLI plus providersDeclarative API objects submitted to a container orchestration platform
LanguageHCL, declarativeYAML or JSON describing API objects (apiVersion, kind, metadata, spec)
Typical scopeCompute, storage, networking, DNS, SaaS features and Kubernetes clusters, through provider APIsWorkloads and cluster objects inside a running cluster
Record of what existsState file mapping real resources to configurationThe live objects themselves, held by the API server and backed by etcd
Preview of changesterraform plankubectl diff
When changes happenOn terraform apply, after approvalAs soon as the API server accepts a write; controllers keep converging afterwards
DriftDetected on the next plan; HCP Terraform health assessments can check for it periodicallyControllers 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 & ConfigurationOrchestration & 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:

namespace.yaml — Kubernetes manifest
apiVersion: v1
kind: Namespace
metadata:
  name: payments

And here is the same Namespace as an Ansible task, using the k8s module from the kubernetes.core collection:

create-namespace.yml — Ansible task
- name: Create the payments namespace
  kubernetes.core.k8s:
    name: payments
    api_version: v1
    kind: Namespace
    state: present

Both 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.

AspectAnsible playbookKubernetes manifest
What the YAML representsOrdered plays and tasks, each calling a moduleOne API object and its desired state
How it runsTop to bottom from a control node, over SSH or APIs, with no agent on targetsStored by the API server; controllers act on it continuously
StateLargely stateless; modules inspect the target at run timeObject spec and status held by the API server, backed by etcd
Re-running itSafe when the modules used are idempotent, which most areRe-applying an unchanged manifest to an unchanged object changes nothing
Typical useConfiguring hosts, patching, deploying software, multi-step procedures; Kubernetes objects through kubernetes.coreDeclaring 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.

ProjectCNCF Landscape categorySubcategory
KubernetesOrchestration & ManagementScheduling & Orchestration
CrossplaneOrchestration & ManagementScheduling & Orchestration
TerraformProvisioningAutomation & Configuration
OpenTofuProvisioningAutomation & Configuration
AnsibleProvisioningAutomation & Configuration
HelmApp Definition and DevelopmentApplication Definition & Image Build
ArgoApp Definition and DevelopmentContinuous Integration & Delivery
FluxApp Definition and DevelopmentContinuous 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.

LayerExamplesTypical owner
Cloud foundationVPCs, subnets, IAM, the managed Kubernetes cluster, node groupsTerraform or OpenTofu (or Pulumi, CloudFormation)
Hosts, where you manage them yourselfOS packages, hardening, patching on self-managed or bare-metal nodesAnsible
Cluster add-ons and workloadsDeployments, Services, Ingress, Helm releasesManifests and charts in Git, applied by Argo CD or Flux
Cloud resources requested from inside the clusterDatabases or buckets exposed as custom resourcesCrossplane, where a platform team offers it
Four infrastructure layers stacked with their usual owners. Terraform or OpenTofu owns the cloud foundation of VPCs, subnets, IAM and the managed Kubernetes cluster. Ansible owns self-managed hosts. Inside the cluster boundary, manifests and charts in Git applied by Argo CD or Flux own add-ons and workloads, and Crossplane owns cloud resources requested from inside the cluster. A hand-off arrow shows the provisioning tool building the cluster and installing the GitOps controller, which then owns everything inside.
One common division of ownership, not a rule. The layers meet at a single hand-off: a provisioning tool builds the cluster and installs the GitOps controller, and from there the controller owns everything inside the boundary.

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 edit is 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.

← All articles