All articles
Tutorials

How to Expose an Application with Kubernetes Ingress

Learn how Kubernetes Ingress helps you expose applications to the internet, route traffic with Traefik, and secure your application with HTTPS and TLS.

How to Expose an Application with Kubernetes Ingress cover
13 min read

TL;DR

  • Deploy a sample application to Kubernetes and expose it with a ClusterIP Service.
  • 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.
  • kubectl installed 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

Kubernetes Ingress request flow from the client through Traefik, Ingress, Service, and 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=nginx

Check the Pod:

kubectl get pods

You 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-app

Verify the Service:

kubectl get service demo-app

You 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/charts

You should see:

"traefik" has been added to your repositories

Update the Helm repositories to retrieve the latest chart information:

helm repo update

You 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-namespace

Check the Traefik deployment:

kubectl get pods -n traefik

You 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 traefik

You 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 traefik

Note 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: 80

Replace 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.yaml

Check the Ingress:

kubectl get ingress demo-app

You 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.com

The 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.com

You 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.yaml

Check that the cert-manager Pods are running:

kubectl get pods -n cert-manager

You 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: traefik

Replace 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.yaml

Verify that the ClusterIssuer is ready:

kubectl get clusterissuer

You 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: 80

Replace 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.yaml

Check the Certificate resource:

kubectl get certificate

You 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-tls

Once the certificate is ready, verify that the TLS Secret exists:

kubectl get secret demo-app-tls

You 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.com

You 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: true

Save the configuration as redirect-to-https.yaml and apply it:

kubectl apply -f redirect-to-https.yaml

Verify that the middleware was created:

kubectl get middleware redirect-to-https

You 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: 80

Replace app.example.com with the domain you configured for your application.

Apply the updated Ingress:

kubectl apply -f demo-app-ingress.yaml

Verify the Ingress:

kubectl get ingress demo-app

Finally, send an HTTP request to your domain and check that it redirects to HTTPS:

curl -I http://app.example.com

You 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.com

The 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-app

If the ADDRESS field is empty, check the Traefik Service:

kubectl get service -n traefik

Make sure the Traefik Service has an external IP address. If it does not, check the Traefik Pod and its events:

kubectl get pods -n traefik
kubectl describe service traefik -n traefik

If the Ingress has an address but requests are still failing, verify that the application Service has endpoints:

kubectl get endpoints demo-app

You 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 pods
kubectl describe service demo-app

Also verify that the Ingress is using the correct Service name and port:

kubectl describe ingress demo-app

If HTTPS is not working, check the certificate status:

kubectl get certificate

If the certificate is not ready, inspect the certificate and its events:

kubectl describe certificate demo-app-tls

You can also check the certificate requests:

kubectl get certificaterequest

For DNS-related issues, verify that your domain resolves to the external IP address of the Traefik Service:

dig +short app.example.com

The 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-app

Delete the Traefik middleware:

kubectl delete middleware redirect-to-https

Delete the application Service and Deployment:

kubectl delete service demo-app
kubectl delete deployment demo-app

Delete the ClusterIssuer:

kubectl delete clusterissuer letsencrypt-prod

Finally, uninstall Traefik and remove its namespace:

helm uninstall traefik -n traefik
kubectl delete namespace traefik

If 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.yaml

This 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 IngressClass and ingressClassName carefully 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.

Frequently asked questions

Share𝕏

Writer

  • Abdul Talha

    Technical Writer and Documentation Engineer passionate about cloud infrastructure, DevOps, open-source software, and developer experience.

Need help with your technical content?

We help B2B SaaS teams turn complex products into clear documentation and content that developers actually use.

Book a call
How to Expose an Application with Kubernetes Ingress | Reclear