Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
# Writable tmp directory when readOnlyRootFilesystem: true
volumeMounts:
-
name:
tmp
mountPath:
/tmp
volumes:
-
name:
tmp
emptyDir:
Probes — Liveness, Readiness, Startup
Understanding when to use each probe is critical:
Probe
Failure Action
Use For
startupProbe
Kills container if slow to start
Slow-starting apps (JVM, Python)
livenessProbe
Restarts container
Deadlock / hung process detection
readinessProbe
Removes from Service endpoints
Temporary unavailability (DB reconnect)
# Correct pattern: startupProbe covers slow startup,# then liveness/readiness take overstartupProbe:httpGet:path:/healthport:8080failureThreshold:30# 30 * 5s = 150s max startup timeperiodSeconds:5livenessProbe:httpGet:path:/healthport:8080periodSeconds:30failureThreshold:3# 3 * 30s = 90s before restartreadinessProbe:httpGet:path:/ready# Separate endpoint: checks DB, cache, etc.port:8080periodSeconds:10failureThreshold:2
# WRONG: initialDelaySeconds without startupProbe# If the app takes 60s to start, set a startupProbe insteadlivenessProbe:httpGet:path:/healthport:8080initialDelaySeconds:60# BAD: Arbitrary wait, race condition
apiVersion:v1kind:ConfigMapmetadata:name:my-app-confignamespace:my-namespacedata:LOG_LEVEL:"info"APP_ENV:"production"MAX_CONNECTIONS:"100"# Mount as a file for complex configapp.yaml:|
server:
port: 8080
timeout: 30s
# Mount ConfigMap as a filevolumes:-name:configconfigMap:name:my-app-configitems:-key:app.yamlpath:app.yamlvolumeMounts:-name:configmountPath:/etc/appreadOnly:true
Secrets — Sensitive data
# Create secret from literal (CLI, then store in Vault/SOPS)
kubectl create secret generic my-app-secrets \
--from-literal=db-password='s3cr3t' \
--namespace=my-namespace \
--dry-run=client -o yaml | kubectl apply -f -
apiVersion:v1kind:Secretmetadata:name:my-app-secretsnamespace:my-namespacetype:Opaque# Values are base64-encoded (NOT encrypted — use Sealed Secrets or ESO for real encryption)data:db-password:czNjcjN0# base64 of 's3cr3t'
Important: Raw Kubernetes Secrets are only base64-encoded, not encrypted at rest unless your cluster has encryption configured. Use Sealed Secrets or External Secrets Operator for production.
Resource Requests and Limits
resources:requests:# Scheduler uses this to place the podcpu:"100m"# 100 millicores = 0.1 CPUmemory:"128Mi"limits:# Container is killed/throttled above thiscpu:"500m"memory:"256Mi"
Rules of thumb:
Workload Type
CPU Request
Memory Request
Notes
Web API
100–250m
128–256Mi
Set limits 2-4x requests
Worker/consumer
250–500m
256–512Mi
Memory limit = request for predictability
JVM app
500m–1
512Mi–2Gi
Allow headroom above -Xmx for JVM overhead
Sidecar
10–50m
32–64Mi
Keep minimal
# WRONG: No requests or limits — unpredictable scheduling, OOM evictionscontainers:-name:appimage:myapp:latest# Missing resources: {} — this is dangerous in production# WRONG: Limits without requests — requests default to limits, over-reserves capacityresources:limits:cpu:"2"memory:"1Gi"# requests missing — will default to limits values
RBAC — Roles and ServiceAccounts
Principle of Least Privilege
Two patterns depending on whether the app calls the Kubernetes API:
Pattern A — App does NOT need the Kubernetes API (most apps)
Disable token automounting on the ServiceAccount. The Role/RoleBinding are not needed.
# ServiceAccount with token disabled — safest defaultapiVersion:v1kind:ServiceAccountmetadata:name:my-app-sanamespace:my-namespaceautomountServiceAccountToken:false# No K8s API token injected into pods
# Reference in Deployment — no token, no API accessspec:template:spec:serviceAccountName:my-app-saautomountServiceAccountToken:false# Belt-and-suspenders: also set at pod level
Pattern B — App DOES need the Kubernetes API (operators, controllers, config watchers)
Enable the token and grant only the permissions actually required.
# 1. ServiceAccount — enable token for this SAapiVersion:v1kind:ServiceAccountmetadata:name:my-app-sanamespace:my-namespaceautomountServiceAccountToken:true# Token required: app calls K8s API
# 2. Role — grant only what the app needs (namespace-scoped)apiVersion:rbac.authorization.k8s.io/v1kind:Rolemetadata:name:my-app-rolenamespace:my-namespacerules:-apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"] # Read-only, specific resource-apiGroups: [""]
resources: ["secrets"]
resourceNames: ["my-app-secrets"] # Restrict to specific secret by nameverbs: ["get"]
# 3. Bind Role to ServiceAccountapiVersion:rbac.authorization.k8s.io/v1kind:RoleBindingmetadata:name:my-app-rolebindingnamespace:my-namespacesubjects:-kind:ServiceAccountname:my-app-sanamespace:my-namespaceroleRef:kind:RoleapiGroup:rbac.authorization.k8s.ioname:my-app-role
# 4. Reference SA in Deploymentspec:template:spec:serviceAccountName:my-app-sa# automountServiceAccountToken defaults to true from SA — token is injected
Horizontal Pod Autoscaler (HPA)
apiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:my-app-hpanamespace:my-namespacespec:scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:my-appminReplicas:2# Always at least 2 for HAmaxReplicas:10metrics:-type:Resourceresource:name:cputarget:type:UtilizationaverageUtilization:70# Scale up when avg CPU > 70%-type:Resourceresource:name:memorytarget:type:UtilizationaverageUtilization:80
HPA requires resources.requests to be set on all containers — it calculates utilization as current / request.
PodDisruptionBudget (PDB)
Prevent too many pods going down during node drains or rolling updates:
apiVersion:policy/v1kind:PodDisruptionBudgetmetadata:name:my-app-pdbnamespace:my-namespacespec:minAvailable:2# OR use maxUnavailable: 1selector:matchLabels:app:my-app
# One-off Job (DB migration, data processing)apiVersion:batch/v1kind:Jobmetadata:name:db-migratenamespace:my-namespacespec:backoffLimit:3# Retry up to 3 times on failurettlSecondsAfterFinished:3600# Auto-delete after 1htemplate:spec:restartPolicy:OnFailure# Never for Jobs (not Always)containers:-name:migrateimage:ghcr.io/org/my-app:1.0.0command: ["python", "manage.py", "migrate"]
resources:requests:cpu:"100m"memory:"256Mi"
# CronJobapiVersion:batch/v1kind:CronJobmetadata:name:cleanup-jobnamespace:my-namespacespec:schedule:"0 2 * * *"# 2am dailyconcurrencyPolicy:Forbid# Don't run if previous still runningsuccessfulJobsHistoryLimit:3failedJobsHistoryLimit:1jobTemplate:spec:template:spec:restartPolicy:OnFailurecontainers:-name:cleanupimage:ghcr.io/org/cleanup:1.0.0resources:requests:cpu:"50m"memory:"64Mi"
kubectl Debugging Cheatsheet
# --- Pod status and logs ---
kubectl get pods -n my-namespace
kubectl get pods -n my-namespace -o wide # Show node assignment
kubectl describe pod <pod-name> -n my-namespace # Events and state details
kubectl logs <pod-name> -n my-namespace # Current logs
kubectl logs <pod-name> -n my-namespace --previous # Logs from crashed container
kubectl logs <pod-name> -n my-namespace -c <container> # Multi-container pod# --- Execute into a running container ---
kubectl exec -it <pod-name> -n my-namespace -- sh
kubectl exec -it <pod-name> -n my-namespace -- bash
# --- Check resource usage ---
kubectl top pods -n my-namespace
kubectl top nodes
# --- Deployment operations ---
kubectl rollout status deployment/my-app -n my-namespace
kubectl rollout history deployment/my-app -n my-namespace
kubectl rollout undo deployment/my-app -n my-namespace # Rollback
kubectl rollout undo deployment/my-app --to-revision=2 -n my-namespace
# --- Scale manually ---
kubectl scale deployment my-app --replicas=5 -n my-namespace
# --- Inspect events (cluster-wide issues) ---
kubectl get events -n my-namespace --sort-by='.lastTimestamp'# --- Port-forward for local debugging ---
kubectl port-forward pod/<pod-name> 8080:8080 -n my-namespace
kubectl port-forward svc/my-app 8080:80 -n my-namespace
# --- Dry-run to validate YAML ---
kubectl apply -f deployment.yaml --dry-run=client
kubectl apply -f deployment.yaml --dry-run=server # Validates against live cluster
# BAD: Using :latest tag — non-deterministic deploymentsimage:myapp:latest# GOOD: Pin to a specific immutable tag (SHA or semver)image:ghcr.io/org/myapp:1.4.2# orimage:ghcr.io/org/myapp@sha256:abc123...# ---# BAD: Running as rootsecurityContext: {} # Defaults to root# GOOD: Non-root with explicit UIDsecurityContext:runAsNonRoot:truerunAsUser:1001# ---# BAD: No resource limits — one pod can starve the entire nodecontainers:-name:appimage:myapp:1.0.0# No resources defined# GOOD: Always set requests and limitsresources:requests:cpu:"100m"memory:"128Mi"limits:cpu:"500m"memory:"256Mi"# ---# BAD: Storing plaintext secrets in ConfigMapsapiVersion:v1kind:ConfigMapdata:DB_PASSWORD:"mysecretpassword"# NEVER — use Secret or external secrets manager# ---# BAD: ClusterAdmin for application service accountsapiVersion:rbac.authorization.k8s.io/v1kind:ClusterRoleBindingroleRef:kind:ClusterRolename:cluster-admin# Grants god-mode to your app# ---# BAD: minAvailable: 0 in PDB — defeats the purposespec:minAvailable:0# ---# BAD: restartPolicy: Always in a Job (causes infinite restart loop)spec:restartPolicy:Always# Use OnFailure or Never for Jobs
Best Practices Checklist
Security
Container runs as non-root (runAsNonRoot: true, runAsUser set)
readOnlyRootFilesystem: true with emptyDir for writable paths
allowPrivilegeEscalation: false
All capabilities dropped (capabilities.drop: [ALL])
Dedicated ServiceAccount per app, not default
automountServiceAccountToken: false unless needed
RBAC follows least privilege (use Role, not ClusterRole unless needed)
Secrets managed via Sealed Secrets or External Secrets Operator
Reliability
All 3 probe types configured (startup + liveness + readiness)
Resource requests AND limits set on every container
minReplicas: 2+ for any production workload
PodDisruptionBudget defined for stateful or critical services
RollingUpdate strategy with maxUnavailable: 0
HPA configured for variable-load services
Observability
App exposes /health (liveness) and /ready (readiness) endpoints