HTTP routing, TLS termination, and path-based traffic management in Helm charts
Lesson 09 — Helm Charts: From Templates to Production Kubernetes
Without Ingress, every Service that needs external access requires its own LoadBalancer — expensive and unmanageable at scale.
/api to one Service, /web to anotherapp.example.com vs api.example.comTells Kubernetes which controller handles an Ingress:
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
annotations:
ingressclass.kubernetes.io/
is-default-class: "true"
spec:
controller: k8s.io/ingress-nginx
A typical templates/ingress.yaml with conditional creation and dynamic hosts:
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.className }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "myapp.fullname" $ }}
port:
number: {{ .port | default 80 }}
{{- end }}
{{- end }}
{{- end }}
Add TLS termination with a Kubernetes Secret or cert-manager:
spec:
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
rules:
# ... host rules as before
cert-manager.io/cluster-issuer: letsencrypt-prod and the TLS secret will be provisioned automatically.ingress:
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
Annotations control controller-specific behaviour:
ingress:
annotations:
# Rewrite /api/v1/users -> /users
nginx.ingress.kubernetes.io/rewrite-target: /$1
# Force HTTPS redirect
nginx.ingress.kubernetes.io/ssl-redirect: "true"
# Max upload size
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
# Rate limiting
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-connections: "5"
# Basic auth
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Auth Required"
# cert-manager
cert-manager.io/cluster-issuer: letsencrypt-prod
The production-ready ingress values block:
ingress:
enabled: false
className: nginx
annotations: {}
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
port: 80
- host: api.example.com
paths:
- path: /v1
pathType: Prefix
port: 8080
- path: /health
pathType: Exact
port: 8080
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
- api.example.com
defaultBackend:
service:
name: default-backend
port:
number: 80
{{- if .Values.ingress.enabled }} — not every environment needs IngressPrefix, Exact, or ImplementationSpecificannotations map for controller-specific features (rewrite, rate limit, auth)hosts array in values