What's the difference between declarative and imperative management in Kubernetes?

5 minintermediatekubectldeclarativeimperativegitops

Quick Answer

Imperative commands tell Kubernetes exactly what action to take right now (`kubectl run`, `kubectl create`, `kubectl scale`) — simple for one-off tasks, but the history of *how* the cluster got to its current state isn't recorded anywhere reusable. Declarative management (`kubectl apply -f manifest.yaml`) describes the desired end state in a file, and Kubernetes computes and applies whatever changes are needed to reach it — this is version-controllable, repeatable, and safely re-runnable, which is why it's the standard approach for anything beyond quick experimentation.

Detailed Answer

Imperative commands

kubectl run nginx --image=nginx:1.25
kubectl create deployment web --image=myapp:1.0 --replicas=3
kubectl scale deployment web --replicas=5
kubectl delete pod nginx

Each command directly tells Kubernetes an action to perform right now. Fast for quick, one-off tasks and exploration, but there's no durable, reviewable record of the full desired configuration — if you later want to know exactly what flags/settings a running Deployment was created with, you have to inspect the live object rather than read a file, and there's no natural way to track this history in version control.

Declarative configuration

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 5
  template:
    spec:
      containers:
        - name: web
          image: myapp:1.0
kubectl apply -f deployment.yaml

You describe the entire desired state in a file, and kubectl apply computes the diff between that desired state and the cluster's current state, applying only what's needed to reconcile them. Running kubectl apply -f deployment.yaml again with no changes is a safe no-op; running it after editing the file to change replicas: 5 to replicas: 8 scales the deployment, with no separate "scale" command needed.

Why declarative wins for anything beyond experimentation

  • Version control — the YAML files are the source of truth, can be code-reviewed, diffed, and rolled back using ordinary git history, exactly like application source code.
  • Idempotency — re-applying the same manifest is always safe, which makes it trivial to build reliable automation (CI/CD pipelines, GitOps controllers) around it, since "did this already run" doesn't need to be tracked separately.
  • Auditable intent — the manifest describes the complete intended configuration, not just the last command that happened to be run — useful when onboarding someone new to a project, or debugging drift between what's running and what was intended.
  • Foundation for GitOps — tools like ArgoCD and Flux (see the production operations topic) work by continuously reconciling a cluster against manifests stored in a git repository — a workflow that's only possible because the underlying object management is declarative in the first place.

When imperative commands still make sense

Quick debugging (kubectl run -it --rm debug --image=busybox -- sh to spin up a throwaway shell), one-off scaling during an incident before a proper fix is written and reviewed, or simply exploring a cluster's state (kubectl get, kubectl describe, kubectl logs are inherently imperative/read operations, and there's no declarative equivalent that would make sense for them).

Recognizing that declarative management isn't just "using YAML files instead of flags" — it's the practice that makes GitOps, safe automation, and reliable rollback possible — demonstrates understanding of why the Kubernetes ecosystem converged so strongly on this pattern, not just familiarity with the apply command.