TL;DR
- Deploy a sample application to Kubernetes and expose it with a
ClusterIPService. - Install Traefik as the Ingress Controller and configure an Ingress resource to route traffic to the application.
- Configure DNS and HTTPS with cert-manager and Let’s Encrypt.
- Redirect HTTP traffic to HTTPS and verify the application is accessible through its domain.
A Kubernetes application can be running perfectly while still being inaccessible from the internet. If the application is exposed through a ClusterIP Service, external users cannot reach it directly. You could expose each application with a separate LoadBalancer, but managing multiple entry points can become difficult as the cluster grows. The challenge is routing external traffic to the correct application through a single entry point while also securing that traffic with HTTPS.
In this article, you'll deploy an application on Kubernetes using a ClusterIP Service, install Traefik as the Ingress Controller with an external LoadBalancer, configure an Ingress resource and DNS, and secure the application with HTTPS using cert-manager and Let's Encrypt.
Note
Before you begin, make sure you have:
- A Kubernetes cluster with at least one worker node.
kubectlinstalled and configured to access the cluster.- Helm installed for deploying Traefik.
- A domain or subdomain that you can manage, such as
app.example.com. - Access to the domain's DNS records so you can point the domain to the Traefik LoadBalancer.
- A publicly reachable Kubernetes cluster with an external IP or LoadBalancer that can receive HTTP and HTTPS traffic.
- Basic familiarity with Kubernetes Deployments, Services, and Pods.
How Kubernetes Ingress Works
Kubernetes Ingress defines rules for routing external HTTP and HTTPS traffic to Services running inside a Kubernetes cluster. You can use these rules to route requests based on a domain name or URL path without exposing each application separately. Kubernetes Ingress documentation
An Ingress Controller processes these rules and routes incoming traffic to the appropriate Service. In this article, you'll use Traefik as the Ingress Controller.
The request flow is:
Client → Traefik → Ingress → Service → Pod
The Ingress defines how requests should be routed. Traefik receives the external request and uses the matching Ingress rule to forward it to the appropriate Service. The Service then routes the request to the application's Pod.
For example, a request to https://app.example.com can be routed by Traefik to the demo-app Service, which forwards the request to the NGINX Pod. Replace https://app.example.com with the URL you configured for your application.
This gives you a single entry point for routing traffic to applications running inside the Kubernetes cluster.
Deploy an Application to Kubernetes
Before configuring Ingress, deploy a sample application inside the Kubernetes cluster. This example uses NGINX and exposes it through a ClusterIP Service.
Create a Deployment named demo-app using the NGINX image:
kubectl create deployment demo-app --image=nginxCheck the Pod:
kubectl get podsYou should see the Pod in the Running state:
NAME READY STATUS RESTARTS AGE
demo-app-xxxxxxxxxx-xxxxx 1/1 Running 0 ...Next, expose the Deployment through a ClusterIP Service:
kubectl expose deployment demo-app \
--port=80 \
--target-port=80 \
--name=demo-appVerify the Service:
kubectl get service demo-appYou should see output similar to:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
demo-app ClusterIP 10.43.x.x <none> 80/TCP ...The ClusterIP Service makes the NGINX application available inside the Kubernetes cluster, but it does not expose the application directly to the internet. In the next section, you'll install Traefik and use it as the Ingress Controller to route external traffic to this Service.
Install and Configure Traefik
Traefik acts as the Ingress Controller and processes the Ingress resources you create in the Kubernetes cluster. Before installing it, add the Traefik Helm repository:
helm repo add traefik https://traefik.github.io/chartsYou should see:
"traefik" has been added to your repositoriesUpdate the Helm repositories to retrieve the latest chart information:
helm repo updateYou should see output similar to:
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "traefik" chart repository
Update Complete. ⎈Happy Helming!⎈Install Traefik in a dedicated traefik namespace:
helm install traefik traefik/traefik \
--namespace traefik \
--create-namespaceCheck the Traefik deployment:
kubectl get pods -n traefikYou should see the Traefik Pod in the Running state:
NAME READY STATUS RESTARTS AGE
traefik-xxxxxxxxxx-xxxxx 1/1 Running 0 ...Check the Traefik Service:
kubectl get service -n traefikYou should see a LoadBalancer Service similar to:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik LoadBalancer 10.43.x.x x.x.x.x 80:xxxxx/TCP,443:xxxxx/TCP ...The LoadBalancer Service provides the external entry point for Traefik. You will use its external IP address when configuring the DNS record for your application.
Next, verify that Traefik is available through its external IP:
curl -I http://<EXTERNAL-IP>Replace <EXTERNAL-IP> with the external IP address shown for the Traefik Service. You should receive an HTTP response from Traefik.
At this point, Traefik is running as the Ingress Controller and can process Ingress resources in the cluster. In the next section, you'll create an Ingress resource and configure DNS to route your domain to Traefik.
Configure Kubernetes Ingress and DNS
Now that Traefik is running, create an Ingress resource to route requests from your domain to the demo-app Service. Kubernetes Ingress uses host and path rules to determine where incoming traffic should be sent.
Before creating the Ingress, get the external IP address assigned to the Traefik Service:
kubectl get service -n traefikNote the value under EXTERNAL-IP. You will use this address when creating the DNS record for your application.
Create a file named demo-app-ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app
spec:
ingressClassName: traefik
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app
port:
number: 80Replace app.example.com with the domain or subdomain you want to use for your application.
The ingressClassName field associates the Ingress resource with the Traefik Ingress Controller. The host field defines the domain that Traefik should match, while the backend points the request to the demo-app Service on port 80.
Apply the Ingress configuration:
kubectl apply -f demo-app-ingress.yamlCheck the Ingress:
kubectl get ingress demo-appYou should see output similar to:
NAME CLASS HOSTS ADDRESS PORTS AGE
demo-app traefik app.example.com x.x.x.x 80 ...The ADDRESS should eventually show the external IP address assigned to Traefik.
Next, configure a DNS record for the domain you specified in the Ingress. Create an A record that points your domain to the external IP address of the Traefik Service.
For example:
Type: A
Name: app
Value: <TRAEFIK-EXTERNAL-IP>Replace <TRAEFIK-EXTERNAL-IP> with the external IP address from the Traefik Service.
After the DNS record propagates, verify that the domain resolves to the Traefik external IP:
dig +short app.example.comThe command should return the external IP address of your Traefik Service.
You can now access the application through your configured domain:
curl http://app.example.comYou should receive the NGINX response:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>At this point, requests to your domain are routed through Traefik to the demo-app Service and then to the NGINX Pod. In the next section, you'll configure HTTPS using cert-manager and Let's Encrypt.
Secure the Application with HTTPS
Now that your application is accessible through the configured domain, secure the connection with HTTPS. In this setup, cert-manager obtains and renews the TLS certificate from Let's Encrypt, while Traefik uses the generated Kubernetes Secret to terminate HTTPS traffic.
First, install cert-manager in the cluster:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yamlCheck that the cert-manager Pods are running:
kubectl get pods -n cert-managerYou should see the cert-manager components in the Running state:
NAME READY STATUS RESTARTS AGE
cert-manager-xxxxxxxxxx-xxxxx 1/1 Running 0 ...
cert-manager-cainjector-xxxxxxxxxx-xxxxx 1/1 Running 0 ...
cert-manager-webhook-xxxxxxxxxx-xxxxx 1/1 Running 0 ...Next, create a ClusterIssuer that uses Let's Encrypt to issue certificates:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
email: your-email@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
ingressClassName: traefikReplace your-email@example.com with your email address.
The ClusterIssuer defines Let's Encrypt as the certificate authority. The HTTP-01 solver allows Let's Encrypt to verify that you control the domain through an HTTP request handled by Traefik.
Save the configuration as letsencrypt-issuer.yaml and apply it:
kubectl apply -f letsencrypt-issuer.yamlVerify that the ClusterIssuer is ready:
kubectl get clusterissuerYou should see:
NAME READY AGE
letsencrypt-prod True ...Next, update the Ingress resource to request a TLS certificate from the letsencrypt-prod ClusterIssuer:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts:
- app.example.com
secretName: demo-app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app
port:
number: 80Replace app.example.com with the domain you configured for your application.
The cert-manager.io/cluster-issuer annotation tells cert-manager which ClusterIssuer to use. The tls section specifies the domain and the Kubernetes Secret where cert-manager will store the generated certificate. Traefik can then use this Secret to terminate HTTPS connections.
Apply the updated Ingress:
kubectl apply -f demo-app-ingress.yamlCheck the Certificate resource:
kubectl get certificateYou should see:
NAME READY SECRET AGE
demo-app-tls True demo-app-tls ...You can also check the certificate request and related resources if the certificate is still being issued:
kubectl describe certificate demo-app-tlsOnce the certificate is ready, verify that the TLS Secret exists:
kubectl get secret demo-app-tlsYou should see:
NAME TYPE DATA AGE
demo-app-tls kubernetes.io/tls 2 ...Finally, verify that the application is accessible through HTTPS:
curl -I https://app.example.comYou should receive an HTTPS response from the application.
At this point, cert-manager has obtained a TLS certificate from Let's Encrypt, stored it in a Kubernetes Secret, and Traefik is using the certificate to serve your application over HTTPS. In the next section, you'll configure HTTP traffic to redirect automatically to HTTPS.
Redirect HTTP Traffic to HTTPS
Now that HTTPS is configured, redirect HTTP requests to HTTPS so visitors always access your application through an encrypted connection.
Create a Traefik middleware that redirects HTTP requests to HTTPS:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: redirect-to-https
namespace: default
spec:
redirectScheme:
scheme: https
permanent: trueSave the configuration as redirect-to-https.yaml and apply it:
kubectl apply -f redirect-to-https.yamlVerify that the middleware was created:
kubectl get middleware redirect-to-httpsYou should see:
NAME AGE
redirect-to-https ...Next, update the Ingress resource to use the middleware for HTTP traffic:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-to-https@kubernetescrd
spec:
ingressClassName: traefik
tls:
- hosts:
- app.example.com
secretName: demo-app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-app
port:
number: 80Replace app.example.com with the domain you configured for your application.
Apply the updated Ingress:
kubectl apply -f demo-app-ingress.yamlVerify the Ingress:
kubectl get ingress demo-appFinally, send an HTTP request to your domain and check that it redirects to HTTPS:
curl -I http://app.example.comYou should see a 301 redirect with a Location header pointing to the HTTPS URL:
HTTP/1.1 301 Moved Permanently
Location: https://app.example.com/You can also verify that the HTTPS endpoint is accessible:
curl -I https://app.example.comThe application now redirects HTTP requests to HTTPS, ensuring that traffic reaches the application through the TLS-secured endpoint.
Troubleshoot and Clean Up
If the application is not accessible through the configured domain, check the Ingress, Traefik Service, and application Service to identify where the request is failing.
First, check the Ingress resource:
kubectl get ingress demo-appIf the ADDRESS field is empty, check the Traefik Service:
kubectl get service -n traefikMake sure the Traefik Service has an external IP address. If it does not, check the Traefik Pod and its events:
kubectl get pods -n traefikkubectl describe service traefik -n traefikIf the Ingress has an address but requests are still failing, verify that the application Service has endpoints:
kubectl get endpoints demo-appYou should see the IP address of the NGINX Pod associated with the Service.
If the endpoints are empty, check the application Pod and Service configuration:
kubectl get podskubectl describe service demo-appAlso verify that the Ingress is using the correct Service name and port:
kubectl describe ingress demo-appIf HTTPS is not working, check the certificate status:
kubectl get certificateIf the certificate is not ready, inspect the certificate and its events:
kubectl describe certificate demo-app-tlsYou can also check the certificate requests:
kubectl get certificaterequestFor DNS-related issues, verify that your domain resolves to the external IP address of the Traefik Service:
dig +short app.example.comThe returned IP address should match the external IP of the Traefik Service.
Once you have finished testing the configuration, remove the resources created during the tutorial.
Delete the Ingress:
kubectl delete ingress demo-appDelete the Traefik middleware:
kubectl delete middleware redirect-to-httpsDelete the application Service and Deployment:
kubectl delete service demo-appkubectl delete deployment demo-appDelete the ClusterIssuer:
kubectl delete clusterissuer letsencrypt-prodFinally, uninstall Traefik and remove its namespace:
helm uninstall traefik -n traefikkubectl delete namespace traefikIf you installed cert-manager specifically for this tutorial and do not need it for other workloads, remove it with:
kubectl delete -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yamlThis removes the resources created during the tutorial and returns the cluster to its previous state.
When this approach breaks down
Kubernetes Ingress works well for routing HTTP and HTTPS traffic, but it may not be suitable for every application or networking requirement.
-
You need more advanced traffic management. If you need more expressive routing, traffic policies, or advanced networking features, consider using the Gateway API.
-
You use multiple Ingress Controllers. Running multiple controllers can make routing more complex. Configure
IngressClassandingressClassNamecarefully so each Ingress resource is handled by the intended controller. -
You need to expose non-HTTP traffic. Ingress is designed primarily for HTTP and HTTPS traffic. Applications that use other protocols may require a different exposure method.
-
You need capabilities beyond the Ingress API. The Kubernetes Ingress API is stable but frozen. For new networking capabilities, Kubernetes recommends the Gateway API.
Ingress remains useful for straightforward HTTP and HTTPS routing, but these requirements may call for a different networking approach or additional Kubernetes resources.

