| name | kubernetes |
| description | This skill MUST be loaded when ANY Kubernetes manifest file (.yml/.yaml) under a k8s/ directory is being read, reviewed, edited, or created — including Deployments, Services, ConfigMaps, Secrets, PVCs, HPAs, Namespaces, StatefulSets, Ingresses, or any resource manifest. Also use when the user asks to "create a Kubernetes deployment", "write K8s manifests", "add health probes", "configure liveness and readiness probes", "create a PersistentVolumeClaim", "set up a ConfigMap or Secret", "add an HPA", "create a K8s namespace", "write a StatefulSet", "expose a service with NodePort or ClusterIP", "set resource limits and requests", "add init containers", "configure envFrom with ConfigMap and Secret", "create a horizontal pod autoscaler", "debug pod startup issues", "fix CrashLoopBackOff", "troubleshoot ImagePullBackOff", "set up persistent storage for databases", "configure Redis persistence in K8s", "deploy a PostgreSQL StatefulSet", "add rolling update strategy", or mentions Kubernetes, K8s, kubectl, pods, deployments, services, namespaces, probes, PVCs, HPA, ConfigMaps, Secrets, resource quotas, container orchestration, or cluster deployment patterns. |
Kubernetes Manifest Guide
Write production-grade Kubernetes manifests that are secure, observable, and
resilient by default. Every manifest should be deployable with a single
kubectl apply -f k8s/ and produce a working system.
Core Principles
- Declarative over imperative — define desired state in YAML; let the
controller loop converge. Never rely on manual
kubectl sequences for
production state.
- Namespace isolation — every project gets its own namespace. All resources
reference it explicitly so
kubectl apply is idempotent and scoped.
- Least privilege — containers run as non-root, Secrets hold credentials,
ConfigMaps hold non-sensitive config. Never embed secrets in manifests
committed to source control (use placeholders +
kubectl create secret).
- Resource budgeting — every container declares
requests (scheduling
guarantee) and limits (hard ceiling). This prevents noisy-neighbor
starvation and enables HPA.
- Health-first deployments — every long-running container has a liveness
probe (restart on hang) and a readiness probe (stop routing on dependency
failure). Probes must be lightweight and side-effect-free.
- Persistent state is explicit — databases and caches use PVCs with named
claims. Stateless services never mount persistent volumes.
Manifest Fundamentals
API Version & Kind
Always use the stable API version for each resource:
apiVersion: apps/v1
apiVersion: v1
apiVersion: autoscaling/v2
apiVersion: networking.k8s.io/v1
Metadata Convention
Every resource needs consistent metadata for filtering and management:
metadata:
name: <resource-name>
namespace: <project-namespace>
labels:
app: <service-name>
component: <role>
part-of: <project-name>
Label Selectors
Deployments and Services find pods via label selectors. The golden rule:
spec.selector.matchLabels must be a subset of spec.template.metadata.labels.
spec:
selector:
matchLabels:
app: my-api
template:
metadata:
labels:
app: my-api
component: backend
spec:
selector:
app: my-api
Namespace
Isolate project resources. Create the namespace first — everything else
references it.
apiVersion: v1
kind: Namespace
metadata:
name: my-project
labels:
app: my-project
Apply order: namespace → config → storage → workloads → scaling.
ConfigMap & Secret
ConfigMap — Non-sensitive configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: my-project
data:
LOG_LEVEL: "info"
DATABASE_HOST: "postgres"
CACHE_URL: "redis://redis:6379"
Secret — Sensitive credentials
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: my-project
type: Opaque
data:
DATABASE_PASSWORD: ""
API_KEY: ""
Mounting in pods — use envFrom to inject all keys as environment variables:
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
For composite values (like a DATABASE_URL that embeds a password), use env
with variable substitution:
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORD
- name: DATABASE_URL
value: "postgresql://user:$(DATABASE_PASSWORD)@postgres:5432/mydb"
K8s resolves $(VAR_NAME) from previously defined env vars in the same
container spec. Order matters — the referenced variable must appear first.
PersistentVolumeClaim
PVCs decouple storage from pod lifecycle. Data survives pod restarts, image
pulls, and rescheduling.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-pvc
namespace: my-project
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Access modes:
| Mode | Use Case |
|---|
ReadWriteOnce | Single pod writes (databases, caches) |
ReadOnlyMany | Multiple pods read (shared config, assets) |
ReadWriteMany | Multiple pods write (shared files — needs NFS/Ceph) |
Mount in a deployment:
spec:
containers:
- volumeMounts:
- name: db-storage
mountPath: /var/lib/postgresql/data
volumes:
- name: db-storage
persistentVolumeClaim:
claimName: db-pvc
Deployments
Resource Requests & Limits
Every container must declare resources. Without them, HPA cannot calculate
utilization and the scheduler cannot make informed placement decisions.
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
Sizing guidelines:
| Service Type | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---|
| API / backend | 100m–250m | 500m–1000m | 256Mi–512Mi | 512Mi–1Gi |
| Web frontend | 50m–100m | 200m–500m | 128Mi–256Mi | 256Mi–512Mi |
| Database | 100m–500m | 500m–2000m | 256Mi–1Gi | 512Mi–2Gi |
| Cache (Redis) | 50m–100m | 200m–500m | 128Mi–256Mi | 256Mi–512Mi |
These are starting points. For production, profile actual usage and set
requests to P50, limits to P99.
Health Probes
Probes serve fundamentally different purposes — choosing the wrong one causes
cascading failures.
Liveness probe — "Is this process alive?"
Restart the pod if it fails. Use for detecting deadlocks, infinite loops, or
zombie processes. Keep it cheap — no dependency checks.
livenessProbe:
httpGet:
path: /healthz
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
Readiness probe — "Can this instance serve traffic?"
Remove from Service endpoints if it fails. Use for dependency checks (database,
cache). The pod stays running — it just stops receiving traffic until recovery.
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 2
Startup probe — "Has the app finished starting?"
Disables liveness/readiness until the app is ready. Use for slow-starting
applications (JVM warmup, large model loading).
startupProbe:
httpGet:
path: /healthz
port: 8000
periodSeconds: 5
failureThreshold: 30
Critical mistakes to avoid:
- Never put dependency checks in the liveness probe — if the database goes
down, you don't want every API pod restarting (cascade failure)
- Always set
initialDelaySeconds — probing before the app starts causes
unnecessary restarts
- Keep probe endpoints fast (< 200ms) — slow probes cause false positives
Init Containers
Run setup tasks before the main container starts. Common uses: database
migrations, config generation, waiting for dependencies.
spec:
initContainers:
- name: run-migrations
image: my-app:latest
command: ["bash", "scripts/migrate.sh"]
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
containers:
- name: app
Init container rules:
- Runs to completion before main container starts
- If it fails, the pod restarts (respects
restartPolicy)
- Has access to the same volumes, ConfigMaps, and Secrets as main container
- Scripts must be idempotent — they run on every pod start
Rolling Update Strategy
Control how deployments roll out new versions:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
maxUnavailable: 0 ensures zero-downtime deploys — new pods must pass
readiness before old pods are terminated.
For single-instance databases, use strategy: Recreate to prevent dual-write.
Services
Services provide stable DNS names and load balancing for pods.
ClusterIP (Internal)
Default type. Accessible within the cluster via DNS:
<service-name>.<namespace>.svc.cluster.local (or just <service-name>
within the same namespace).
apiVersion: v1
kind: Service
metadata:
name: my-api
namespace: my-project
spec:
type: ClusterIP
selector:
app: my-api
ports:
- name: http
port: 8000
targetPort: 8000
protocol: TCP
NodePort (External Access for Dev)
Exposes on each node's IP at a static port (30000–32767). Use for local dev
when you need external access without an Ingress controller.
apiVersion: v1
kind: Service
metadata:
name: my-web
namespace: my-project
spec:
type: NodePort
selector:
app: my-web
ports:
- name: http
port: 3000
targetPort: 3000
nodePort: 30000
When to use which
| Type | Use Case | Access |
|---|
| ClusterIP | Internal services (DB, cache, API-to-API) | svc-name:port within cluster |
| NodePort | Dev/local external access | localhost:nodePort |
| LoadBalancer | Cloud production external access | Cloud-provisioned external IP |
| Ingress | HTTP routing, TLS, path/host-based | Domain-based routing |
HorizontalPodAutoscaler
Auto-scale workloads based on observed metrics. Requires resource requests
on target pods (HPA calculates utilization as current / request).
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-api-hpa
namespace: my-project
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-api
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Behavior tuning (prevent flapping):
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 2
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 60
Scale up aggressively to handle spikes; scale down conservatively to avoid
oscillation.
Best Practices
-
Always set a namespace explicitly — never depend on kubectl context.
Manifests should be self-contained and apply correctly regardless of the
user's current context.
-
Never commit real secrets — use empty placeholders in manifests and
create real secrets with kubectl create secret generic. Add secret files
to .gitignore if they ever contain real values.
-
Match probe type to failure mode — liveness for process health (no deps),
readiness for dependency health (DB, cache). Mixing them causes cascading
restarts.
-
Set resource requests AND limits on every container — requests enable
scheduling; limits prevent runaway consumption. Without requests, HPA
cannot function.
-
Use envFrom over individual env entries — cleaner manifests, easier
to add new config without touching deployments.
-
Make init containers idempotent — they run on every pod start, not just
the first time. Use IF NOT EXISTS in SQL, check-then-create patterns.
-
PVCs for stateful services only — databases and caches get PVCs. API
servers and web frontends should be stateless and disposable.
-
Label everything consistently — app, component, and part-of labels
enable kubectl get pods -l app=my-api and service discovery.
-
Order manifests for dependency — namespace first, then config/secrets,
then storage, then workloads, then scaling. Use file naming
(00-namespace.yml, 10-config.yml) or kustomize for ordering.
-
Test with kubectl apply --dry-run=client — catches syntax errors and
missing fields before hitting the cluster. Follow with kubectl diff to
preview changes against the live state.
When to Read Reference Files
| Need | File |
|---|
| Deployment, StatefulSet, init containers, probes, rolling updates, pod security | references/workload-resources.md |
| Services, Ingress, PVCs, ConfigMaps, Secrets, NetworkPolicy, storage classes | references/networking-and-storage.md |
| HPA, VPA, resource tuning, pod disruption budgets, chaos testing, monitoring | references/scaling-and-reliability.md |
Example Templates
| Need | File |
|---|
| Namespace + ConfigMap + Secret (project bootstrap) | examples/namespace-config.yml |
| Database Deployment + PVC + Service (PostgreSQL pattern) | examples/stateful-database.yml |
| Cache Deployment + PVC + Service (Redis with AOF) | examples/stateful-cache.yml |
| API Deployment with init container, probes, HPA + Service | examples/api-deployment.yml |
| Web Deployment + NodePort Service (frontend pattern) | examples/web-deployment.yml |