Kubernetes operations
The definitive collection of cross-platform Agent Skills. Compatible with Claude Code, Codex, Cursor, OpenClaw, Gemini CLI, Copilot, Hermes. Curated weekly. Higher quality than any alternative.
npx -y skills add JPeetz/agent-skills --skill kubernetes-operationsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Production-grade Kubernetes operations for AI agents: manifest generation, security hardening, Helm charts, GitOps workflows, and multi-cloud deployment patterns. Prevents K8s hallucinations with failure-mode diagnosis, compliance validation, and structured output contracts.
SKILL.md
29.5 KB, ~7.6k tokens by cl100k_base, as published. Nobody here has run it
Kubernetes Operations — Agent Skill
Production-grade Kubernetes operations for AI agents. Generates secure, scalable, multi-cloud K8s manifests and Helm charts with built-in failure-mode prevention, compliance validation, and structured output contracts.
Core Workflow — 7-Step Failure-Mode Prevention
Every K8s response MUST follow this sequence. Skip a step → risk a failure mode.
Context → Diagnose → Reference → Design → Validate → Output → Rollback
| Step | Action | Deliverable |
|---|---|---|
| 1. Context | Identify target platform (EKS/GKE/AKS/OpenShift/k3s), cluster version, namespace, existing workloads, constraints | Context summary |
| 2. Diagnose | Check for failure-mode exposures in the ask (see §Failure Modes) | Risk assessment |
| 3. Reference | Pull current API versions, platform-specific defaults, security standards (PSS, CIS, NSA/CISA) | Reference baseline |
| 4. Design | Build manifest/chart/policy with least-privilege, resource bounds, network isolation | Artifact draft |
| 5. Validate | Run mental validation against all 8 failure modes, check API deprecations, verify security context | Validation pass/fail |
| 6. Output | Emit structured output with assumptions, tradeoffs, and rollback instructions per §Output Contract | Final artifact + contract |
| 7. Rollback | Provide kubectl delete / helm uninstall / kubectl rollout undo instructions | Rollback plan |
Failure Modes — The 8 Ways K8s Goes Wrong
These are the failure modes this skill prevents. Every manifest review and generation MUST check all eight.
FM-1: Insecure Workloads
Symptom: Container running as root, privileged mode, hostPath mounts, no securityContext.
Prevention: runAsNonRoot: true, readOnlyRootFilesystem: true, drop ALL capabilities, add only required ones.
Detection: kubectl get pods -o json | jq '.items[].spec.containers[].securityContext'
FM-2: Resource Starvation
Symptom: No requests/limits, unbounded memory growth, CPU throttling, OOMKilled.
Prevention: Always set requests = limits for Guaranteed QoS on critical workloads. Use LimitRange at namespace level.
Detection: kubectl top pods --namespace=<ns> and check for OOMKilled in pod status.
FM-3: Network Exposure
Symptom: Missing NetworkPolicies, services exposed as LoadBalancer unnecessarily, no TLS termination.
Prevention: Deny-all ingress by default, explicit NetworkPolicy allowlists, TLS via cert-manager.
Detection: kubectl get netpol --all-namespaces and kubectl get svc --all-namespaces | grep LoadBalancer
FM-4: Privilege Sprawl
Symptom: ClusterRoleBindings to cluster-admin, overly broad RBAC, service accounts with secrets access.
Prevention: Least-privilege RBAC, per-namespace Roles, service account token audiences restricted.
Detection: kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin")'
FM-5: Fragile Rollouts
Symptom: No health probes, no PDB, rolling update with single replica, no revision history.
Prevention: Readiness + liveness probes, PodDisruptionBudget, revisionHistoryLimit: 10, minReadySeconds.
Detection: kubectl get deployment -o json | jq '.items[] | select(.spec.replicas==1 and .spec.strategy.type=="RollingUpdate")'
FM-6: API Drift
Symptom: Deprecated extensions/v1beta1, apps/v1beta2, policy/v1beta1 PDB.
Prevention: Always check kubectl api-resources for current API version. Use apps/v1 for Deployments, policy/v1 for PDB.
Detection: kubectl get --raw /apis | jq -r '.groups[].preferredVersion.groupVersion'
FM-7: GitOps Divergence — NEW
Symptom: Manual kubectl apply bypasses GitOps pipeline, cluster state drifts from Git, unrecorded changes.
Prevention: All changes through Git → Flux/ArgoCD reconciliation. kubectl for read-only and emergencies only.
Detection: flux get kustomizations -A or argocd app diff <app> to detect drift.
FM-8: Multi-Cloud Skew — NEW
Symptom: EKS-specific annotations on GKE, AKS ingress class mismatch, OpenShift SCC violations.
Prevention: Platform-conditional manifest generation. Check cloud provider before emitting manifests.
Detection: Validate against provider-specific kubectl api-resources and admission webhooks.
Platform Guidance
Amazon EKS
- Ingress: AWS Load Balancer Controller (
kubernetes.io/ingress.class: alb) - Storage: EBS CSI driver (
gp3default) - Auth: IAM Roles for Service Accounts (IRSA) —
eks.amazonaws.com/role-arnannotation - Networking: VPC CNI, security groups for pods
- Patching: Bottlerocket OS recommended
Google GKE
- Ingress: GKE Ingress Controller / Gateway API (
networking.gke.io/managed-certificates) - Storage: Compute Engine persistent disk CSI (
pd-standard,pd-ssd) - Auth: Workload Identity Federation (
iam.gke.io/gcp-service-accountannotation) - Networking: Dataplane V2, NetworkPolicy logging via Cloud Logging
- Patching: GKE Autopilot for managed nodes
Azure AKS
- Ingress: Application Gateway Ingress Controller (AGIC) or NGINX
- Storage: Azure Disk CSI / Azure Files CSI
- Auth: Microsoft Entra Workload ID (
azure.workload.identity/use: "true") - Networking: Azure CNI with NetworkPolicy via Calico
- Patching: AKS node image auto-upgrade
Red Hat OpenShift
- Ingress: OpenShift Router (HAProxy-based,
route.openshift.io) - Security: Security Context Constraints (SCC) —
restricted-v2as default, drop ALL caps mandatory - Storage: OpenShift Data Foundation / any CSI
- Auth: OAuth integrated, no IRSA equivalent — use
serviceAccountannotations - Patching: Cluster Version Operator (CVO) managed
Security
Pod Security Standards (PSS)
restricted (default recommended)
- runAsNonRoot: true
- seccompProfile: RuntimeDefault
- capabilities.drop: ["ALL"]
- readOnlyRootFilesystem: true (where feasible)
- allowPrivilegeEscalation: false
Namespace labeling:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
RBAC Least-Privilege Patterns
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: app-ns
name: app-deployer
rules:
- apiGroups: ["apps", ""]
resources: ["deployments", "pods", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]
# NO delete, NO secrets access, NO cluster-wide verbs
NetworkPolicy — Deny-All + Allowlist
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
OWASP Kubernetes Top 10 (Rapid Reference)
- Insecure workload configurations → FM-1
- Supply chain vulnerabilities → Image scanning (Trivy/Grype)
- Overly permissive RBAC → FM-4
- Lack of centralized policy enforcement → OPA/Gatekeeper, Kyverno
- Inadequate logging & monitoring → FM-8 Observability
- Broken authentication → Service account token hygiene
- Missing network segmentation → FM-3
- Secrets management failures → §Secret Management
- Misconfigured cluster components → CIS benchmarks
- Outdated/vulnerable K8s components → FM-6
NSA/CISA Kubernetes Hardening Guidance
- Scan container images for vulnerabilities
- Run containers as non-root
- Use network segmentation (NetworkPolicies)
- Encrypt secrets at rest (etcd encryption)
- Enable audit logging
- Use service mesh for mTLS (Istio/Linkerd)
- Regularly update cluster components
CIS Kubernetes Benchmarks
- 1.1: API server — AlwaysPullImages admission plugin
- 1.2: Scheduler — profiling disabled
- 4.1: Worker node — kubelet
--protect-kernel-defaults=true - 5.1: RBAC — minimize
cluster-adminbindings - 5.2: Pod Security Standards — restricted enforcement
- 5.3: Network Policies — namespaced default deny
Helm Chart Generation & Review
Chart Structure
mychart/
├── Chart.yaml # name, version, appVersion, dependencies
├── values.yaml # Default values with documentation
├── values/ # Multi-environment overrides (values-prod.yaml, values-staging.yaml)
├── templates/
│ ├── _helpers.tpl # Reusable template functions
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── pdb.yaml
│ ├── serviceaccount.yaml
│ ├── networkpolicy.yaml
│ └── NOTES.txt # Post-install instructions
├── templates/tests/ # Helm test pod definitions
├── crds/ # Custom Resource Definitions
└── README.md
values.yaml Patterns
# ALWAYS document every value
replicaCount: 3
image:
repository: nginx
tag: "1.25"
pullPolicy: IfNotPresent
# pullSecrets for private registries
pullSecrets: []
# Security context as a named block — reusable
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]
# Resource defaults — MUST be set
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
# Autoscaling
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# Probes with sensible defaults
probes:
liveness:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readiness:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
# Pod disruption budget
pdb:
enabled: true
minAvailable: 1
# Ingress with TLS
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: app-tls
hosts:
- app.example.com
# Network policy
networkPolicy:
enabled: true
ingressAllow:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
egressAllow:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
app: database
ports:
- port: 5432
protocol: TCP
# Service account with annotations for cloud IAM
serviceAccount:
create: true
annotations:
eks.amazonaws.com/role-arn: "arn:aws:iam::123456789012:role/app-role" # EKS IRSA
# iam.gke.io/gcp-service-account: "[email protected]" # GKE WI
# azure.workload.identity/use: "true" # AKS WI
# Tolerations & node affinity
tolerations: []
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values: ["myapp"]
topologyKey: kubernetes.io/hostname
# Topology spread constraints — zone-level HA
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: myapp
nodeSelector: {}
# platform-specific:
# EKS: nodeSelector: { "eks.amazonaws.com/capacityType": "ON_DEMAND" }
# GKE: nodeSelector: { "cloud.google.com/gke-nodepool": "default-pool" }
Dependency Management
# Chart.yaml
dependencies:
- name: redis
version: "18.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
- name: postgresql
version: "15.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
Multi-environment deployment:
# Staging
helm upgrade --install myapp ./mychart -f values.yaml -f values/staging.yaml -n staging
# Production
helm upgrade --install myapp ./mychart -f values.yaml -f values/prod.yaml -n production
GitOps Integration
Flux CD Pattern
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: myapp
namespace: flux-system
spec:
interval: 1m
url: https://github.com/org/myapp-deploy
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: myapp
namespace: flux-system
spec:
interval: 5m
path: ./overlays/production
prune: true
sourceRef:
kind: GitRepository
name: myapp
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: myapp
namespace: production
postBuild:
substitute:
ENV: production
ArgoCD Pattern
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/myapp-deploy
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 30s
factor: 2
maxDuration: 5m
Resource Management
Requests & Limits
| Workload Type | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---|---|---|---|---|
| Burstable API | 100m | 500m | 128Mi | 256Mi |
| Guaranteed DB | 500m | 500m | 2Gi | 2Gi |
| Background Job | 50m | 200m | 64Mi | 128Mi |
# Pod with Guaranteed QoS (requests == limits)
resources:
requests:
cpu: "500m"
memory: "2Gi"
limits:
cpu: "500m"
memory: "2Gi"
HPA (Horizontal Pod Autoscaler)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
VPA vs HPA Decision Matrix
- HPA: Stateless workloads, request-driven scaling, predictable patterns
- VPA: Stateful workloads, right-sizing after profiling, JVM heap tuning
- Both: Use VPA in recommendation mode (
updateMode: "Off") with HPA for execution
Priority Classes
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "Critical production workloads"
---
# System-critical (never evicted): 2000000000
# Production high: 1000000
# Production default: 100000
# Batch / non-critical: -1
Cost Optimization
Right-Sizing
- Use
kubectl top pods+ VPA recommender to profile actual usage - Set requests at P95 of observed usage, not guesstimates
- Run
kubecostoropencostfor namespace-level cost allocation
Spot Instances
# EKS
spec:
nodeSelector:
eks.amazonaws.com/capacityType: SPOT
tolerations:
- key: "eks.amazonaws.com/capacityType"
operator: "Equal"
value: "SPOT"
effect: "NoSchedule"
# GKE
spec:
nodeSelector:
cloud.google.com/gke-spot: "true"
tolerations:
- key: "cloud.google.com/gke-spot"
operator: "Equal"
value: "true"
effect: "NoSchedule"
# AKS
spec:
nodeSelector:
kubernetes.azure.com/scalesetpriority: spot
tolerations:
- key: "kubernetes.azure.com/scalesetpriority"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
Cluster Autoscaler
- Set min/max on node groups, not on individual deployments
- Use
cluster-autoscaler.kubernetes.io/safe-to-evict: "true"on pods that can move - Apply PDBs so autoscaler respects availability during scale-down
Workload Rightsizing Rules
- Never run without limits. Use LimitRange to enforce defaults
- Burstable for dev/staging. Guaranteed for production databases and stateful sets
- Preemptible nodes for batch. Tolerations + nodeSelectors
- HPA over static replicas. Let metrics drive scaling decisions
Observability
Prometheus Metrics
# Pod annotations for Prometheus scraping
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
# ServiceMonitor (Prometheus Operator)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: metrics
interval: 30s
path: /metrics
Structured Logging
# Sidecar or daemonset pattern — emit JSON to stdout
# Containers should log to stdout/stderr in JSON format:
# {"level":"info","ts":"2026-06-18T02:06:00Z","msg":"request","method":"GET","path":"/api","duration_ms":42,"status":200}
# Use Fluent Bit or Vector as DaemonSet for log collection:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
serviceAccountName: fluent-bit
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.1
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
OpenTelemetry
# Instrument with OTel SDK, configure via OTEL_EXPORTER_OTLP_ENDPOINT
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector.observability:4317"
- name: OTEL_SERVICE_NAME
value: "myapp"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "deployment.environment=production,cloud.provider=aws"
# OpenTelemetry Collector — sidecar pattern
# OR use the OpenTelemetry Operator for auto-instrumentation
Golden Signals Dashboard (Grafana / Datadog / New Relic)
- Latency: P50, P95, P99 of request duration
- Traffic: Requests per second
- Errors: 5xx rate + error budget burn rate
- Saturation: CPU throttle %, memory pressure, goroutine count
Alert Rules (Prometheus)
groups:
- name: app
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.01
for: 5m
labels:
severity: critical
annotations:
summary: "Error rate > 1% for {{ $labels.app }}"
- alert: PodRestarting
expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
for: 5m
labels:
severity: warning
Secret Management
External Secrets Operator (ESO)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secretsmanager # or gcp-secretmanager, azure-keyvault
kind: ClusterSecretStore
target:
name: myapp-secrets
creationPolicy: Owner
data:
- secretKey: DATABASE_URL
remoteRef:
key: prod/myapp/database-url
- secretKey: API_KEY
remoteRef:
key: prod/myapp/api-key
Sealed Secrets
# Encrypt a secret for GitOps
kubectl create secret generic mysecret --from-literal=password=s3cret --dry-run=client -o yaml \
| kubeseal --controller-namespace kube-system --format yaml > sealed-secret.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: mysecret
namespace: production
spec:
encryptedData:
password: AgBy8hCKF...encrypted_base64...
Secret Management Decision Tree
| Scenario | Solution |
|---|---|
| Cloud-native, existing secrets manager | External Secrets Operator |
| GitOps, no external dependency | Sealed Secrets |
| Dynamic secrets (DB creds, PKI) | Vault + Vault CSI Provider |
| Simple, single-cluster | Kubernetes Secrets + etcd encryption |
| Multi-cluster, audit trail | Vault with Kubernetes auth method |
Multi-Cloud Deployment Patterns
Conditional Manifests (Helm)
# templates/ingress.yaml
{{- if eq .Values.cloudProvider "aws" }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
# ...
{{- else if eq .Values.cloudProvider "gcp" }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: gce
networking.gke.io/managed-certificates: myapp-cert
# ...
{{- end }}
Node Selectors & Taints/Tolerations
# EKS — Spot node pool
nodeSelector:
eks.amazonaws.com/capacityType: SPOT
tolerations:
- key: "eks.amazonaws.com/capacityType"
operator: "Equal"
value: "SPOT"
effect: "NoSchedule"
# GKE — GPU node pool
nodeSelector:
cloud.google.com/gke-accelerator: nvidia-l4
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"
# AKS — Memory-optimized node pool
nodeSelector:
agentpool: memoryoptimized
Cloud-Agnostic Abstraction
# Use Kubernetes-native primitives, not cloud-specific ones
# Good: Ingress + cert-manager (works everywhere with proper controller)
# Bad: Cloud-specific LoadBalancer annotations hardcoded
# For cloud-specific features, use Helm template functions:
{{- define "app.cloudConfig" -}}
{{- if eq .Values.cloudProvider "aws" }}
serviceAccountAnnotations:
eks.amazonaws.com/role-arn: {{ .Values.aws.roleArn }}
{{- else if eq .Values.cloudProvider "gcp" }}
serviceAccountAnnotations:
iam.gke.io/gcp-service-account: {{ .Values.gcp.serviceAccount }}
{{- else if eq .Values.cloudProvider "azure" }}
podLabels:
azure.workload.identity/use: "true"
{{- end }}
{{- end }}
Output Contract
Every response that produces K8s artifacts MUST include:
Required Sections
- Assumptions: What you assumed about the environment
- Tradeoffs: What you chose and why (e.g., Guaranteed vs Burstable QoS)
- Rollback Instructions: Exact
kubectl delete/helm uninstall/kubectl rollout undocommands
### Assumptions
- Target cluster: EKS 1.30, namespace: `production`
- You have cert-manager and AWS Load Balancer Controller installed
- IRSA is configured for service account IAM roles
### Tradeoffs
- Chose Guaranteed QoS for the database pod (slight over-provision, but predictable performance)
- Used Burstable QoS for the API (cost-effective, acceptable for stateless workloads)
- Disabled privilege escalation even though it breaks some debugging tools — security over convenience
### Rollback
\`\`\`bash
kubectl delete -f manifest.yaml
# OR for Helm:
helm uninstall myapp -n production
# OR to undo last rollout:
kubectl rollout undo deployment/myapp -n production
\`\`\`
DO / DON'T
✅ DO
# DO: Complete security context
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
# DO: Both probes
livenessProbe:
httpGet:
path: /healthz
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
# DO: Resource requests AND limits
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
# DO: Namespace labels for PSS
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted
# DO: Use current API versions
apiVersion: apps/v1
apiVersion: networking.k8s.io/v1
apiVersion: policy/v1
# DO: Pod anti-affinity for HA
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: myapp
topologyKey: kubernetes.io/hostname
# DO: Topology spread for zone HA
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
# DO: Provide PDB
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
minAvailable: 1
❌ DON'T
# DON'T: No security context
spec:
containers:
- name: app
image: myapp:latest
# securityContext is MISSING — FM-1
# DON'T: Running as root
securityContext:
runAsUser: 0 # NO — FM-1
# DON'T: Privileged container
securityContext:
privileged: true # NO — FM-1, NEVER in production
# DON'T: hostPath mounts without extreme caution
volumes:
- name: dangerous
hostPath:
path: /var/run/docker.sock # NO — FM-1, container escape risk
# DON'T: No resource limits
resources: {} # NO — FM-2
# DON'T: No health probes — FM-5
# No livenessProbe or readinessProbe defined
# DON'T: Deprecated API versions — FM-6
apiVersion: extensions/v1beta1 # DEPRECATED since 1.16
# DON'T: Single replica without PDB — FM-5
spec:
replicas: 1
# No PodDisruptionBudget defined
# DON'T: Overly broad RBAC — FM-4
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"] # NO — just use cluster-admin if you need this
# DON'T: No NetworkPolicy — FM-3
# Default deny NetworkPolicy must exist in every namespace
# DON'T: Hardcoded cloud provider specifics — FM-8
annotations:
eks.amazonaws.com/role-arn: "..." # OK if target is known EKS, NOT OK as generic manifest
# DON'T: kubectl apply for GitOps-managed resources — FM-7
# Use Git → Flux/ArgoCD path instead
Quick Reference Cards
Minimal Production-Ready Deployment Checklist
-
securityContextwithrunAsNonRoot: true,allowPrivilegeEscalation: false, capabilities drop ALL -
readinessProbeANDlivenessProbedefined -
resources.requestsANDresources.limitsset for all containers - HPA with
minReplicas >= 2for stateless workloads - PodAntiAffinity or TopologySpreadConstraints for HA
- PodDisruptionBudget with
minAvailable: 1ormaxUnavailable: 1 - ServiceAccount with cloud IAM annotations (if cloud managed)
- NetworkPolicy — deny-all + explicit allowlist
- PSS namespace labels (
enforce: restricted) - No deprecated API versions (
kubectl api-resourcesverified) - Secrets externalized (ESO/SealedSecrets/Vault), NOT in plain ConfigMap
- Prometheus scrape annotations or ServiceMonitor
Troubleshooting Quick Commands
# Pod won't start
kubectl describe pod <pod> -n <ns>
kubectl logs <pod> -n <ns> --previous
# Resource issues
kubectl top pods -n <ns>
kubectl get events -n <ns> --sort-by='.lastTimestamp'
# RBAC issues
kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa> -n <ns>
# Network issues
kubectl run tmp --rm -it --image=nicolaka/netshoot -n <ns> -- /bin/bash
# Then: curl, nc, dig, tcpdump from inside cluster
# Drift detection (Flux)
flux get kustomizations -A --status-selector ready=false
# Drift detection (ArgoCD)
argocd app diff <app>
# API version check
kubectl api-resources --verbs=list -o wide
kubectl explain deployment --api-version=apps/v1
Evaluation
See evals/eval_cases.json for trigger match test cases and near-miss negatives.
References
references/k8s-security-hardening.md— Full security reference: PSS, RBAC, NetworkPolicies, OWASP Top 10references/k8s-failure-modes.md— All 8 failure modes with detection and remediationreferences/helm-patterns.md— Helm chart architecture, multi-env patterns, dependency management
Scripts
scripts/validate-k8s-manifest.sh— Validate K8s YAML against schemas (kubeval/kubeconform)scripts/security-scan-k8s.sh— Security scan for K8s manifests (kubesec, privileged checks)