Zum Inhalt springen

Expose your app with Ingress and TLS

Dieser Inhalt ist noch nicht in deiner Sprache verfügbar.

Deploy your first app left the demo app reachable only from inside the cluster. This guide puts it on the public internet, with a real domain and a valid HTTPS certificate: an ingress controller in front, cert-manager fetching certificates from Let’s Encrypt, and one Ingress object per app. The pattern works on any conformant Kubernetes cluster; we use a paasbox cluster as the example.

  • The demo app from deploy your first app running (namespace demo, Service web on port 80) — or any Service you want to expose.
  • helm installed locally, alongside kubectl.
  • A domain name you control, with access to its DNS records. We’ll use demo.example.com — substitute your own throughout.

The ingress controller is the single front door for all HTTP(S) traffic into the cluster. Its Service has type LoadBalancer, so the cloud-controller-manager automatically provisions a Hetzner load balancer in your project when you install it:

Terminal window
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.service.annotations."load-balancer\.hetzner\.cloud/location"=nbg1

Set the location annotation to your cluster’s region (nbg1 here). Wait for the load balancer to get a public IP:

Terminal window
kubectl -n ingress-nginx get svc ingress-nginx-controller -w

After a minute or so the EXTERNAL-IP column fills in. Note the IP — your DNS record points at it.

This load balancer appears on your Hetzner bill. Like your worker nodes, it lives in your own Hetzner project and Hetzner bills you for it directly — it’s part of the two bills split, not the paasbox fee. One load balancer serves all the apps behind the ingress controller, so you don’t pay per app. Deleting the Service (see cleanup) deletes the load balancer and stops the charge.

2. Install cert-manager and a Let’s Encrypt issuer

Section titled “2. Install cert-manager and a Let’s Encrypt issuer”

cert-manager watches your Ingress objects and keeps valid certificates in place — issuing, renewing, all automatic:

Terminal window
helm upgrade --install cert-manager cert-manager \
--repo https://charts.jetstack.io \
--namespace cert-manager --create-namespace \
--set crds.enabled=true

Then give it an account with Let’s Encrypt. Save this as cluster-issuer.yamlreplace the email with your own (Let’s Encrypt sends expiry notices there, though cert-manager renews well before expiry):

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: you@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
ingressClassName: nginx
Terminal window
kubectl apply -f cluster-issuer.yaml

This uses the HTTP-01 challenge: Let’s Encrypt verifies you control the domain by fetching a token over plain HTTP through your new ingress. No DNS provider credentials needed — but the domain must resolve to your load balancer first, which is step 3.

At your DNS provider, create an A record for your hostname pointing at the load balancer IP from step 1:

demo.example.com. 300 IN A <EXTERNAL-IP>

Confirm it resolves before moving on — the certificate can’t be issued until it does:

Terminal window
dig +short demo.example.com

The Ingress ties it together: route demo.example.com to the web Service, and let cert-manager handle the certificate. Save as ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
namespace: demo
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts: [demo.example.com]
secretName: web-tls
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port: { number: 80 }
Terminal window
kubectl apply -f ingress.yaml

cert-manager spots the annotation, runs the HTTP-01 challenge, and stores the issued certificate in the web-tls Secret. Watch it happen:

Terminal window
kubectl -n demo get certificate -w

READY flips to True — typically within a minute or two.

Certificate stuck in pending? Almost always DNS: the record doesn’t resolve publicly yet (fresh records and negative caching can take a few minutes), or it points at the wrong IP. kubectl -n demo describe certificaterequest and the cert-manager logs say exactly which check failed. Fix the record, and issuance proceeds on its own.

Terminal window
curl https://demo.example.com

A response from the app, over HTTPS, with a valid certificate — check the padlock in a browser, or:

Terminal window
curl -vI https://demo.example.com 2>&1 | grep -E 'subject|issuer'

From here, every additional app is just another Ingress object: same controller, same load balancer, same issuer — one manifest per hostname.

If this was just an experiment, delete in reverse order:

Terminal window
kubectl delete -f ingress.yaml # releases the certificate secret
helm -n cert-manager uninstall cert-manager
helm -n ingress-nginx uninstall ingress-nginx # deletes the Hetzner load balancer
kubectl delete namespace ingress-nginx cert-manager

Uninstalling ingress-nginx removes the LoadBalancer Service, and the cloud-controller-manager deletes the Hetzner load balancer with it — that line item leaves your Hetzner bill. Remember to remove the DNS record too.

  • Scale & autoscale workloads — now that traffic can reach the app, let the cluster grow and shrink it.
  • Guides for running Postgres with CloudNativePG and deploying a full Django SaaS behind this ingress setup are on the way.