Kubernetes Manifests skill for Codex
Deployments that survive rollouts and node drains. Codex writes Deployments where each field has a job: startup probes instead of long initial delays, liveness that never checks the database, memory limits equal to requests, a preStop pause so endpoints drain before SIGTERM, and a PDB that lets node drains proceed one pod at a time. Manifests are checked with kubeconform and a server-side dry run before anything is applied.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- kubernetes-manifests Installs to ~/.agents/skills/kubernetes-manifests/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install kubernetes-manifests The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project kubernetes-manifests Installs to .agents/skills/kubernetes-manifests/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/kubernetes-manifests.zipunzip -o kubernetes-manifests.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) kubernetes-manifests PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $kubernetes-manifests, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Writes production Kubernetes YAML - Deployments with startup, readiness and liveness probes, resource requests and limits, security contexts, rolling update settings, graceful shutdown, Services, PodDisruptionBudgets and topology spread - and validates it with kubeconform and server-side dry runs. Use when deploying a service to Kubernetes, reviewing manifests, adding probes or resources, fixing downtime during rollouts or node drains, or hardening a pod spec.
Use it when
- A service is being deployed to Kubernetes for the first time
- Rollouts or cluster upgrades cause errors or brief outages
- Probes, resources or security contexts are missing or guessed
- A pull request changes Deployment or Service YAML
Use something else when
- Pods are already crashing or stuck Pending and you need a diagnosis: use kubernetes-debugging
- You need per-environment variants of the same manifests: use kustomize-overlays
- The question is RBAC, network policies or admission control: use kubernetes-security
How to check the result
kubeconform passes, kubectl apply --dry-run=server is accepted, and a rollout restart under steady traffic produces no failed requests.
The mistake it prevents
Pointing the liveness probe at a check that includes the database, so one database hiccup restarts every replica at the same moment.
A manifest is production-ready when a rollout or node drain causes zero failed requests, the scheduler knows what the pod needs, a hung process gets restarted but a slow one does not, and the container cannot escalate privileges. Every field below exists for one of those reasons.
Inspect first
- Read
AGENTS.mdand find how manifests are delivered: raw YAML, Kustomize (kustomization.yaml), Helm (Chart.yaml), or Argo CD. Edit the source of truth, not rendered output. - Check the cluster version (
kubectl version) and any policy engine (Kyverno, Gatekeeper, Pod Security Admission labels on the namespace). Policies reject manifests at apply time. - Learn the app: port, health endpoints, startup time, memory at steady state, whether it handles SIGTERM, and whether it writes to disk.
- Reuse existing labels and naming. Changing
spec.selectoron an existing Deployment is rejected (immutable); it requires delete and recreate, which is an outage.
kubectl against a real cluster needs credentials and network. Offline, validate with
kubeconform schemas if present; otherwise ask the user to run the server-side checks.
Reference Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders
labels: &labels
app.kubernetes.io/name: orders
app.kubernetes.io/part-of: shop
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels:
app.kubernetes.io/name: orders
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
template:
metadata:
labels: *labels
spec:
serviceAccountName: orders
automountServiceAccountToken: false
terminationGracePeriodSeconds: 40
securityContext:
runAsNonRoot: true
seccompProfile: { type: RuntimeDefault }
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels: { app.kubernetes.io/name: orders }
containers:
- name: orders
image: registry.example.com/shop/orders:1.14.2
ports:
- name: http
containerPort: 8080
env:
- name: GOMEMLIMIT
valueFrom:
resourceFieldRef: { resource: limits.memory }
envFrom:
- configMapRef: { name: orders-config }
- secretRef: { name: orders-secrets }
resources:
requests: { cpu: 250m, memory: 384Mi }
limits: { memory: 384Mi }
startupProbe:
httpGet: { path: /healthz, port: http }
periodSeconds: 2
failureThreshold: 60 # up to 120 s to start
readinessProbe:
httpGet: { path: /ready, port: http }
periodSeconds: 5
failureThreshold: 2
livenessProbe:
httpGet: { path: /healthz, port: http }
periodSeconds: 10
failureThreshold: 3
lifecycle:
preStop:
sleep: { seconds: 10 } # 1.30+; older clusters: exec ["sleep","10"]
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
volumeMounts:
- { name: tmp, mountPath: /tmp }
volumes:
- name: tmp
emptyDir: { sizeLimit: 256Mi }
---
apiVersion: v1
kind: Service
metadata:
name: orders
spec:
selector: { app.kubernetes.io/name: orders }
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: orders
spec:
maxUnavailable: 1
unhealthyPodEvictionPolicy: AlwaysAllow
selector:
matchLabels: { app.kubernetes.io/name: orders }
Probes
| Probe | Answers | Endpoint should check | Never |
|---|---|---|---|
| startup | Has the process finished booting? | Same as liveness | Omit it and compensate with a huge initialDelaySeconds on liveness |
| readiness | Should this pod get traffic right now? | Can serve: warmed caches, required downstreams reachable | Fail it forever; a never-ready pod blocks the rollout |
| liveness | Is the process wedged beyond recovery? | Event loop / main thread responds | Check the database; a DB blip then restarts every pod at once |
Liveness and readiness are disabled until the startup probe succeeds. Use gRPC probes
(grpc: { port: 9090 }) for gRPC servers rather than shelling out to a health binary.
Resources
- Always set requests: they drive scheduling, HPA utilisation and eviction order.
- Memory: limit equals request. Memory is not compressible; overcommit means OOM kills under node pressure. Size from observed p99 working set plus 20-30 percent.
- CPU: set a request; prefer no CPU limit for latency-sensitive services because limits cause CFS throttling even on idle nodes. Set a CPU limit only when policy requires it or the workload is batch and noisy.
- Tell runtimes about their limits:
GOMEMLIMIT,-XX:MaxRAMPercentage=75for the JVM,--max-old-space-sizefor Node (in MB, about 75 percent of the limit). - Guaranteed QoS (requests equal limits for CPU and memory) only for workloads that need it.
Zero-downtime rollouts and drains
maxUnavailable: 0with a surge keeps capacity during rollout.- Readiness gates traffic to new pods.
- On termination, endpoints are removed asynchronously. The
preStopsleep keeps the old pod serving while kube-proxy, ingress controllers and load balancers catch up; then the app receives SIGTERM and must finish in-flight requests. terminationGracePeriodSecondsmust exceed preStop sleep plus the app’s drain time.- A PDB lets
kubectl drainand cluster upgrades evict pods one at a time. A PDB withminAvailableequal to replicas (ormaxUnavailable: 0) blocks drains forever; do not. - With fewer than 2 replicas a PDB cannot help. Production services run at least 2, spread across zones.
Hard rules
- Pin images by immutable tag or digest, never
:latest; withlatesta restart can pull different code than its siblings run. SetimagePullPolicyonly when you mean it. - Secrets come from
Secretobjects or an external secrets operator, never literal env values in the Deployment. Base64 in a Secret manifest is encoding, not encryption: do not commit Secret YAML with real values (see$secrets-management). - Name ports and reference them by name in probes and Services.
- One Deployment per independently scaled process. Sidecars for proxies and log shippers
only; use native sidecars (
initContainerswithrestartPolicy: Always, 1.29+ beta, GA in 1.33) when a sidecar must start before and stop after the app. - Use
apiVersionvalues that the target cluster still serves.kubectl api-resourceslists them;policy/v1beta1,extensions/v1beta1andautoscaling/v2beta2are gone. - Applying to a production cluster is a deploy: show the diff and get the user’s
confirmation before
kubectl applyagainst anything other than a dev cluster.
Workflow
- [ ] 1. Find the delivery mechanism and edit its source files
- [ ] 2. Gather port, health endpoints, startup time, memory profile
- [ ] 3. Write Deployment, Service, PDB (plus ServiceAccount, ConfigMap as needed)
- [ ] 4. Validate offline with kubeconform
- [ ] 5. kubectl apply --dry-run=server and kubectl diff against the target namespace
- [ ] 6. With user confirmation, apply and watch the rollout
- [ ] 7. Prove a rollout causes no errors (load test or synthetic traffic during restart)
Verification
kubeconform -strict -summary -kubernetes-version 1.33.0 k8s/*.yaml
kubectl apply --dry-run=server -f k8s/ -n shop # admission webhooks and policies run
kubectl diff -f k8s/ -n shop
kubectl rollout status deploy/orders -n shop --timeout=5m
kubectl get pdb orders -n shop # ALLOWED DISRUPTIONS should be >= 1
kubectl rollout restart deploy/orders -n shop # while sending traffic; expect 0 errors
kubeconform downloads schemas on first use (network); pass -schema-location for a
local mirror in air-gapped setups. CRDs need -ignore-missing-schemas or their schemas.
Output
**Objects:** Deployment, Service, PDB, ServiceAccount (namespace shop)
**Sizing:** 3 replicas, 250m / 384Mi request, 384Mi memory limit, no CPU limit
**Probes:** startup /healthz (120 s budget), readiness /ready, liveness /healthz
**Shutdown:** preStop sleep 10 s, grace 40 s; app drains on SIGTERM (verified in logs)
**Checks:** kubeconform clean, server dry-run accepted, rollout restart under load: 0 errors
**Open:** memory request based on staging; revisit after a week of production metrics What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Kubernetes Manifests"
short_description: "Deployments that survive rollouts and node drains"
default_prompt: "$kubernetes-manifests Write Kubernetes manifests for this API with proper probes and resources" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.