Kubernetes manifests

Ship workloads with aligned labels/selectors, sensible probes, resources, and securityContext, plus ConfigMap/Secret conventions. This page walks flow → workload → networking → probes → resources → security → policy → validation → snippet lab.

The SKILL defines namespaces, standard labels (app, version, team), and Deployment rolling-update strategy; document Service types and when to use headless.

Tune liveness/readiness/startup probes for slow starts; list security defaults such as readOnlyRootFilesystem, dropped capabilities, and runAsNonRoot per cluster policy.

If HPA, PDB, and NetworkPolicy live in the same repo, state apply order and minimal examples; note image pull policy and imagePullSecrets differences per environment.

  • Scaling: pick one guidance path for replicas, anti-affinity, and topology spread.
  • Storage: when PVC vs StatefulSet applies.
  • Validation: where kubectl apply --dry-run=server or kubeconform runs in CI.

From repo draft to schedulable cluster (flow)

  [ Manifest dir / Kustomize or Helm ]
        │
        ▼
  ┌─────────────┐     Align: labels & selector, name prefix, standard annotations
  │  Workload    │──── Deployment / SS / DS: strategy, revisionHistoryLimit
  └─────────────┘
        │
        ▼
  ┌─────────────┐     Type: ClusterIP / LB / headless; port & targetPort
  │  Service     │──── Match Pod port names; avoid magic numbers
  └─────────────┘
        │
        ▼
  ┌─────────────┐     startup → readiness → liveness; resources required
  │ Pod template │──── securityContext; probe timeouts/thresholds explained
  └─────────────┘
        │
        ▼
  ┌─────────────┐     dry-run=server / kubeconform; namespace & quotas
  │ Validate     │──── Change notes: image tag, resource changes, probe tweaks
  └─────────────┘

Merge order: align selectors with Service ports first, then probes and resources; slow-start workloads need startupProbe so liveness does not kill pods prematurely.

Labels, Deployment, rolling updates

metadata.labels, spec.selector, and template.metadata.labels must match; version labels aid observability and rollbacks—avoid latest alone.

  • strategy.type: for RollingUpdate, document maxSurge / maxUnavailable and team caps.
  • revisionHistoryLimit caps old ReplicaSets; if canaries live behind a gateway/Argo, document boundaries vs native rolling updates.

Complete Deployment YAML (all recommended fields):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
    version: "1.2.3"
    team: platform
    environment: production
spec:
  replicas: 3
  revisionHistoryLimit: 5
  selector:
    matchLabels:
      app: myapp          # immutable; must match template.labels exactly
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1         # allow 1 extra Pod during update
      maxUnavailable: 0   # always keep replicas Pods available
  template:
    metadata:
      labels:
        app: myapp
        version: "1.2.3"
    spec:
      terminationGracePeriodSeconds: 30
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: myapp
      containers:
        - name: myapp
          image: myregistry/myapp:1.2.3-abc1234
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 3000
              protocol: TCP
          env:
            - name: NODE_ENV
              value: production
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: myapp-secrets
                  key: db-password
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 1Gi
          securityContext:
            runAsNonRoot: true
            runAsUser: 1001
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: tmp
          emptyDir: {}

Service & headless

ClusterIP is default; external entry via LoadBalancer or Ingress per platform docs. Prefer named container ports for port / targetPort.

  • clusterIP: None for StatefulSet peer discovery or client-side LB; document DNS shape and client responsibilities.

Complete Service + Ingress configuration example:

---
apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
spec:
  type: ClusterIP
  selector:
    app: myapp              # must match Deployment template.labels
  ports:
    - name: http
      port: 80
      targetPort: http      # reference container port name, not magic numbers
      protocol: TCP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    # cert-manager auto-issues TLS certificate
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - myapp.example.com
      secretName: myapp-tls
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp
                port:
                  name: http

Liveness / readiness / startup probes

readiness gates traffic; liveness restarts the container on failure; startup runs only during boot, then hands off to the other two.

Prefer

  • HTTP health paths that mean “can serve traffic”; avoid heavy deps in readiness that drain the whole fleet.
  • Slow starts: add startup first, then tighten liveness initialDelaySeconds.
  • Align timeoutSeconds, periodSeconds, failureThreshold with SLOs and comment why.

Avoid

  • Identical, overly strict liveness and readiness causing restart flaps or permanent NotReady.
  • Downstream calls inside probes causing cascade timeouts (unless explicitly required).
  • Exec probes without timeouts or with heavy shells.

Requests, limits & QoS

