open-weight/skills/kubernetes-manifests/SKILL.md
Kubernetes manifest authoring guidelines. Load this skill when creating or reviewing Kubernetes manifests for any service. Covers when to use Kubernetes, manifest structure, resource limits, health probes, and provider-agnostic conventions.
npx skillsauth add jon23d/skillz kubernetes-manifestsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Kubernetes manifests describe desired state, not imperative instructions. Write them to be readable by a human who has never seen the cluster — every field should have a clear reason to exist. Keep manifests provider-agnostic: no cloud-vendor-specific annotations unless the user explicitly targets a specific cloud.
The foundation is always Docker. Kubernetes orchestrates containers, but the container image is the unit of deployment. If the Dockerfile is wrong, no amount of manifest tuning will fix it. Manifests come after images.
Before producing any manifests, assess whether Kubernetes is the right tool. Do not generate manifests as a default — recommend and confirm with the user first.
Kubernetes is likely appropriate when:
Kubernetes is likely premature when:
docker-compose.yml is sufficient for production (small teams, low traffic, single host)When in doubt, say so. Present the assessment and ask the user to confirm before writing any manifests.
Manifests live in a k8s/ directory at the monorepo root, organised by service:
k8s/
base/
namespace.yaml
services/
api/
deployment.yaml
service.yaml
configmap.yaml
hpa.yaml
worker/
deployment.yaml
service.yaml
Keep a base/ directory for cluster-wide resources (namespace, RBAC, shared network policies). Each service gets its own subdirectory.
Always deploy into a named namespace — never default.
apiVersion: v1
kind: Namespace
metadata:
name: myapp
labels:
app.kubernetes.io/managed-by: kubectl
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: myapp
labels:
app.kubernetes.io/name: api
app.kubernetes.io/component: backend
app.kubernetes.io/part-of: myapp
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # zero-downtime: never remove a pod before a new one is ready
template:
metadata:
labels:
app.kubernetes.io/name: api
app.kubernetes.io/component: backend
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
containers:
- name: api
image: myapp/api:1.0.0 # never use :latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: http
envFrom:
- configMapRef:
name: api-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: api-secrets
key: database-url
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 15
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
terminationGracePeriodSeconds: 30
Resource limits — required on every container, no exceptions. A container without limits can starve its neighbours.
resources:
requests:
cpu: 100m # guaranteed allocation
memory: 128Mi
limits:
cpu: 500m # hard ceiling
memory: 512Mi
Start with conservative limits based on the service's actual usage, not guesses. Document the basis in a comment if it is non-obvious.
Liveness probe — answers "is the process alive?". A failing liveness probe restarts the container.
Readiness probe — answers "is the process ready for traffic?". A failing readiness probe removes the pod from the Service endpoints without restarting it. Both are required. They may hit the same endpoint, but they should ideally be distinct: /health for liveness, /ready for readiness.
Image tag — never use :latest. Use the explicit application version or git SHA.
runAsNonRoot: true — required on all pods. Matches the non-root user set in the Dockerfile.
readOnlyRootFilesystem: true — preferred. If the application writes to the filesystem, mount an explicit emptyDir volume for those paths rather than disabling this.
apiVersion: v1
kind: Service
metadata:
name: api
namespace: myapp
labels:
app.kubernetes.io/name: api
spec:
selector:
app.kubernetes.io/name: api
ports:
- name: http
port: 80
targetPort: http
type: ClusterIP # default; use LoadBalancer only for external entry points
Use ClusterIP for internal service-to-service communication. Use LoadBalancer only for services that receive external traffic and only when no ingress controller is in use. Prefer Ingress for HTTP/HTTPS traffic.
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
namespace: myapp
data:
NODE_ENV: "production"
PORT: "3000"
LOG_LEVEL: "info"
Only non-sensitive values go in ConfigMaps. Secrets go in kind: Secret (or an external secrets operator).
Never commit secret values to the repository. Reference the Secret object in the manifest, but do not define its data values in version-controlled files.
# k8s/services/api/secret.yaml — committed (structure only, no values)
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
namespace: myapp
type: Opaque
# data values are applied out-of-band (CI/CD pipeline, external secrets operator)
Document in a k8s/README.md how secrets are populated in each environment.
Add an HPA for any service that could benefit from elastic scaling. Do not add one speculatively — only when there is a scaling requirement.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
namespace: myapp
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Set minReplicas to at least 2 for any service that must be highly available.
Use the standard Kubernetes recommended labels on all objects:
labels:
app.kubernetes.io/name: api # name of the application component
app.kubernetes.io/component: backend # role (backend, frontend, worker, cache)
app.kubernetes.io/part-of: myapp # the parent application
app.kubernetes.io/version: "1.0.0" # current version (on pods/deployments)
app.kubernetes.io/managed-by: kubectl # or helm, argocd, etc.
Consistent labels enable kubectl get all -l app.kubernetes.io/part-of=myapp to surface everything related to the application.
The default strategy is RollingUpdate with maxUnavailable: 0. This guarantees that traffic is always served during deploys. Only deviate from this with explicit justification.
Recreate (stop all pods, then start new ones) is appropriate only for jobs or stateful services that cannot run two versions simultaneously, and only when downtime is acceptable.
Keep manifests free of cloud-specific annotations unless the user has confirmed a target platform:
kubernetes.io/ingress.class: alb (AWS-specific)cloud.google.com/* annotations unless on GKEPrefer nginx as the assumed ingress class when an ingress is needed — it runs identically on every cluster and locally via minikube or kind.
Before finalising any set of manifests:
defaultresources.requests and resources.limits setlivenessProbe and readinessProbe:latestrunAsNonRoot: true)Secret objects are structural onlymaxUnavailable: 0app.kubernetes.io/* labels on all objectsk8s/README.md describes how to apply the manifests and how secrets are populateddevelopment
Use when adding or modifying environment variable handling in TypeScript projects or monorepos — especially when using process.env directly, missing startup validation, sharing env schemas across packages, or encountering "undefined is not a string" errors at runtime from missing env vars.
testing
Use when creating a new skill, editing an existing skill, writing a SKILL.md, or verifying a skill works before deployment.
development
React UI design principles and conventions. Load when building or modifying any user interface or React components. Covers application type detection, visual standards, component design and structure, Mantine (business apps) and Tailwind (consumer apps), accessibility, responsiveness, state management, data fetching, testing, and in-app help patterns.
development
Use when setting up ESLint and/or Prettier in a TypeScript project, adding linting to an existing TypeScript codebase, or configuring typescript-eslint, eslint-config-prettier, or related packages.