Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add jon23d/skillz --skill "kubernetes-manifests"
Install specific skill from multi-skill repository
# Description
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.
# SKILL.md
name: kubernetes-manifests
description: 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.
Philosophy
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.
When to use Kubernetes
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:
- The monorepo has 3 or more independently deployable services
- Services have different scaling characteristics (some need more replicas, some do not)
- The team needs independent deployment cycles per service
- There is a requirement for zero-downtime deployments and rollback at the service level
- The project already has Kubernetes configuration in any form
Kubernetes is likely premature when:
- There is only one service, or all services scale uniformly
- A docker-compose.yml is sufficient for production (small teams, low traffic, single host)
- The team has no Kubernetes operational experience
When in doubt, say so. Present the assessment and ask the user to confirm before writing any manifests.
Repository layout
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.
Namespace
Always deploy into a named namespace — never default.
apiVersion: v1
kind: Namespace
metadata:
name: myapp
labels:
app.kubernetes.io/managed-by: kubectl
Deployment
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
Required fields and rules
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.
Service
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.
ConfigMap (non-secret configuration)
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).
Secrets
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.
HorizontalPodAutoscaler
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.
Labels
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.
Rolling update strategy
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.
Provider-agnostic conventions
Keep manifests free of cloud-specific annotations unless the user has confirmed a target platform:
- No
kubernetes.io/ingress.class: alb(AWS-specific) - No
cloud.google.com/*annotations unless on GKE - No Azure-specific labels
Prefer nginx as the assumed ingress class when an ingress is needed — it runs identically on every cluster and locally via minikube or kind.
Checklist
Before finalising any set of manifests:
- [ ] Kubernetes assessed as appropriate for this project (not blindly generated)
- [ ] All resources are in a named namespace, not
default - [ ] Every container has
resources.requestsandresources.limitsset - [ ] Every Deployment has both
livenessProbeandreadinessProbe - [ ] Image tags are pinned versions, never
:latest - [ ] Pods run as non-root (
runAsNonRoot: true) - [ ] No secret values are committed —
Secretobjects are structural only - [ ] Rolling update strategy has
maxUnavailable: 0 - [ ] No cloud-vendor-specific annotations (unless user confirmed target platform)
- [ ] Standard
app.kubernetes.io/*labels on all objects - [ ] A
k8s/README.mddescribes how to apply the manifests and how secrets are populated
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.