requests inform scheduling and HPA; limits cap usage. Requests without limits are often Burstable; requests=limits (and not BestEffort) tends toward Guaranteed (see official QoS rules).

  • Avoid BestEffort in production; leave headroom for off-heap and stacks on Java/Go runtimes.
  • Document latency, GC, and OOM risks when changing CPU/memory; large batch jobs may need a separate workload class.

securityContext & volume mounts

Combine pod- and container-level securityContext: runAsNonRoot, readOnlyRootFilesystem, allowPrivilegeEscalation: false, capabilities.drop: ["ALL"], add capabilities back only as allowlisted.

  • ConfigMap/Secret: consistent read-only mount paths; document defaultMode and who may apply sensitive volumes under RBAC.
  • imagePullPolicy and imagePullSecrets differ by environment (CI, staging, prod).

HPA, PDB, NetworkPolicy

Document HPA signal sources (CPU, custom metrics, KEDA) and minimum replicas; review PDB minAvailable / maxUnavailable with release strategy.

  • NetworkPolicy: default-deny vs default-allow is platform-specific; generated policies must name namespaces and label selectors explicitly.

HPA configuration example (CPU + custom metrics) + PDB:

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70    # CPU utilization target
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second   # custom metric (requires custom.metrics.k8s.io)
        target:
          type: AverageValue
          averageValue: "100"
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300  # scale-down stabilization: prevent rapid flapping
      policies:
        - type: Percent
          value: 25
          periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
        - type: Pods
          value: 4
          periodSeconds: 30

---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: myapp
  namespace: production
spec:
  minAvailable: 2     # or maxUnavailable: 1, coordinated with replicas and rolling update strategy
  selector:
    matchLabels:
      app: myapp

---
# securityContext full configuration example
# Under Deployment spec.template.spec:
# securityContext (Pod-level)
#   runAsNonRoot: true
#   runAsUser: 1001
#   runAsGroup: 1001
#   fsGroup: 1001
#   seccompProfile:
#     type: RuntimeDefault
# securityContext (container-level)
#   allowPrivilegeEscalation: false
#   readOnlyRootFilesystem: true
#   capabilities:
#     drop: ["ALL"]
#     add: ["NET_BIND_SERVICE"]    # add back only when listening on ports below 1024

Validation & CI

  • kubectl apply --dry-run=server or kubectl diff; kubeconform/kubeval version sets must match clusters.
  • Policy admission (OPA Gatekeeper, Kyverno) errors should map back to manifest field names.

Probe & resources snippet lab

Generate paste-ready YAML for containers[] from port, probe type, and startup/readiness toggles (includes ports, probes, optional resources); tune for real health checks and capacity before merge.

Probes
resources

With startupProbe, liveness/readiness often use initialDelaySeconds: 0 as shown—adjust failureThreshold and periodSeconds for real startup time.

Further reading

  • Assigning CPU and memory resources (Kubernetes docs) — requests, limits, and scheduling
  • Container probes (Kubernetes docs) — the three probe types and fields
  • Pod Security Standards (Kubernetes docs) — baseline aligned with securityContext
---
name: kubernetes-manifests
description: Author production-ready K8s Deployment/Service manifests
tags: [kubernetes, k8s, devops, deployment]
---
# Workloads
1. labels and selector strictly aligned: metadata.labels / spec.selector / template.labels
2. Rolling update: maxSurge=1, maxUnavailable=0 ensures continuous service
3. revisionHistoryLimit: 5 limits stale ReplicaSet accumulation
4. topologySpreadConstraints spreads Pods across availability zones

# Networking
5. Service targetPort references container port name (no magic numbers)
6. Ingress with TLS + cert-manager auto-issuance, ssl-redirect: true
7. ClusterIP is default; LoadBalancer fixed by platform docs; headless documents DNS shape

# Probes
8. startupProbe protects slow-start containers (failureThreshold * periodSeconds >= max startup time)
9. readinessProbe gates traffic; liveness restarts on failure; the two are not identical
10. Probe HTTP path aligned with business readiness semantics; don't call heavy deps from readiness

# Resources & security
11. requests + limits required; Guaranteed QoS (requests=limits) for production critical services
12. securityContext: runAsNonRoot, allowPrivilegeEscalation:false, readOnlyRootFilesystem
13. capabilities.drop: ["ALL"], add back minimum needed capabilities
14. emptyDir mount at /tmp handles temp files with readOnlyRootFilesystem

# Resilience & validation
15. HPA minReplicas/maxReplicas + scaleDown stabilizationWindow prevents flapping
16. PDB minAvailable coordinated with release strategy; ensures no downtime during node maintenance
17. Validation: kubectl apply --dry-run=server + kubeconform with pinned version in CI

All skills More skills