Skip to main content

Last updated: April 28, 2026

Kubernetes Deployment YAML

Field-by-field anatomy of a Kubernetes Deployment manifest, plus the Service, ConfigMap, and Secret YAML you almost always need alongside it. Copy-ready Nginx example included.

Written by Mohan Raj Kolavi.

Quick answer

A Kubernetes Deployment YAML uses apiVersion: apps/v1, kind: Deployment, a metadata.name, and a spec with replicas, a selector, and a pod template defining containers and their image, ports, resources, env, and probes. Apply it with kubectl apply -f deployment.yaml.

Nginx Deployment example

A complete, working Deployment that runs three Nginx replicas with sensible CPU and memory bounds:

nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 256Mi

Field-by-field:

  • apiVersion: apps/v1 - the API group and version for Deployments.
  • kind: Deployment - the resource type.
  • metadata.name - unique within the namespace.
  • spec.replicas: 3 - desired pod count.
  • spec.selector.matchLabels - must match the labels in spec.template.metadata.labels, or the Deployment will not own its pods.
  • spec.template - the pod template, used to create each replica.
  • resources.requests / limits - what the scheduler reserves and the maximum the container can consume.

Service manifest

A Deployment without a Service is unreachable from anywhere except the pod itself. The matching ClusterIP Service routes traffic to any pod with the app: nginx label:

nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

port is what other workloads in the cluster call. targetPort is the pod port traffic is forwarded to. Use type: NodePort or LoadBalancer to expose the service outside the cluster.

ConfigMap example

A ConfigMap stores non-secret configuration as key-value pairs, with optional embedded YAML or JSON files:

configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: info
  FEATURE_FLAGS: "search,recommendations"
  config.yaml: |
    server:
      port: 8080
      timeout: 30s

The config.yaml entry uses a literal block scalar (|) to embed a multiline value. See the multiline strings guide for the full block-scalar reference.

Secret example

stringData takes plaintext that Kubernetes base64-encodes when it stores the Secret. Use this form rather than data (which expects values to already be base64) to keep the YAML readable:

secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:
  DATABASE_URL: "postgres://user:pass@db:5432/app"
  API_KEY: "sk_live_xxxxxxxxxxxxx"

Do not commit raw Secret manifests to Git - base64 is encoding, not encryption. Use Sealed Secrets, External Secrets Operator, or SOPS to handle them safely.

Probes and environment injection

Production-grade Deployments wire env vars from a ConfigMap and Secret, and expose readiness and liveness probes so Kubernetes can stop sending traffic to broken pods:

probes-env.yaml (excerpt)
# Same Deployment with health probes and env from ConfigMap + Secret
spec:
  template:
    spec:
      containers:
        - name: api
          image: myorg/api:1.4.2
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: app-config
            - secretRef:
                name: app-secrets
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20

readinessProbe controls when traffic is routed to the pod. livenessProbe controls when the kubelet restarts the pod. Set both - they answer different questions.

Multiple resources in one file

YAML supports multiple documents in a single file separated by ---. This is how a single manifest ships a Namespace, Deployment, Service, and ConfigMap together:

bundle.yaml
# Multiple resources in one file - separated by ---
apiVersion: v1
kind: Namespace
metadata:
  name: production
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: production
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27

Applying with kubectl

  • kubectl apply -f file.yaml - create or update from a single file.
  • kubectl apply -f . - apply every YAML in the directory.
  • kubectl apply --dry-run=client -f file.yaml - validate without contacting the cluster.
  • kubectl diff -f file.yaml - preview the change that apply would make.
  • kubectl rollout status deployment/<name> - wait for the new replicas to become healthy.
  • kubectl rollout undo deployment/<name> - roll back to the previous revision.

Common errors

  • error validating data: unknown field "X" - typo in a field name, or wrong API version. Check the apiVersion first.
  • spec.selector: required value - Deployments require spec.selector.matchLabels matching the pod template's labels.
  • found character that cannot start any token - tabs in indentation. YAML rejects tabs; use two spaces. Run the file through the YAML validator to find the exact line.
  • ImagePullBackOff - not a YAML problem; check the image name, tag, and pull secret.
  • CrashLoopBackOff - container starts and exits. Inspect logs with kubectl logs <pod> and check the readiness probe.

