Imperative and declarative Kubernetes configuration

Kubernetes is built around desired state. You describe what resources should exist and controllers continually work to make the live cluster match that desired state.

There are two common ways to change that state:

  • Imperative commands tell Kubernetes to perform an operation immediately.
  • Declarative configuration describes the desired resource state in files and applies those files to the cluster.

Both are useful. The right choice depends on whether you are experimenting interactively or managing repeatable production configuration.

Imperative Kubernetes commands

An imperative command directly asks the API server to create or modify a resource.

For example:

kubectl run nicepod \
  --image=nginx:1.29

Verify the result:

kubectl get pod nicepod

Imperative commands are fast and convenient for development, troubleshooting and one-off administration.

Declarative Kubernetes configuration

The declarative approach stores the desired state in YAML or JSON and uses kubectl apply to reconcile the live object with that configuration.

A Pod manifest might look like this:

apiVersion: v1
kind: Pod
metadata:
  name: nicepod
  labels:
    app: demo
spec:
  containers:
    - name: web
      image: nginx:1.29
      ports:
        - containerPort: 80

Apply it:

kubectl apply -f nicepod.yaml

If the object does not already exist, apply creates it. If the managed configuration changes later, another apply updates the live object.

Preview declarative changes with kubectl diff

A useful modern workflow is to preview the effect of a manifest before applying it:

kubectl diff -f nicepod.yaml

Then apply the change:

kubectl apply -f nicepod.yaml

This makes declarative changes easier to review in scripts, CI/CD pipelines and production operations.

Imperative Deployment example

Create a Deployment from the command line:

kubectl create deployment cooldeploy \
  --image=nginx:1.29

Scale it to three replicas:

kubectl scale deployment/cooldeploy \
  --replicas=3

Check the Deployment:

kubectl get deployment cooldeploy

Declarative Deployment example

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cooldeploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.29
          ports:
            - containerPort: 80

Apply it:

kubectl apply -f deployment.yaml

The manifest now captures the Deployment's intended image, labels and replica count in a form that can be reviewed, versioned and reused.

Namespace examples

Create a Namespace imperatively:

kubectl create namespace coolspace

The declarative equivalent is:

apiVersion: v1
kind: Namespace
metadata:
  name: coolspace
  labels:
    environment: dev

Apply it with:

kubectl apply -f coolspace.yaml

Imperative Service example

Expose the Deployment with a Service:

kubectl expose deployment cooldeploy \
  --name=my-service \
  --port=8080 \
  --target-port=80 \
  --type=LoadBalancer

Then inspect it:

kubectl get service my-service

A LoadBalancer Service requests an external load balancer from a compatible cloud or local implementation. It does not guarantee that every Kubernetes environment will immediately provide a publicly routable IP address.

Declarative Service example

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 80
  type: LoadBalancer

Apply the Service:

kubectl apply -f myservice.yaml

The original article contained a filename typo, myservice-yaml. The correct example uses myservice.yaml.

Imperative does not mean unaudited

Imperative configuration is not inherently unaudited. Kubernetes API requests can be captured by Kubernetes audit logging when the cluster is configured for it.

The real advantage of declarative configuration is that the desired state itself can live in source control. That provides reviewable diffs, change history, pull requests, rollback points and a natural foundation for GitOps workflows.

Why declarative configuration is preferred for production

For production systems, declarative manifests are usually easier to operate because they make the intended cluster state reproducible.

Benefits include:

  • configuration can be stored in Git;
  • changes can be code-reviewed;
  • the same resources can be recreated in another cluster;
  • kubectl diff can preview changes;
  • CI/CD and GitOps tools can automate reconciliation; and
  • configuration drift becomes easier to detect and correct.

When imperative commands are better

Imperative commands remain valuable for:

  • learning Kubernetes;
  • temporary test resources;
  • interactive troubleshooting;
  • quick scaling operations;
  • administrative inspection; and
  • generating a starting point for manifests.

Generate YAML without creating the resource

A useful compromise is to use an imperative generator to produce declarative YAML:

kubectl create deployment cooldeploy \
  --image=nginx:1.29 \
  --dry-run=client \
  -o yaml

Redirect the output to a file:

kubectl create deployment cooldeploy \
  --image=nginx:1.29 \
  --dry-run=client \
  -o yaml \
  > deployment.yaml

You can then edit, review and apply that manifest declaratively.

Do not casually mix management styles

If a resource is managed declaratively with kubectl apply, treat its configuration files as the source of truth.

Ad hoc imperative edits can create configuration drift when those changes are not reflected back into the manifests. The next declarative reconciliation may overwrite them.

For production resources, pick a management model and use it consistently.

Imperative vs. declarative Kubernetes cheat sheet

Task Imperative Declarative
Create Pod kubectl run ... kubectl apply -f pod.yaml
Create Deployment kubectl create deployment ... kubectl apply -f deployment.yaml
Create Namespace kubectl create namespace ... kubectl apply -f namespace.yaml
Create Service kubectl expose ... kubectl apply -f service.yaml
Preview changes Command-specific kubectl diff -f ...
Best fit Interactive work Repeatable operations

The simplest distinction is this: imperative commands describe an action to perform now, while declarative configuration describes the state Kubernetes should maintain. Use imperative commands for speed and exploration, and declarative manifests for repeatable, reviewable production configuration.