Skip to content

Troubleshooting

Use this flow to find the right section:

flowchart TD
    symptom{"What is the symptom?"}
    symptom -->|"Ingress not exposed"| events["Check Ingress events"]
    events --> ingressSection["Go to: Ingress is not exposed"]
    symptom -->|"DNS record missing"| dns["Check controller logs and DNS records"]
    dns --> dnsSection["Go to: DNS record is missing"]
    symptom -->|"Tunnel returns 502"| connectorLogs["Check cloudflared logs"]
    connectorLogs --> tunnelSection["Go to: Tunnel connects but returns 502"]
    symptom -->|"Credentials stay stale"| credentials["Check Secret update and controller restart"]
    credentials --> credentialsSection["Go to: Credentials rotated but the controller still uses the old token"]

Start by checking that the controller and connector pods are running:

Terminal window
kubectl get pods -n cloudflare-tunnel-ingress-controller

If you used a different Helm release name or namespace, replace them in the commands below.

Check that the Ingress uses the cloudflare-tunnel class, then inspect its events:

Terminal window
kubectl get ingress <name> -n <namespace> -o yaml
kubectl describe ingress <name> -n <namespace>

The controller reports these warnings:

ReasonEvent messageWhat to check
RuleSkippedrule for host <host> has no http section, skippedAdd an http section to the rule. Only this rule is skipped.
TLSIgnoredingress has tls specified, SSL Passthrough is not supported, it will be ignoredRemove the tls section. Cloudflare terminates TLS at the edge.
TransformFailed<transformation error>Fix the error in the event message. All routes from this Ingress are skipped until transformation succeeds.
flowchart TD
    change["Ingress change"] --> classCheck{"Uses cloudflare-tunnel class?"}
    classCheck -->|No| ignored["Controller ignores Ingress"]
    classCheck -->|Yes| tls{"TLS configured?"}
    tls -->|Yes| tlsIgnored["TLSIgnored: ignore TLS settings"]
    tls -->|No| transform["Transform rules"]
    tlsIgnored --> transform
    transform --> result{"Transform result"}
    result -->|"Rule has no http section"| ruleSkipped["RuleSkipped: skip that rule"]
    ruleSkipped --> transform
    result -->|Error| transformFailed["TransformFailed: skip all routes from this Ingress"]
    result -->|Success| reconcile["Reconcile tunnel routes and managed DNS"]

Common TransformFailed messages:

  • fetch service <namespace>/<service>: ... when the backend Service does not exist.
  • service <namespace>/<service> has None for cluster ip, headless service is not supported.
  • service <namespace>/<service> has no port named <port>.
  • path type in ingress <namespace>/<name> is <pathType>, which is not supported.

Check controller logs for reconciliation errors:

Terminal window
kubectl logs deployment/cloudflare-tunnel-ingress-controller \
-n cloudflare-tunnel-ingress-controller

Check the public DNS response, then check controller logs for Cloudflare API, zone, and DNS reconciliation errors:

Terminal window
dig <hostname>
kubectl logs deployment/cloudflare-tunnel-ingress-controller \
-n cloudflare-tunnel-ingress-controller

Verify that:

  • The API token can edit Cloudflare Tunnel and DNS resources and can read the zone.
  • The hostname belongs to a zone in the configured Cloudflare account.
  • The Ingress does not set disable-dns-management: "true".

A connected tunnel with a 502 response usually cannot reach the backend Service. Check the Service, port, and ready endpoints:

Terminal window
kubectl get service <service> -n <namespace>
kubectl get endpointslice -n <namespace> \
-l kubernetes.io/service-name=<service>

Then inspect connector logs for the origin connection error:

Terminal window
kubectl logs deployment/controlled-cloudflared-connector \
-n cloudflare-tunnel-ingress-controller

If the backend expects HTTPS or a specific host name, verify the relevant Ingress annotations.

Credentials rotated but the controller still uses the old token

Section titled “Credentials rotated but the controller still uses the old token”

The controller reads credentials once at startup. Updating the Secret does not refresh a running controller. Restart the controller after rotating credentials:

Terminal window
kubectl rollout restart deployment cloudflare-tunnel-ingress-controller \
-n cloudflare-tunnel-ingress-controller

See Cloudflare credentials for the full credential setup and rotation caveat.

Missing something in the docs? Open an issue