Indentation bugs in Kubernetes YAML are silent until apply time. Drop your manifest into the YAML validator before running kubectl, or use the YAML formatter to normalize indentation across a repo. For shared blocks across multiple manifests, see YAML anchors and merge keys.

Frequently Asked Questions

What is a Kubernetes deployment YAML?

A Kubernetes deployment YAML is a manifest file (typically with a .yaml or .yml extension) that declares a Deployment resource. It tells the Kubernetes control plane which container image to run, how many replicas to maintain, the rolling-update strategy, and the pod template. Apply it with 'kubectl apply -f deployment.yaml'.

What are the four required fields in every Kubernetes manifest?

Every manifest must include 'apiVersion' (the Kubernetes API group and version), 'kind' (the resource type, e.g. Deployment, Service), 'metadata' (at minimum a name, plus optional labels and namespace), and 'spec' (the desired state for that kind of resource).

What is the apiVersion for a Kubernetes Deployment?

Use 'apps/v1'. The older 'extensions/v1beta1' and 'apps/v1beta1' versions are deprecated and removed in modern clusters. ConfigMap, Service, Secret, and Namespace use 'v1' (the core API group).

How many replicas should I set in a Kubernetes Deployment?

Start with three for production-grade web services - that survives a single node failure and allows zero-downtime rolling updates. Use one for stateful or singleton workloads. The replicas field can be changed later with 'kubectl scale' or by editing the YAML and re-applying.

How do I apply a Kubernetes YAML file?

Run 'kubectl apply -f deployment.yaml' to create or update the resources defined in the file. 'kubectl apply -f .' applies every YAML in the current directory. Use 'kubectl diff -f file.yaml' to preview the change before applying.

What is the difference between 'kubectl apply' and 'kubectl create'?

'kubectl create' fails if the resource already exists. 'kubectl apply' creates the resource if absent and patches it if present, using a three-way merge between the YAML, the live object, and the last-applied annotation. Apply is the recommended choice for almost all GitOps and declarative workflows.

How do I pass environment variables to a Kubernetes container?

Three options: hardcode under spec.template.spec.containers[].env (each entry has a name and value), reference a ConfigMap or Secret per-key with valueFrom.configMapKeyRef or valueFrom.secretKeyRef, or pull every key from a ConfigMap/Secret with envFrom.configMapRef or envFrom.secretRef.

How do I set CPU and memory limits in a Deployment YAML?

Add a resources block under each container. 'requests' is what the scheduler reserves; 'limits' is the maximum the container can use. CPU is measured in cores or millicores ('500m' = 0.5 CPU). Memory uses Mi (mebibytes) or Gi (gibibytes). Always set both requests and limits in production.

How do I put multiple Kubernetes resources in one YAML file?

Separate documents with a line containing only three dashes (---). Each document is parsed as a complete manifest. 'kubectl apply -f file.yaml' processes every document in order. This is the standard way to ship a Deployment plus its Service plus its ConfigMap as a single artifact.

Why does my Kubernetes YAML get rejected with 'error validating data'?

The most common causes are wrong apiVersion or kind, indentation issues (always two spaces, never tabs), unknown fields under spec, missing required fields like 'selector' on a Deployment, or quoting numeric strings incorrectly. Run 'kubectl apply --dry-run=client -f file.yaml' to validate before applying for real.

Should I store Kubernetes Secrets as YAML in Git?

Plain Secret manifests are base64-encoded, not encrypted - committing them is equivalent to committing plaintext. Use Sealed Secrets, External Secrets Operator, SOPS-encrypted YAML, or an external secret manager (Vault, AWS Secrets Manager) and reference it from your deployment instead.

What is the difference between containerPort, port, and targetPort?

'containerPort' is the port the container listens on inside the pod. 'port' is the port the Service exposes inside the cluster. 'targetPort' is the pod port the Service forwards to (defaults to 'port' if omitted, but should match the container's containerPort). Get all three right or traffic does not reach the pod.

Kubernetes Deployment YAML: Examples & Reference (2026) | Kolavi Studio