Terminate TLS at the ingress
TLS termination means the ingress controller holds the certificate, decrypts incoming HTTPS, and forwards plain HTTP to your pods inside the cluster. Your applications never handle certificates; the controller does so once for every application behind it.
How it works#
A client opens an HTTPS connection. The Hetzner load balancer runs at Layer 4, so it forwards the encrypted TCP stream to the ingress controller without decrypting it. The controller is where TLS ends: it presents the certificate, completes the handshake, and decrypts the request, then routes plain HTTP to the backend pod over the cluster network. The pod receives a normal HTTP request and responds as if no TLS were involved. This is the standard division of responsibility: the load balancer remains a plain TCP forwarder, and the reverse proxy holds the certificate and terminates TLS.
flowchart LR client[Client] -->|HTTPS| lb[Hetzner load balancer] lb -->|HTTPS| ctrl[Ingress controller<br/>holds the certificate] ctrl -->|plain HTTP| pod[Backend pod]
The certificate and its private key live in a Kubernetes Secret of type kubernetes.io/tls. The Ingress names that Secret, and the controller loads it. If you have not installed a controller yet, start with Install Traefik .
This is a reverse-proxy pattern, not an ingress-nginx feature. Any reverse proxy in front of your applications works the same way: Traefik reads the same kind of Secret, and Istio's ingress gateway (an Envoy proxy) terminates TLS by referencing a kubernetes.io/tls Secret from its Gateway resource. The resource you write changes, but the function is identical: hold the certificate, terminate TLS, forward plain traffic to the application.
Point an Ingress at a certificate Secret#
Add a tls block naming the hosts and the Secret that holds their certificate:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts: [my-app.example.com]
secretName: my-app-tls
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
The nginx.ingress.kubernetes.io/ssl-redirect: "true" annotation redirects plain HTTP requests to HTTPS, so a caller on http:// is redirected to https://.
Where the certificate comes from#
Populate the Secret in one of two ways: create it manually from a certificate you purchased, or let cert-manager request and renew one for you.
If you purchased a certificate, create the Secret manually from the certificate and key files:
$ kubectl create secret tls my-app-tls --cert=tls.crt --key=tls.key
You then renew it manually before it expires.
The usual setup delegates renewal to cert-manager : it requests the certificate from a certificate authority, writes it into the Secret, and renews it automatically. Install cert-manager, create a Let's Encrypt ClusterIssuer (a cluster-wide object that tells cert-manager which certificate authority to use and how to prove domain ownership), and add cert-manager.io/cluster-issuer: <issuer-name> to the Ingress annotations. Syself Autopilot does not provide cert-manager; you install it yourself.
Automate the certificate and DNS record#
Putting a service on a name involves two recurring tasks: obtain a certificate for the name, and publish a DNS record that points the name to your load balancer. Automate both, and adding a hostname becomes the only manual step:
- cert-manager requests the certificate over ACME, writes it into the Secret above, and renews it before it expires.
- external-dns reads the host from the
Ingressand creates the matching DNS record at your provider, updating it if the load balancer address changes.
With both in place, you add a hostname to an Ingress and the certificate and the DNS record are created automatically.
Terminating at the ingress leaves the last hop, controller to pod, as plain HTTP inside the cluster. For most workloads this is acceptable, because the pod network is not exposed. If a compliance rule requires encryption all the way to the pod, you can re-encrypt: the controller terminates the client's TLS, then opens a second TLS connection to the backend. In ingress-nginx this is the nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" annotation, and the backend pod must serve HTTPS itself. A service mesh with mTLS is an alternative way to encrypt that hop; see Choose a service mesh .
Next#
- Set up a Let's Encrypt ClusterIssuer to issue and renew certificates automatically.
- Serve multiple domains and wildcards to hold a certificate per host behind one controller.
For how Syself Autopilot handles secrets and encryption across the cluster, see security .
Route traffic with the Gateway API
Use the Gateway API (Gateway and HTTPRoute) instead of Ingress for role-split routing and richer traffic rules on Syself Autopilot.
Serve multiple domains and wildcards
Host many client domains and wildcard subdomains behind one ingress and one Hetzner load balancer.