| name | kubernetes-native |
| description | Kubernetes native resource patterns, Gateway API v1 implementations, native sidecar containers, workload best practices, resource optimization, security hardening, and multi-environment deployment strategies.
Activate when user mentions: Kubernetes manifests, Deployment, StatefulSet, Service, ConfigMap, Secret, Gateway API, HTTPRoute, native sidecars, init containers, Pod lifecycle, resource requests/limits, HPA (Horizontal Pod Autoscaler), VPA (Vertical Pod Autoscaler), PDB (Pod Disruption Budget), RBAC, ServiceAccount, NetworkPolicy, PodSecurityPolicy, SecurityContext, kubeconform validation, Kustomize overlays, spot instance tolerations, node affinity, taints and tolerations.
Use for: Native Kubernetes resource generation, Gateway API v1 migration, lifecycle management, security contexts, resource optimization, cost-effective scheduling, multi-environment configurations.
Do NOT use for: Helm charts (use helm-charts skill), GitOps patterns (use gitops-flux or gitops-argocd skills), container image generation (use container-analysis skill), cloud-specific resources (use aws-eks or gcp-gke skills).
|
Kubernetes Native Skill
Purpose
Provides patterns and best practices for native Kubernetes resource definitions using YAML manifests. Covers workload resources (Deployments, StatefulSets, Jobs, CronJobs), networking (Services, Gateway API v1), configuration (ConfigMaps, Secrets), security (RBAC, NetworkPolicy, SecurityContext), and operational concerns (autoscaling, resource management, cost optimization). This skill is referenced by the iac-generator agent when creating Kubernetes manifests and by iac-analyzer when evaluating existing cluster configurations.
Core Capabilities
1. Gateway API v1 Migration and Implementation
Context (2026): Gateway API reached GA (v1.0) in October 2023 and is now the recommended API for ingress traffic management, replacing the legacy Ingress API. Gateway API v1.4 introduced BackendTLSPolicy v1alpha3 for advanced TLS configurations.
Migration Strategy
Phase 1: Parallel Running
- Deploy Gateway API resources alongside existing Ingress resources
- Test traffic routing through both systems simultaneously
- Validate Gateway Programmed status condition is True before proceeding
- Incremental migration: move services one at a time rather than bulk deletion
Phase 2: Validation
- Use
ingress2gateway tool for automated conversion from Ingress to HTTPRoute
- Verify all services accessible through new Gateway API routes
- Check conformance reports to select appropriate Gateway implementation (Envoy Gateway, NGINX Gateway Fabric, Istio)
- Validate BackendTLSPolicy configurations if using TLS re-encryption
Phase 3: Cutover
- Update DNS entries to point to Gateway service
- Monitor traffic metrics during switchover
- Retain Ingress resources for rollback capability
- Delete legacy Ingress resources only after 7-day stability period
Gateway API Resource Pattern
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: gateway-system
spec:
gatewayClassName: envoy-gateway
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls-cert
kind: Secret
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: production
spec:
parentRefs:
- name: production-gateway
namespace: gateway-system
sectionName: https
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: app-service-stable
port: 8080
weight: 80
- name: app-service-canary
port: 8080
weight: 20
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Environment
value: production
BackendTLSPolicy for TLS Re-Encryption
apiVersion: gateway.networking.k8s.io/v1alpha3
kind: BackendTLSPolicy
metadata:
name: app-backend-tls
namespace: production
spec:
targetRefs:
- kind: Service
name: app-service-stable
group: ""
validation:
caCertificateRefs:
- name: backend-ca-cert
kind: ConfigMap
group: ""
hostname: app-backend.production.svc.cluster.local
Important Gateway API Notes:
- Role separation: Gateway (cluster operator) vs HTTPRoute (app developer) with RBAC enforcement
- Cross-namespace certificates: NOT allowed; cert must be in same namespace as Gateway
- BackendTLSPolicy status: Limited representation when targeting multiple Services in HTTPRoutes on same Gateway
- Conformance reports: Check implementation-specific features before relying on advanced capabilities
- Migration tool: Use
kubectl ingress2gateway for automated Ingress → HTTPRoute conversion
2. Native Sidecar Containers (Kubernetes 1.29+)
Context (2026): Kubernetes 1.29 introduced native sidecar support using restartPolicy: Always on init containers, replacing the traditional sidecar pattern that used regular containers.
Native Sidecar Pattern
apiVersion: v1
kind: Pod
metadata:
name: app-with-sidecar
namespace: production
spec:
initContainers:
- name: log-forwarder
image: fluent/fluent-bit:3.0
restartPolicy: Always
ports:
- containerPort: 24224
protocol: TCP
startupProbe:
httpGet:
path: /api/v1/health
port: 2020
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 30
readinessProbe:
httpGet:
path: /api/v1/health
port: 2020
periodSeconds: 10
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo 'Sidecar initialized' >> /var/log/sidecar.log"]
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5 && /fluent-bit/bin/fluent-bit -s"]
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
volumeMounts:
- name: logs
mountPath: /var/log/app
- name: init-database-migration
image: app-migrator:1.0
command: ["/app/run-migrations.sh"]
containers:
- name: app
image: app:1.2.3
ports:
- containerPort: 8080
volumeMounts:
- name: logs
mountPath: /var/log/app
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi
volumes:
- name: logs
emptyDir: {}
terminationGracePeriodSeconds: 60
Native Sidecar Behavior:
- Startup: Sidecars start in init container sequence but DON'T block subsequent containers (once startupProbe succeeds)
- Readiness: Sidecar readiness probes contribute to overall Pod readiness
- Termination: Sidecars terminate AFTER main containers in REVERSE startup order
- Job compatibility: Native sidecars don't prevent Job completion (traditional sidecars would)
- Version requirement: Kubernetes 1.29+ (both API server and nodes)
Use Cases:
- Logging/metrics agents (Fluent Bit, Datadog, Prometheus exporter)
- Service mesh proxies (Istio, Linkerd)
- Secret rotation agents (external-secrets, vault-agent)
- Configuration sync (consul-template, confd)
3. Workload Resource Patterns
Deployment (Stateless Applications)
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: production
labels:
app: frontend
tier: web
version: v1.2.3
spec:
replicas: 3
revisionHistoryLimit: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: frontend
tier: web
template:
metadata:
labels:
app: frontend
tier: web
version: v1.2.3
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
serviceAccountName: frontend-sa
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: frontend
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: workload-type
operator: In
values:
- spot-optimized
- weight: 20
preference:
matchExpressions:
- key: workload-type
operator: In
values:
- on-demand
tolerations:
- key: "spot"
operator: "Equal"
value: "true"
effect: "NoSchedule"
terminationGracePeriodSeconds: 120
containers:
- name: frontend
image: frontend:1.2.3
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
ports:
- name: http
containerPort: 8080
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: environment
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
startupProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 30
livenessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: http
periodSeconds: 5
successThreshold: 1
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15 && /app/graceful-shutdown.sh"]
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 768Mi
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
- name: config
mountPath: /app/config
readOnly: true
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: config
configMap:
name: app-config
defaultMode: 0444
StatefulSet (Stateful Applications)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: database
namespace: production
spec:
serviceName: database-headless
replicas: 3
revisionHistoryLimit: 10
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0
selector:
matchLabels:
app: database
template:
metadata:
labels:
app: database
spec:
securityContext:
runAsNonRoot: true
runAsUser: 999
fsGroup: 999
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- database
topologyKey: topology.kubernetes.io/zone
containers:
- name: postgres
image: postgres:16-alpine
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
ports:
- name: postgres
containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
livenessProbe:
exec:
command:
- pg_isready
- -U
- postgres
periodSeconds: 10
failureThreshold: 3
readinessProbe:
exec:
command:
- pg_isready
- -U
- postgres
periodSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 1000m
memory: 2Gi
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
- name: tmp
mountPath: /tmp
- name: run
mountPath: /var/run/postgresql
volumes:
- name: tmp
emptyDir: {}
- name: run
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
---
apiVersion: v1
kind: Service
metadata:
name: database-headless
namespace: production
spec:
clusterIP: None
selector:
app: database
ports:
- name: postgres
port: 5432
targetPort: postgres
Job and CronJob
apiVersion: batch/v1
kind: Job
metadata:
name: database-migration
namespace: production
spec:
parallelism: 1
completions: 1
backoffLimit: 3
activeDeadlineSeconds: 600
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
job: database-migration
spec:
restartPolicy: OnFailure
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
initContainers:
- name: log-sidecar
image: fluent/fluent-bit:3.0
restartPolicy: Always
containers:
- name: migrator
image: app-migrator:1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup
namespace: production
spec:
schedule: "0 2 * * *"
timeZone: "America/New_York"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
startingDeadlineSeconds: 300
jobTemplate:
spec:
backoffLimit: 2
ttlSecondsAfterFinished: 86400
template:
spec:
restartPolicy: OnFailure
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: backup
image: backup-tool:1.0
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
env:
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: s3-credentials
key: access_key_id
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: s3-credentials
key: secret_access_key
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
4. Service Networking
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: production
labels:
app: backend
spec:
type: ClusterIP
selector:
app: backend
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
---
apiVersion: v1
kind: Service
metadata:
name: frontend-lb
namespace: production
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
cloud.google.com/load-balancer-type: "External"
spec:
type: LoadBalancer
selector:
app: frontend
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
- name: https
protocol: TCP
port: 443
targetPort: 8443
externalTrafficPolicy: Local
5. Configuration and Secrets
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
environment: "production"
log_level: "info"
feature_flags: "feature-a,feature-b"
app.yaml: |
server:
port: 8080
timeout: 30s
database:
max_connections: 100
connection_timeout: 5s
---
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
data:
password: cGxhY2Vob2xkZXI=
url: cG9zdGdyZXNxbDovL3VzZXI6cGFzc0BkYjo1NDMyL2RibmFtZQ==
---
apiVersion: v1
kind: Secret
metadata:
name: docker-registry-secret
namespace: production
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6InVzZXIiLCJwYXNzd29yZCI6InBhc3MiLCJhdXRoIjoiZFhObGNqcHdZWE56In19fQ==
Secret Management Best Practices:
- NEVER commit actual secrets to Git - use
.env.example pattern for documentation
- Use external secret management:
- external-secrets-operator: Sync from AWS Secrets Manager, GCP Secret Manager, Vault
- sealed-secrets: Encrypt secrets for GitOps workflows
- SOPS: Encrypt secrets with Age or AWS KMS
- Use short-lived credentials via IRSA (AWS) or Workload Identity (GCP)
- Rotate secrets regularly using automated tools
6. Security Context and RBAC
apiVersion: v1
kind: ServiceAccount
metadata:
name: frontend-sa
namespace: production
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/frontend-role
iam.gke.io/gcp-service-account: frontend@project-id.iam.gserviceaccount.com
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: config-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "watch"]
resourceNames: ["app-config", "db-credentials"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: frontend-config-reader
namespace: production
subjects:
- kind: ServiceAccount
name: frontend-sa
namespace: production
roleRef:
kind: Role
name: config-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: gateway-system
- podSelector:
matchLabels:
app: gateway
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 80
- to:
- namespaceSelector:
matchLabels:
name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
7. Autoscaling and Resource Management
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: frontend-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: frontend
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 2
periodSeconds: 60
selectPolicy: Min
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 30
- type: Pods
value: 4
periodSeconds: 30
selectPolicy: Max
---
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: backend-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: backend
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: backend
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 2000m
memory: 4Gi
controlledResources: ["cpu", "memory"]
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: frontend-pdb
namespace: production
spec:
minAvailable: 2
selector:
matchLabels:
app: frontend
8. Multi-Environment Configuration with Kustomize
# Directory structure
base/
deployment.yaml
service.yaml
configmap.yaml
kustomization.yaml
overlays/
staging/
kustomization.yaml
patch-deployment.yaml
configmap.yaml
production/
kustomization.yaml
patch-deployment.yaml
configmap.yaml
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
commonLabels:
app: myapp
managed-by: kustomize
overlays/staging/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: staging
bases:
- ../../base
patchesStrategicMerge:
- patch-deployment.yaml
configMapGenerator:
- name: app-config
behavior: replace
literals:
- environment=staging
- log_level=debug
- replicas=1
images:
- name: app
newName: registry.example.com/app
newTag: staging-latest
commonAnnotations:
environment: staging
overlays/staging/patch-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: workload-type
operator: In
values:
- spot-optimized
tolerations:
- key: "spot"
operator: "Equal"
value: "true"
effect: "NoSchedule"
containers:
- name: app
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 200m
memory: 512Mi
overlays/production/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
bases:
- ../../base
patchesStrategicMerge:
- patch-deployment.yaml
configMapGenerator:
- name: app-config
behavior: replace
literals:
- environment=production
- log_level=warn
- replicas=5
images:
- name: app
newName: registry.example.com/app
newTag: v1.2.3
commonAnnotations:
environment: production
overlays/production/patch-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 5
template:
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: workload-type
operator: In
values:
- spot-optimized
- weight: 20
preference:
matchExpressions:
- key: workload-type
operator: In
values:
- on-demand
tolerations:
- key: "spot"
operator: "Equal"
value: "true"
effect: "NoSchedule"
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: myapp
containers:
- name: app
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 768Mi
9. Cost Optimization Strategies
Spot Instance Configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: batch-processor
namespace: production
spec:
replicas: 10
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: workload-type
operator: In
values:
- spot-optimized
tolerations:
- key: "spot"
operator: "Equal"
value: "true"
effect: "NoSchedule"
terminationGracePeriodSeconds: 120
containers:
- name: processor
image: batch-processor:1.0
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
# Deregister from load balancer
sleep 15
# Drain in-flight requests
/app/graceful-shutdown.sh
# Final cleanup
exit 0
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 1000m
memory: 2Gi
Right-Sizing Best Practices
Resource Request Sizing:
- CPU requests: Set at 40-70% of expected average usage (avoid over-provisioning)
- Memory requests: Set slightly above average usage (avoid OOM kills)
- CPU limits: Set 1.5-2x requests (allow for bursts)
- Memory limits: Set 1.2-1.5x requests (allow for spikes)
Monitoring and Adjustment:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: app-vpa-recommender
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: app
updatePolicy:
updateMode: "Off"
Non-Production Environment Scheduling
apiVersion: batch/v1
kind: CronJob
metadata:
name: staging-scaledown
namespace: kube-system
spec:
schedule: "0 19 * * 1-5"
jobTemplate:
spec:
template:
spec:
serviceAccountName: environment-manager
restartPolicy: OnFailure
containers:
- name: scaledown
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
# Scale down all Deployments in staging namespace
kubectl scale deployment --all --replicas=0 -n staging
# Scale down StatefulSets
kubectl scale statefulset --all --replicas=0 -n staging
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: staging-scaleup
namespace: kube-system
spec:
schedule: "0 8 * * 1-5"
jobTemplate:
spec:
template:
spec:
serviceAccountName: environment-manager
restartPolicy: OnFailure
containers:
- name: scaleup
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
# Restore Deployments to desired state (stored in annotations)
kubectl get deployment -n staging -o json | \
jq -r '.items[] | "\(.metadata.name) \(.metadata.annotations["original-replicas"] // "1")"' | \
while read name replicas; do
kubectl scale deployment $name --replicas=$replicas -n staging
done
10. Validation and Security Scanning
Technical Validation
kubectl apply --dry-run=client -f deployment.yaml
kubectl apply --dry-run=server -f deployment.yaml
kubectl kustomize overlays/production
kubeconform -summary -output json deployment.yaml
yamllint -c .yamllint.yml manifests/
Intent Validation (Policy-as-Code)
trivy config --severity CRITICAL,HIGH manifests/
checkov --directory manifests/ --framework kubernetes --check CIS_KUBERNETES
conftest test manifests/ -p policies/
kyverno apply policies/ --resource manifests/
Example OPA Policy (require resource limits):
# policy/resource-limits.rego
package kubernetes.admission
deny[msg] {
input.kind == "Deployment"
container := input.spec.template.spec.containers[_]
not container.resources.limits
msg = sprintf("Container '%s' must define resource limits", [container.name])
}
AI Hallucination Detection
When working with AI-generated Kubernetes manifests:
-
Resource Type Validation: Ensure all apiVersion and kind combinations are valid
kubectl api-resources | grep <resource-type>
-
Schema Validation: Verify field names and types using kubectl explain
kubectl explain Deployment.spec.template.spec.containers
kubectl explain Gateway.spec.listeners.tls
-
Dependency Validation: Check referenced resources exist
kubectl get configmap app-config -n production
kubectl get secret db-credentials -n production
kubectl get serviceaccount frontend-sa -n production
-
Test in Non-Production: Always apply generated manifests to staging before production
-
Human Review Required: Never auto-deploy AI-generated configurations without approval gate
Security Best Practices
✅ Required Security Practices
- Non-root containers: Always set
runAsNonRoot: true and runAsUser: 1000+
- Read-only root filesystem: Set
readOnlyRootFilesystem: true where possible
- Drop capabilities: Remove all Linux capabilities with
capabilities.drop: [ALL]
- No privilege escalation: Set
allowPrivilegeEscalation: false
- Seccomp profile: Use
seccompProfile.type: RuntimeDefault
- Resource limits: Always define requests and limits for CPU and memory
- Network policies: Implement network segmentation with NetworkPolicy
- RBAC: Use ServiceAccounts with minimal permissions via Role/RoleBinding
- Secret management: Use external-secrets, sealed-secrets, or SOPS (never commit secrets)
- OIDC authentication: Use IRSA (AWS) or Workload Identity (GCP) instead of long-lived credentials
✅ Validation Commands
kubectl get pod <pod-name> -o json | jq '.spec.securityContext'
kubectl exec <pod-name> -- id
kubectl describe pod <pod-name> | grep -A 5 "Limits:"
kubectl auth can-i --list --as=system:serviceaccount:production:frontend-sa
kubectl describe networkpolicy frontend-netpol -n production
kubectl get gateway production-gateway -n gateway-system -o yaml
kubectl get httproute app-route -n production -o yaml
Anti-Patterns to Avoid
❌ Common Mistakes
-
Running as root:
spec:
containers:
- name: app
image: app:latest
FIX: Always specify non-root user:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: app
image: app:latest
-
No resource limits:
containers:
- name: app
image: app:latest
FIX: Always define resources:
containers:
- name: app
image: app:latest
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 768Mi
-
Using latest tag:
image: app:latest
FIX: Pin specific versions:
image: app:1.2.3
-
Hardcoded secrets:
env:
- name: DATABASE_PASSWORD
value: "mysecretpassword"
FIX: Use Secret references:
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
-
No health checks:
containers:
- name: app
image: app:latest
FIX: Add startup, liveness, and readiness probes:
containers:
- name: app
image: app:latest
startupProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 30
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
-
Ingress instead of Gateway API (2026 context):
apiVersion: networking.k8s.io/v1
kind: Ingress
FIX: Migrate to Gateway API v1:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
-
Traditional sidecars in Kubernetes 1.29+:
containers:
- name: app
image: app:latest
- name: sidecar
image: sidecar:latest
FIX: Use native sidecar pattern:
initContainers:
- name: sidecar
image: sidecar:latest
restartPolicy: Always
containers:
- name: app
image: app:latest
-
No graceful shutdown:
containers:
- name: app
image: app:latest
FIX: Add preStop hook:
containers:
- name: app
image: app:latest
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15 && /app/graceful-shutdown.sh"]
-
Cross-namespace certificate references in Gateway API:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
spec:
listeners:
- tls:
certificateRefs:
- name: cert
namespace: cert-manager
FIX: Certificate in same namespace as Gateway:
spec:
listeners:
- tls:
certificateRefs:
- name: cert
-
No Pod Disruption Budget for critical workloads:
FIX: Define PDB to maintain availability:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: myapp
Integration with iac-team Agents
With iac-generator Agent
When the iac-generator agent invokes this skill:
- Context: Provide application type (stateless/stateful), scale requirements, environment
- Security: Apply SPEC.md constraints (non-root, resource limits, RBAC)
- Optimization: Include cost optimization patterns (Spot instances, right-sizing)
- Gateway API: Use v1 Gateway API for ingress (not legacy Ingress)
- Native sidecars: Use Kubernetes 1.29+ pattern with
restartPolicy: Always
- Validation: Ensure manifests pass
kubectl --dry-run and security scanning
With iac-analyzer Agent
When iac-analyzer detects Kubernetes resources:
- Identify resource types and relationships (Deployment → Service → Gateway)
- Analyze security posture (SecurityContext, RBAC, NetworkPolicy)
- Detect optimization opportunities (missing HPA, over-provisioned resources)
- Flag outdated patterns (Ingress API, traditional sidecars)
- Recommend right-sizing based on resource usage
- Validate against Gateway API v1 compatibility
Best Practices Summary
- Always use Gateway API v1 for ingress (not legacy Ingress)
- Implement native sidecars with
restartPolicy: Always on init containers (Kubernetes 1.29+)
- Run as non-root with read-only root filesystem and dropped capabilities
- Define resource requests and limits targeting 40-70% CPU utilization
- Use HPA for horizontal scaling and VPA for right-sizing recommendations
- Implement PDB for critical workloads to maintain availability during disruptions
- Add health checks (startup, liveness, readiness) for all containers
- Use external secret management (external-secrets, sealed-secrets, SOPS)
- Apply NetworkPolicy for network segmentation and zero-trust networking
- Leverage Spot instances for cost optimization (70% savings) with proper interruption handling
- Validate with multi-phase pipeline (technical + intent validation)
- Test in non-production first before deploying AI-generated manifests
- Use Kustomize overlays for multi-environment configuration management
- Implement graceful shutdown with preStop hooks and terminationGracePeriodSeconds
- Monitor and adjust resource allocations quarterly using VPA recommendations
References
For comprehensive patterns and advanced techniques, see:
Version: 1.0.0
Last Updated: 2026-02-03
Compatible With: Kubernetes 1.29+, Gateway API v1.x
This skill is part of the iac-team plugin. For related capabilities, see: container-analysis (Dockerfiles), gitops-flux (GitOps patterns), helm-charts (Helm templates), terraform-modules (infrastructure provisioning), aws-eks (AWS specifics), gcp-gke (GCP specifics).