An application can respond inside a Kubernetes cluster while requests to its public hostname fail at the routing layer. Troubleshooting Ingress separates an unhandled class, an unmatched URL path, and a missing TLS certificate so the request reaches the intended backend again.

The Ingress object describes a route, but its controller decides whether to serve it. With Traefik, an incorrect IngressClass or an unmatched path can produce the same 404 page not found response, so the status alone does not identify the cause. A backend error with no ready Service endpoints belongs to Service troubleshooting.

Direct requests to the controller address bypass DNS while preserving the hostname used for routing. HTTPS also needs the hostname during the TLS handshake and a trusted certificate from a Secret in the Ingress namespace. Certificate issuance and controller installation are prerequisites rather than repairs to a route.

Steps to troubleshoot Kubernetes Ingress routing:

  1. Set INGRESS_ADDRESS to the reachable address of your Traefik controller.
    $ INGRESS_ADDRESS=172.21.0.2

    The address is specific to your cluster. The route below is named web in namespace default and serves app.example.com through Service web:80. A ready controller with the Kubernetes Ingress provider is required; Traefik's Helm installation instructions cover that prerequisite. An unhandled Ingress may have a blank Address even though the controller has a reachable address.

  2. Reproduce the failing HTTP request with the route's Host header.
    $ curl -sS -i -H 'Host: app.example.com' "http://$INGRESS_ADDRESS/"
    HTTP/1.1 404 Not Found
    Content-Type: text/plain; charset=utf-8
    ##### snipped #####
    404 page not found

    A connection timeout or refusal occurs before this HTTP response and calls for a controller or network check. The class and path repairs below apply when the controller answers but the route does not match.

  3. Inspect the Ingress class and host rule for web.
    $ kubectl describe ingress web --namespace default
    Name:             web
    Labels:           <none>
    Namespace:        default
    Address:
    Ingress Class:    unhandled
    Default backend:  <default>
    Rules:
      Host             Path  Backends
      ----             ----  --------
      app.example.com
                       /   web:80 (10.42.0.7:80)
    Annotations:       <none>
    Events:            <none>

    The host must match the request's Host header. The backend has an address here, but the class is unhandled. A blank backend address or an unavailable Service is a handoff to How to troubleshoot a Kubernetes Service, rather than a reason to change the class or path.

  4. Compare the route's class with the installed IngressClass names.
    $ kubectl get ingressclass
    NAME                CONTROLLER                      PARAMETERS   AGE
    traefik (default)   traefik.io/ingress-controller   <none>       60s

    spec.ingressClassName references the class name traefik, not the controller identifier traefik.io/ingress-controller. The unhandled class from the failed route is absent.

  5. Save the current Ingress before changing its route.
    $ kubectl get ingress web --namespace default -o yaml > web-ingress.before.yaml

    Changing the class or path can expose an application or interrupt existing clients. HTTP provides neither encryption nor authentication. Only the correction matching the observed fault belongs on the live route. For a directly managed route, kubectl apply -f web-ingress.before.yaml restores the saved Ingress settings; that rollback also restores the original fault. For Helm or GitOps ownership, the desired manifest and its rollback belong in that system to avoid reconciliation reverting the repair.

  6. Assign traefik when the route names an unhandled class.
    $ kubectl patch ingress web --namespace default --type=merge --patch '{"spec":{"ingressClassName":"traefik"}}'
    ingress.networking.k8s.io/web patched
  7. Retest the original Host-header request after the class correction.
    $ curl -fsS -H 'Host: app.example.com' "http://$INGRESS_ADDRESS/"
    Hostname: web-85787c857b-8fp74
    ##### snipped #####
    GET / HTTP/1.1
    Host: app.example.com
    ##### snipped #####

    -f makes HTTP errors fail the command. The identifying response comes from the web backend. Controller updates can take a few seconds; a continuing 404 requires another look at the rule.

  8. Reproduce the failing child path when the root URL works.
    $ curl -sS -i -H 'Host: app.example.com' "http://$INGRESS_ADDRESS/child"
    HTTP/1.1 404 Not Found
    Content-Type: text/plain; charset=utf-8
    ##### snipped #####
    404 page not found

    This is a separate fault from the class mismatch. Only the path branch is needed when the class already matches and a child URL fails.

  9. Read the path and pathType for the matching host rule.
    $ kubectl get ingress web --namespace default -o 'jsonpath={.spec.rules[0].http.paths[0]}'
    {"backend":{"service":{"name":"web","port":{"number":80}}},"path":"/","pathType":"Exact"}

    This route has one host and one path, so index 0 selects both. Exact at / ignores child URLs; Prefix at / includes them. A different configured path must also match the requested URL. Prefix matching does not strip or rewrite the path sent to the application.

    A root prefix makes every URL path on this host eligible for this backend. It is appropriate only when that is the intended exposure; a route intended for a smaller URL subtree needs that narrower path.

  10. Change the root path to Prefix if the application should receive child URLs.
    $ kubectl patch ingress web --namespace default --type=json --patch '[{"op":"replace","path":"/spec/rules/0/http/paths/0/pathType","value":"Prefix"}]'
    ingress.networking.k8s.io/web patched
  11. Verify that the previously failing child URL reaches web.
    $ curl -fsS -H 'Host: app.example.com' "http://$INGRESS_ADDRESS/child"
    Hostname: web-85787c857b-8fp74
    ##### snipped #####
    GET /child HTTP/1.1
    Host: app.example.com
    ##### snipped #####
  12. Reproduce certificate verification failure for an existing HTTPS route.
    $ curl -fsS --cacert ca.crt --resolve "app.example.com:443:$INGRESS_ADDRESS" https://app.example.com/child
    curl: (60) SSL: no alternative certificate subject name matches target hostname 'app.example.com'
    ##### snipped #####

    This branch assumes TLS is already enabled on the Traefik route, such as through traefik.ingress.kubernetes.io/router.tls: “true”, with a spec.tls Secret reference. ca.crt is the trusted issuing CA certificate for a private CA; a publicly trusted certificate uses curl's normal trust store without –cacert ca.crt. –resolve preserves both TLS SNI and the HTTP hostname while connecting to the controller address.

    Disabling certificate verification with -k would hide this failure. A hostname error can mean Traefik is serving its fallback certificate; it does not by itself prove which Secret is missing.

  13. Read the TLS Secret reference on the Ingress.
    $ kubectl get ingress web --namespace default -o 'jsonpath={.spec.tls}'
    [{"hosts":["app.example.com"],"secretName":"app-example-com-tls"}]
  14. Check for the referenced Secret in the Ingress namespace.
    $ kubectl get secret app-example-com-tls --namespace default
    Error from server (NotFound): secrets "app-example-com-tls" not found

    A Secret with this name in another namespace cannot satisfy this reference. The missing object explains why the route cannot load its intended certificate.

  15. Locate any Secret with the same name in another namespace.
    $ kubectl get secrets --all-namespaces --field-selector metadata.name=app-example-com-tls
    NAMESPACE    NAME                  TYPE                DATA   AGE
    tls-assets   app-example-com-tls   kubernetes.io/tls   2      6s

    This optional metadata lookup needs permission to list Secrets across namespaces. It shows a misplaced Secret without printing its contents; an empty result means none is visible to the account.

    tls.crt must contain the approved certificate chain issued for app.example.com and tls.key its matching private key. The key must remain private. For a certificate managed by an issuer or GitOps, the namespace and Secret name belong in that owner's configuration instead of an unmanaged copy.

  16. Create the missing TLS Secret in default from the approved certificate and key files.
    $ kubectl create secret tls app-example-com-tls --namespace default --cert=tls.crt --key=tls.key
    secret/app-example-com-tls created

    This creation is appropriate only after NotFound; it does not replace an existing certificate.

  17. Confirm that the same HTTPS URL reaches web with certificate verification enabled.
    $ curl -fsS --cacert ca.crt --resolve "app.example.com:443:$INGRESS_ADDRESS" https://app.example.com/child
    Hostname: web-85787c857b-8fp74
    ##### snipped #####
    GET /child HTTP/1.1
    Host: app.example.com
    ##### snipped #####
    X-Forwarded-Proto: https
    ##### snipped #####

    The request now passes certificate and hostname verification and reaches the backend through Traefik. The HTTP response, URL path, and backend identity together confirm the repaired Ingress route.

Share this guide