Platform engineering
Platform engineering with reusable Helm charts
A reusable chart is a paved road when teams depend on it as a small, versioned application API—not merely shared templates.
Chart fidelity
Examples match Universal Helm Chart application 0.4.3 portable keys (values.yaml). Simple path: route.gateway / route.gatewayNamespace / route.sectionName. Advanced: full HTTPRoute.spec via route.spec. kgateway-specific policies belong in labeled escape-hatch manifests—not invented route.cors keys. Lab Gateway namespace in this series: kgateway-system.
Chart 0.4.3 — args / multi-port / service.enabled
From application 0.4.3: main container args (default []); Deployment revisionHistoryLimit (default 10); multi-port via containerPorts and service.ports; optional chart-managed Service via service.enabled (default true). When service.ports is set, Ingress / simple HTTPRoute / NOTES use the first entry (templates/_helpers.tpl application.servicePort). Legacy single-port service.name / service.port / service.protocol / service.appProtocol remain supported. Set service.enabled: false when the workload does not need a chart-managed Service. Do not invent other port keys.
The platform contract
Our conventional service contract renders Deployment, ClusterIP Service, probes, resources, optional HPA, migration Job, ServiceMonitor, and HTTPRoute. The values schema defines supported inputs and fails on mistakes. Apps own immutable image versions, probe paths, sizing, migrations, hostnames, and approved traffic weights. Platform owns templates, schema, secure defaults, releases, GatewayClass, shared Gateway, TLS, listeners, and observability. Cluster operators own controllers, CRDs, admission and network policy, certificates, secrets delivery, capacity, and alerts.
Supported extension points are Secret references, ConfigMaps, volumes, scheduling, HTTPRoute rules, and reviewed extraManifests. Arbitrary templates are not the normal path.
Compatibility and deprecation
Semantic versions make compatibility explicit: PATCH fixes rendering, MINOR adds optional features, and MAJOR may remove or reinterpret keys. Pin chart and image versions; reject floating dependencies and latest. Deprecation requires a replacement, warnings, an announced window, consumer inventory, and removal only in a major release.
Production-shaped values
image: ghcr.io/example-org/orders-api
imageTag: "2026.08.31-1"
service: {type: ClusterIP, port: 8080}
readinessProbe: {httpGet: {path: /ready, port: http}}
livenessProbe: {httpGet: {path: /live, port: http}}
resources:
requests: {cpu: 200m, memory: 256Mi}
limits: {cpu: "1", memory: 512Mi}
autoscaling: {enabled: true, minReplicas: 3, maxReplicas: 12}
serviceMonitor:
enabled: true
endpoints: [{port: http, path: /metrics, interval: 30s}]The baseline runs non-root with RuntimeDefault seccomp, drops all capabilities, forbids privilege escalation, and uses a read-only root filesystem. NodePort, hostPath, privileged mode, plaintext secrets, and mutable tags are prohibited.
Jobs and migrations
job:
enabled: true
name: orders-api-migrate
imageTag: "2026.08.31-1"
restartPolicy: Never
backoffLimit: 1
command: ["/app/orders-api"]
args: ["migrate"]
envFrom: [{secretRef: {name: orders-api-runtime}}]Argo CD runs migration before Deployment and stops on failure. Migrations support old and new app versions using expand, deploy, contract. Do not run long schema changes in every pod or commit credentials.
kgateway as a capability
Platform owns GatewayClass and shared Gateway; apps own namespaced HTTPRoutes. The HTTPS listener terminates TLS, limits hostnames to *.example.com, and allows only namespaces labeled gateway-access=external. TLS Secrets remain in the Gateway namespace. Cross-namespace backends require ReferenceGrant.
route:
enabled: true
gateway: external
gatewayNamespace: kgateway-system
sectionName: https-apps
hostnames: [orders.example.com]
rules:
- backendRefs:
- {name: "", weight: 90}
- {name: orders-api-canary, port: 8080, weight: 10}
timeouts: {request: 30s, backendRequest: 25s}Promote a 90/10 canary only after Accepted=True, ResolvedRefs=True, health, latency, and errors are checked. Restore 100/0 before rollback. Gateway API routing and timeouts are portable; retry details may not be. kgateway retries, CORS, rate limits, and backend TLS use reviewed, labeled escape-hatch policies with pinned CRD/controller version, owner, and expiry. If exceptions dominate, use a dedicated chart.
Policy and ownership boundaries
Admission enforces non-root, seccomp, approved registries, resources, and no privileged, host namespace, or hostPath use. Default-deny NetworkPolicy permits required gateway and monitoring ingress, DNS, and declared dependencies. These cluster-specific controls stay centrally versioned.
Onboarding and Argo CD promotion
- Create a thin wrapper with a pinned chart and app values.
- Render and validate schema in CI; scan images and manifests.
- Review namespace access, hostname, probes, resources, and exceptions.
- Sync dev; verify Job, Deployment, endpoints, ServiceMonitor, route conditions, and smoke traffic.
- Promote the same image digest to production; do not rebuild.
Sync migration Job, Deployment and Service, HTTPRoute, then smoke checks. A failed migration stops before pods. A failed rollout retains the prior ReplicaSet when readiness and RollingUpdate are correct. Argo CD Synced alone does not prove routing works.
Platform-wide upgrade and rollback
- Publish an immutable chart and changelog.
- Render every known profile, compare golden YAML, and test supported Kubernetes/controllers.
- Bump a non-critical canary, then one ring at a time.
- Watch rollout, Jobs, HPA, scrape health, route status, gateway errors, and latency.
- Automate remaining bumps after the observation window; inventory old pins.
Rollback: revert the GitOps commit, sync Argo CD, and restore traffic weights. Prefer a forward database fix; destructive down-migrations are not generic rollback. Pause on schema, admission, route, probe, or SLO failure.
Migrate safely from ingress-nginx
Kubernetes Ingress is stable and is not deprecated. Migration drivers concern ingress-nginx controller retirement, risk, or missing capabilities, not removal of the Ingress API.
- Inventory annotations, snippets, TLS, DNS, redirects, rewrites, and timeouts.
- Map portable behavior to HTTPRoute and reviewed controller behavior to kgateway policy.
- Install kgateway and shared Gateway without changing DNS; validate TLS, namespace policy, and telemetry.
- Dual-publish only for a bounded test; compare behavior and SLOs.
- Shift traffic gradually; retain the old path through rollback, then retire ingress-nginx.
Limits and anti-patterns
- Operators, databases, complex stateful systems, and vendor apps often need dedicated charts.
- Do not expose every Kubernetes field or let escape hatches bypass RBAC.
- Do not embed secrets, TLS keys, private registries, or internal URLs.
- Do not claim portability for kgateway-specific CRDs.
- Do not upgrade fleet-wide without pins, rings, evidence, and reversal.
Related reading
Production Helm charts checklist → · One chart vs many → · Internal Developer Platform → · Gateway API vs Ingress → · GitOps repository structure →