DEV Community

Cover image for Installing Traefik Ingress Controller with cert-manager on Kubernetes
Sanskriti Harmukh for Vultr

Posted on with Aashish Chaurasiya • Originally published at docs.vultr.com

Installing Traefik Ingress Controller with cert-manager on Kubernetes

Traefik is an open-source Kubernetes Ingress controller that pairs with cert-manager to automatically provision and renew TLS certificates for services exposed from your cluster. This guide installs Traefik and cert-manager, then deploys a sample app behind an Ingress with a Let's Encrypt certificate.

Prerequisites: a Kubernetes cluster, kubectl and Helm configured against it, a domain (this guide uses example.com).


Install Traefik

$ kubectl create namespace traefik-namespace
$ helm repo add traefik https://helm.traefik.io/traefik
$ helm repo update
$ helm install --namespace=traefik-namespace traefik traefik/traefik
$ kubectl get services -n traefik-namespace
Enter fullscreen mode Exit fullscreen mode

The traefik Service's EXTERNAL-IP may show <pending> briefly while your cloud provider provisions the LoadBalancer — wait and re-check.

Point a domain A record at that external IP with your DNS provider.


Install cert-manager

$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml
$ kubectl get pods --namespace cert-manager
Enter fullscreen mode Exit fullscreen mode

Check the cert-manager releases page for the current version.


Create a ClusterIssuer

$ nano cluster-issuer.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: hello@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            class: traefik
Enter fullscreen mode Exit fullscreen mode

Use a real email address — Let's Encrypt rejects example.com addresses.

$ kubectl apply -f cluster-issuer.yaml
$ kubectl get clusterissuer
Enter fullscreen mode Exit fullscreen mode

READY: True confirms it's ready to issue certs.


Deploy a Sample App

$ kubectl create namespace example-app-namespace
$ nano example-app-deployment.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: example-app-namespace
  name: example-app-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: example-app
  template:
    metadata:
      labels:
        app: example-app
    spec:
      containers:
        - name: web-app
          image: nginx
          ports:
            - containerPort: 80
Enter fullscreen mode Exit fullscreen mode
$ kubectl apply -f example-app-deployment.yaml
$ kubectl get pods -n example-app-namespace
Enter fullscreen mode Exit fullscreen mode

Create an Ingress

1. Service:

$ nano example-app-service.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: v1
kind: Service
metadata:
  namespace: example-app-namespace
  name: example-app-service
spec:
  selector:
    app: example-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
Enter fullscreen mode Exit fullscreen mode
$ kubectl apply -f example-app-service.yaml
$ kubectl get services -n example-app-namespace
Enter fullscreen mode Exit fullscreen mode

2. Ingress — replace example.com:

$ nano example-app-ingress.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-ingress
  namespace: example-app-namespace
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: example-app-service
                port:
                  number: 80
  tls:
    - secretName: web-app-cert
      hosts:
        - example.com
Enter fullscreen mode Exit fullscreen mode

The Traefik-specific annotations route HTTPS traffic through the websecure entrypoint; the cert-manager.io/cluster-issuer annotation ties the Ingress to the ClusterIssuer. secretName doesn't need to exist beforehand — cert-manager creates it.

$ kubectl apply -f example-app-ingress.yaml
$ kubectl get ingress -n example-app-namespace
Enter fullscreen mode Exit fullscreen mode

ADDRESS populates a few seconds later with Traefik's external IP.

3. Confirm the certificate:

$ kubectl get certificates -n example-app-namespace
Enter fullscreen mode Exit fullscreen mode

READY: True means it's issued. Check auto-renewal is scheduled:

$ kubectl describe -n example-app-namespace certificate web-app-cert
Enter fullscreen mode Exit fullscreen mode

Look for a Renewal Time a month or so out.

4. Visit https://example.com — you should see the default Nginx welcome page over a valid cert.


Troubleshooting

Certificate stuck False — confirm DNS points at Traefik's LoadBalancer IP, then check logs:

$ kubectl logs deployment/cert-manager -n cert-manager --tail=15 -f
Enter fullscreen mode Exit fullscreen mode

Traefik routing issues — check its logs and resource status:

$ kubectl logs -n <namespace> <traefik-pod>
$ kubectl get pods -n traefik-namespace
Enter fullscreen mode Exit fullscreen mode

Ingress not routing — verify the manifest has the right Traefik/cert-manager annotations:

$ kubectl get ingress
Enter fullscreen mode Exit fullscreen mode

Next Steps

Traefik is routing traffic with automatic Let's Encrypt certificates via cert-manager. From here:

  • Add more Ingress resources for additional apps, reusing the same ClusterIssuer
  • Explore Traefik middlewares for rate limiting, auth, or redirects
  • Switch to DNS-01 challenges in the ClusterIssuer if you need wildcard certificates

For the full guide, visit the original article on Vultr Docs.

Top comments (0)