What's the difference between declarative and imperative management in Kubernetes?
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 instead of reading 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. They 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. "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. That workflow is 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 — 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 — shows you understand why the Kubernetes ecosystem converged so strongly on this pattern, not just familiarity with the apply command.