Production-grade platform engineering handbook — Kubernetes, Terraform, Flux CD, GitHub Actions, AWS, and more.
77
97%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Structured guidance for cluster baseline standards, RBAC, workload patterns, and operational debugging on Kubernetes.
/platform-skills:kubernetes baseline # generate namespace, RBAC, network policy, PDB, quota scaffold
/platform-skills:kubernetes rbac # diagnose 401/403; generate Role/RoleBinding; simulate access
/platform-skills:kubernetes workload # Deployment, HPA, probes, securityContext hardening
/platform-skills:kubernetes debug # pod crashloop, OOMKill, pending scheduling, image pull errorsWhen invoked with no arguments, ask before proceeding:
Q1 — Mode?
What do you need?
1. baseline — namespace, RBAC, network policy, PDB, resource quota scaffold
2. rbac — diagnose 401/403, generate Role/RoleBinding, simulate access
3. workload — Deployment, HPA, probes, securityContext, PDB hardening
4. debug — crashloop, OOMKill, pending, image pull, general troubleshoot
Enter 1–4 or mode name:Q2 — Context (after mode selected):
Which namespace? What team owns it? Any special requirements (privileged workloads, GPU, ingress)?Paste the 403 error message or describe which service account needs which access.Paste the Deployment manifest or describe what the workload does.Paste the pod describe output and recent logs, or describe the symptom.Triggers: baseline, scaffold, new namespace, set up namespace, platform baseline
Read references/kubernetes.md before responding.
Generate the minimum platform baseline for a new namespace. Ask for namespace name and team before generating.
apiVersion: v1
kind: Namespace
metadata:
name: app-team
labels:
team: app-team
environment: production
managed-by: platform
annotations:
contact: platform@org.comapiVersion: v1
kind: ResourceQuota
metadata:
name: app-team-quota
namespace: app-team
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20"
persistentvolumeclaims: "10"apiVersion: v1
kind: LimitRange
metadata:
name: container-defaults
namespace: app-team
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128MiapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: app-team
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Allow DNS egress — required for pod name resolution
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: app-team
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCPapiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: app-pdb
namespace: app-team
spec:
minAvailable: 1 # keep at least 1 pod running during voluntary disruptions
selector:
matchLabels:
app: app-teamkubectl apply --dry-run=server -f namespace-baseline/
# Confirm quota is applied
kubectl describe resourcequota app-team-quota -n app-team
# Confirm network policy is in place
kubectl get networkpolicy -n app-team
# Test DNS still works from a pod
kubectl run dns-test --image=busybox --restart=Never --rm -it -n app-team \
-- nslookup kubernetes.defaultHandoffs:
/platform-skills:kyverno or /platform-skills:opa/platform-skills:secretsTriggers: 401, 403, forbidden, unauthorized, role, binding, service account, RBAC, can-i
Read references/kubernetes.md → RBAC troubleshooting section before responding.
| Code | Layer | First check |
|---|---|---|
401 Unauthorized | Authentication failed | kubectl auth whoami — confirm identity |
403 Forbidden | Authorized identity, missing permission | kubectl auth can-i — confirm what is allowed |
# Test exactly what the service account can do
kubectl auth can-i <verb> <resource> \
--as=system:serviceaccount:<namespace>:<sa-name> \
-n <namespace>
# Examples
kubectl auth can-i get pods \
--as=system:serviceaccount:app-team:app-controller \
-n app-team
kubectl auth can-i list secrets \
--as=system:serviceaccount:app-team:app-controller \
-n app-teamkubectl get rolebindings,clusterrolebindings -A -o json \
| jq -r '
.items[]
| select(
.subjects[]?
| select(.kind=="ServiceAccount"
and .name=="<sa-name>"
and .namespace=="<namespace>")
)
| "\(.kind)/\(.metadata.namespace)/\(.metadata.name) -> \(.roleRef.name)"'apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: app-team
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader-binding
namespace: app-team
subjects:
- kind: ServiceAccount
name: app-controller
namespace: app-team
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io| Situation | Use |
|---|---|
| Workload needs access within one namespace | RoleBinding → Role |
| Reuse a cluster role, scoped to one namespace | RoleBinding → ClusterRole |
| Controller needs cross-namespace or node access | ClusterRoleBinding → ClusterRole |
Prefer the narrowest scope. Audit ClusterRoleBinding usage regularly:
kubectl get clusterrolebindings -o json \
| jq -r '.items[] | select(.subjects[]?.kind=="ServiceAccount") | "\(.metadata.name) -> \(.roleRef.name)"'kubectl auth can-i get pods \
--as=system:serviceaccount:app-team:app-controller \
-n app-team
# expected: yes
kubectl auth can-i delete pods \
--as=system:serviceaccount:app-team:app-controller \
-n app-team
# expected: noRollback: Delete the RoleBinding. Roles and bindings are additive — removing them revokes the access without affecting running workloads beyond that access.
Triggers: deployment, statefulset, HPA, probes, securityContext, harden, resources, requests, limits
Read references/kubernetes.md before responding.
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: app-team
labels:
app: app
version: "1.0.0"
spec:
replicas: 2
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
serviceAccountName: app-sa # dedicated SA, not default
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: ghcr.io/org/app:1.0.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi # no CPU limit — throttles unnecessarily
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 2
startupProbe: # protects slow-starting apps from liveness kills
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 10apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
namespace: app-team
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70For event-driven or custom metric scaling → /platform-skills:keda
| Gap | Fix |
|---|---|
No resources.requests | Pod is unschedulable under resource pressure; add requests |
resources.limits.cpu set | Causes throttling even when cores are idle; remove CPU limit unless hard isolation is needed |
runAsRoot: true or no runAsNonRoot | Run as non-root; most app images support UID 1000+ |
No readinessProbe | Pod receives traffic before it is ready; add readiness probe |
No startupProbe for slow apps | Liveness kills pods during slow start; add startup probe |
default service account | Creates implicit access to cluster APIs; create a dedicated SA |
Triggers: crashloop, OOMKilled, pending, ImagePullBackOff, ErrImagePull, error, not starting, debug
Apply the structured checklist:
# Pod state and recent events
kubectl get pod <pod-name> -n <namespace>
kubectl describe pod <pod-name> -n <namespace>
# Container logs — current and previous (if restarted)
kubectl logs <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace> --previous
# Recent events in the namespace
kubectl get events -n <namespace> --sort-by='.lastTimestamp' | tail -20CrashLoopBackOff
# Application is starting and exiting — check exit code and logs
kubectl describe pod <pod-name> -n <namespace> | grep -A5 "Last State"
kubectl logs <pod-name> -n <namespace> --previousCommon causes: application error on startup, missing env var or secret, wrong entrypoint, liveness probe killing a slow-starting pod (add startupProbe).
OOMKilled
# Check memory usage vs limit
kubectl top pod <pod-name> -n <namespace> --containers
kubectl get pod <pod-name> -n <namespace> \
-o jsonpath='{.spec.containers[*].resources}'
# Check OOM events
kubectl describe pod <pod-name> -n <namespace> | grep -i oomFix: increase resources.limits.memory or profile the application for a memory leak. Never remove the limit — set it correctly.
Pending
# Events show why scheduling failed
kubectl describe pod <pod-name> -n <namespace> | grep -A10 "Events:"| Pending reason | Cause | Fix |
|---|---|---|
Insufficient cpu/memory | No node has enough capacity | Add nodes, reduce requests, or check Karpenter/Cluster Autoscaler |
didn't match node selector/affinity | Node labels don't match | Fix the nodeSelector or affinity block |
Unschedulable: taint | Node has a taint the pod doesn't tolerate | Add the matching toleration to the pod spec |
PVC not bound | PersistentVolumeClaim is pending | kubectl describe pvc <name> -n <ns> — check StorageClass and provisioner |
ImagePullBackOff / ErrImagePull
kubectl describe pod <pod-name> -n <namespace> | grep -A5 "Failed"Common causes: image tag does not exist, registry is private and imagePullSecrets is missing, incorrect registry hostname.
# Verify the image exists
docker manifest inspect ghcr.io/org/app:1.0.0
# Check imagePullSecrets are mounted
kubectl get pod <pod-name> -n <namespace> \
-o jsonpath='{.spec.imagePullSecrets}'After applying a fix, confirm the pod reaches Running and passes readiness:
kubectl rollout status deployment/<name> -n <namespace>
kubectl get pod -n <namespace> -l app=<label> -w# Roll back the Deployment to the previous revision
kubectl rollout undo deployment/<name> -n <namespace>
# Check rollout history
kubectl rollout history deployment/<name> -n <namespace>resources.limits.cpu — CPU limits throttle containers even when other cores are idle. Set requests but omit the CPU limit unless hard isolation is specifically required.default service account — it may have accumulated bindings over time and creates implicit API access. Always create a dedicated service account per workload.startupProbe for slow-starting apps — liveness probes kill pods during startup if initialDelaySeconds is too short. Use a startup probe to gate liveness until the app is running.ClusterRoleBinding when RoleBinding was sufficient — grants access cluster-wide. Audit all ClusterRoleBindings with service account subjects.kubectl apply -f in production without --dry-run=server first — server-side dry-run catches admission errors, invalid API versions, and webhook rejections before they apply.Full guidance: references/kubernetes.md
For policy enforcement: /platform-skills:kyverno or /platform-skills:opa
For event-driven autoscaling: /platform-skills:keda
For node provisioning: /platform-skills:karpenter
Examples:
examples/kubernetes/deployment-baseline.yamlexamples/kubernetes/namespace-baseline.yamlexamples/kubernetes/network-policy-default-deny.yamlexamples/kubernetes/pod-disruption-budget.yaml.claude-plugin
.github
assets
commands
docs
examples
agent-self-improve
argocd
awesome-docs
aws
cloudfront
functions
lambda-edge
functions
azure
compliance
conventional-commits
datadog
llm-observability
demo
documentation
dora
dynatrace
fluxcd
github-actions
composite-actions
configure-cloud
db-migrate
docker-build-push
k8s-deploy
notify-slack
pr-comment
release-tag
security-scan
setup-env
setup-terraform
terraform-plan
helm
web-service
templates
karpenter
kubernetes
kyverno
mcp
observability
openshift
pr-review
ownership
runtime-security
setup-agents
terraform
references
skills
platform-skills
tests
website