Published Sep 16, 2025 ⦁ Updated Oct 11, 2026 ⦁ 16 min read
Kubernetes Orchestration Example: Deploy, Scale, Update

Kubernetes Orchestration Example: Deploy, Scale, Update

Kubernetes orchestration means you describe the state you want, such as "run three copies of this container, reachable at this address, with this config", and Kubernetes keeps the cluster in that state. It schedules containers onto nodes, restarts them when they crash, scales them with load, and swaps versions without downtime. The example below runs that workflow end to end on a local cluster. Every manifest is copy-pasteable, and each step shows which orchestration feature does the work.

As of October 2026 the current release is Kubernetes 1.37, and 1.36 and 1.35 are still supported (kubernetes.io/releases). Everything here uses stable APIs (apps/v1, v1, autoscaling/v2, batch/v1) that work on any supported version.

What the example covers

Step Kubernetes object Orchestration job
1 Cluster (kind) Nodes to schedule onto
2 ConfigMap, Secret Configuration kept out of the image
3 Deployment Desired replicas, health checks, self-healing
4 Service Stable address and load balancing across pods
5 HorizontalPodAutoscaler Scaling with CPU load
6 Deployment rollout Zero-downtime updates and rollback
7 CronJob Scheduled batch work
8 Gateway API Traffic from outside the cluster

Step 1: Create a local cluster

kind runs Kubernetes nodes as Docker containers, which is the quickest way to get a disposable cluster. You need Docker and kubectl.

brew install kind            # or: go install sigs.k8s.io/kind@latest
kind create cluster --name demo
kubectl get nodes

minikube or Docker Desktop's built-in Kubernetes work too. The manifests below don't change.

Step 2: Configuration with a ConfigMap and a Secret

Keep settings out of the container image so the same image runs in every environment.

# config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  APP_MODE: "production"
  LOG_LEVEL: "info"
---
apiVersion: v1
kind: Secret
metadata:
  name: web-secret
type: Opaque
stringData:
  API_KEY: "replace-me"

Secrets are only base64-encoded by default, not encrypted. In a real cluster, turn on encryption at rest and limit who can read Secrets with RBAC (Kubernetes docs: Secrets).

Step 3: A Deployment that heals itself

The Deployment is the core of the workflow. It tells Kubernetes how many pods to run, what's in them, and how to tell whether they're healthy.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.28
          ports:
            - containerPort: 80
          envFrom:
            - configMapRef:
                name: web-config
            - secretRef:
                name: web-secret
          resources:
            requests:
              cpu: 100m
              memory: 64Mi
            limits:
              memory: 128Mi
          readinessProbe:
            httpGet:
              path: /
              port: 80
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10
kubectl apply -f config.yaml -f deployment.yaml
kubectl get pods -l app=web

What each part does:

  • replicas: 3: the ReplicaSet the Deployment creates keeps exactly three pods running.
  • resources.requests: the scheduler uses these to choose a node with room. The HPA in step 5 also needs a CPU request to compute utilization.
  • readinessProbe: a pod gets traffic only after it passes. livenessProbe: a container that stops responding gets restarted.

Now test self-healing. Delete a pod and watch a replacement appear within seconds:

kubectl delete $(kubectl get pods -l app=web -o name | head -n 1)
kubectl get pods -l app=web -w

That's orchestration at its simplest: you never told Kubernetes to start a new pod. It saw that the actual state (two pods) didn't match the desired state (three) and fixed it.

Step 4: A Service for a stable address

Pods come and go, and their IPs change. A Service gives them one stable name and load-balances across whichever pods are ready.

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
kubectl apply -f service.yaml
kubectl port-forward service/web 8080:80   # then open http://localhost:8080

Inside the cluster, other pods reach it at http://web (or web.default.svc.cluster.local).

Step 5: Autoscaling with a HorizontalPodAutoscaler

The HPA needs resource metrics, which come from Metrics Server:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

On kind, Metrics Server usually can't verify the kubelet certificates. For a local demo only, add --kubelet-insecure-tls to its container args with kubectl -n kube-system edit deployment metrics-server.

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 3
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
kubectl apply -f hpa.yaml
kubectl get hpa web -w

When average CPU goes above 60% of the requested 100m, the HPA adds pods (up to 10). When load drops, it scales back down after a stabilization window. The official HPA walkthrough includes a load generator if you want to watch it happen.

Step 6: Rolling update and rollback

Change the image and Kubernetes replaces pods one at a time. Because of maxUnavailable: 0, it only removes an old pod once a new one passes its readiness probe.

kubectl set image deployment/web web=nginx:1.29
kubectl rollout status deployment/web
kubectl rollout history deployment/web

If the new version misbehaves:

kubectl rollout undo deployment/web

In practice, make the change in deployment.yaml and kubectl apply it (or let a GitOps tool such as Argo CD or Flux apply it from Git), so the file stays the source of truth.

Step 7: Scheduled work with a CronJob

Batch tasks such as reports, cleanups and backups get the same scheduling and retry machinery:

# cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-report
spec:
  schedule: "0 3 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: report
              image: busybox:1.36
              command: ["sh", "-c", "date; echo generating report"]

Trigger it once without waiting for 3 a.m.: kubectl create job --from=cronjob/nightly-report test-run.

Step 8: Getting traffic in from outside

For production traffic, Kubernetes' recommended API is now the Gateway API, which replaces the feature-frozen Ingress API. This matters if you followed older tutorials: the popular ingress-nginx controller was retired in March 2026 and no longer gets security fixes (Kubernetes blog). With a Gateway controller installed (Envoy Gateway, Cilium, Istio, or your cloud's), routing to the Service looks like this:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web
spec:
  parentRefs:
    - name: my-gateway
  hostnames: ["app.example.com"]
  rules:
    - backendRefs:
        - name: web
          port: 80

Clean up the demo with kind delete cluster --name demo.

What happens behind the scenes

When you run kubectl apply, the API server validates the object and stores it in etcd. Controllers in the controller manager (Deployment, ReplicaSet, HPA, CronJob) watch for differences between desired and actual state and create or delete objects to close the gap. The scheduler places new pods on nodes with enough requested capacity, and the kubelet on each node starts the containers and runs the probes. Each component does one job, and together they produce the self-healing, scaling behavior above (Kubernetes components).

When Kubernetes is the wrong tool

Kubernetes adds real operational work: upgrades every few months (each minor version gets about 14 months of patches), networking, storage, security hardening and monitoring. For one or two services, a platform as a service or a managed container service is usually cheaper in engineering time. See free Heroku alternatives and DevHunt's hosting tools. If you do run Kubernetes, harden your images and cluster from the start. Our STIG container hardening guide covers that side, and DevHunt's DevOps category lists newer deployment tools.

FAQ

What is orchestration in Kubernetes?

It's the automated management of containers across a cluster: placing them on nodes, restarting failed ones, scaling replicas, routing traffic and rolling out new versions. You declare the desired state in YAML, and Kubernetes controllers keep reconciling the cluster to match.

What are examples of container orchestration tools?

Kubernetes (including managed versions such as Amazon EKS, Google GKE and Azure AKS), Docker Swarm, HashiCorp Nomad and Amazon ECS. Kubernetes is the most widely used.

Is Docker an orchestration tool?

Docker on its own builds and runs containers on a single host. Docker Swarm mode adds basic orchestration across hosts. Kubernetes doesn't need Docker: it runs containers through a CRI runtime such as containerd, though images built with Docker work fine.

What's the difference between a Deployment and a ReplicaSet?

A ReplicaSet keeps a fixed number of identical pods running. A Deployment manages ReplicaSets for you and adds rolling updates, rollout history and rollback. You almost never create ReplicaSets directly.

Which Kubernetes version should I use?

As of October 2026, Kubernetes 1.37 is the newest release, and 1.36 and 1.35 still get patches. Managed providers usually trail upstream by a version or two, so use the newest version your provider offers.