Camunda Cluster Healthy Status

Hello,

I deployed camunda 8.9 using helm chart using best practices from camunda itself. Management and Orchestration components are separated in 2 namespaces but I have an issue actually that my cluster is always unhealthy because operate and tasklist are unhealthy. But identity, modeler, console and optimize are healthy.
In my helm values I added a section for security by the orchestration cluster, so that all components, that want to communicate with him should be authenticated. My assumption is that since console does not belong to known audiences (just see zeebe configurattion snipset below) for orchestration cluster, the token it obtains from keycloak is rejected by zeebe.

  security:
    authentication:
      oidc:
        username-claim: "preferred_username"
        client-id-claim: "client_id"
        client-id: "orchestration"
        client-secret: ${VALUES_ORCHESTRATION_CLIENT_SECRET:}
        audiences:
          - "orchestration"
          - "orchestration-api"
          - "web-modeler-api"

Can someone hel me solve this ‘unhealthy cluster error’ ?

This looks like an audience mismatch issue where Operate and Tasklist can’t authenticate with Zeebe due to incorrect OIDC configuration. I found the following relevant resources:

Does this help? If not, can anyone from the community jump in? :waving_hand:


:light_bulb: Hints: Use the Ask AI feature in Camunda’s documentation to chat with AI and get fast help. Report bugs and features in Camuda’s GitHub issue tracker. Trust the process. :robot:

Hi @Maurice_Tchangue ,

The key thing first: your assumption doesn’t match the 8.9 architecture

In Camunda 8.9, Operate and Tasklist are no longer standalone services that authenticate to Zeebe with an OIDC token. They are part of the single consolidated Orchestration Cluster application (StandaloneCamunda) together with the Zeebe broker/gateway and Admin. They run in the same JVM/pod and share the same configuration — there is no machine-to-machine token exchange between Operate/Tasklist and Zeebe that could be “rejected by Zeebe.”

That older model (separate Operate/Tasklist doing M2M to a zeebe/zeebe-api audience) was 8.7 and earlier. So the theory “Console isn’t in the audiences → its token is rejected by Zeebe → Operate/Tasklist unhealthy” does not hold in 8.9:

  • Console is a Management component and has its own audience (e.g. console-api). It does not need to be in the Orchestration Cluster’s audiences list, and adding it there would not change Operate/Tasklist health.
  • Health/readiness actuator endpoints are not behind OIDC. The 9600/actuator/health/* endpoints the platform (and Console) use to determine health are unauthenticated by default, so an audience mismatch cannot make them report “unhealthy.”

Your audiences block controls validation of tokens presented to the Orchestration Cluster REST API / UI — nothing more.

What actually determines Operate/Tasklist health in 8.9

Because they’re part of one app, “Operate/Tasklist unhealthy” is reported through the Orchestration Cluster’s Spring Boot Actuator on port 9600. The health indicators that matter are:

"searchEngineCheck": { "status": "UP" },
"indicesCheck":      { "status": "UP" },
"brokerReady":       { "status": "UP" }
  • readinessProbe/actuator/health/readinesstied to secondary storage (Elasticsearch/OpenSearch) and index availability.
  • livenessProbe/actuator/health/liveness — in 8.9 this was deliberately decoupled from Elasticsearch (the default livenessProbe.probePath changed from /actuator/health/readiness to /actuator/health/liveness) so ES blips don’t restart the pod.

So in an overwhelming majority of cases, Operate/Tasklist show “unhealthy/unready” because the Orchestration Cluster cannot reach or initialize its Elasticsearch/OpenSearch indices, not because of OIDC.

Two most likely real root causes (in order)

1. Secondary storage (Elasticsearch/OpenSearch) is unreachable from the orchestration namespace

Operate/Tasklist readiness fails (searchEngineCheck/indicesCheck = DOWN → HTTP 503) when the search engine URL, credentials, TLS, or cross-namespace network policy is wrong. Since you split into two namespaces, this is the prime suspect.

Diagnose — hit the health endpoint directly inside the orchestration pod:

kubectl -n orchestration exec -it <orchestration-pod> -- \
  curl -s localhost:9600/actuator/health | jq

Look for which sub-indicator is DOWN. If it’s searchEngineCheck/indicesCheck, it’s storage, not auth. Then check the logs:

kubectl -n orchestration logs <orchestration-pod> | grep -iE "elasticsearch|opensearch|readiness|index|connection"

2. Console’s monitoring is pointed at wrong/unreachable readiness URLs

If health is being read by Console’s Cluster Status Dashboard in the management namespace, it reaches the orchestration namespace via explicit readiness URLs. In 8.9 the correct URLs (note the /operate and /tasklist context paths on port 9600) are:

- name: Operate
  id: operate
  readiness: http://camunda-zeebe.orchestration:9600/operate/actuator/health/readiness
  metrics:   http://camunda-zeebe.orchestration:9600/operate/actuator/prometheus
- name: Tasklist
  id: tasklist
  readiness: http://camunda-zeebe.orchestration:9600/tasklist/actuator/health/readiness
  metrics:   http://camunda-zeebe.orchestration:9600/tasklist/actuator/prometheus
- name: Orchestration Cluster
  id: orchestration
  readiness: http://camunda-zeebe.orchestration:9600/actuator/health/readiness

If those URLs are wrong, or a NetworkPolicy blocks management → orchestration traffic on 9600, Console will mark Operate/Tasklist unhealthy while everything in the management namespace (Identity, Modeler, Console) plus Optimize shows healthy — which is exactly your symptom pattern. Verify cross-namespace reachability:

kubectl -n management exec -it <console-pod> -- \
  curl -s http://camunda-zeebe.orchestration:9600/operate/actuator/health/readiness

About your OIDC audiences block

Your snippet is a reasonable Orchestration Cluster OIDC config, but a few notes:

security:
  authentication:
    oidc:
      username-claim: "preferred_username"
      client-id-claim: "client_id"
      client-id: "orchestration"
      client-secret: ${VALUES_ORCHESTRATION_CLIENT_SECRET:}
      audiences:
        - "orchestration"
        - "orchestration-api"
        - "web-modeler-api"   # keep this so Web Modeler can deploy to the cluster
  • The audiences list validates the aud claim of incoming tokens to the Orchestration Cluster REST API/UI. Include the values your real callers put in aud (typically the orchestration client id / orchestration-api, and web-modeler-api for Web Modeler deployments). Console does not belong here.
  • If (and only if) you later see genuine auth errors, the doc symptom is "Invalid token" / "Audience mismatch" in logs — not a health-probe failure. To confirm, decode a token and inspect aud:
    curl -X POST '<token-endpoint>' \
      -d 'client_id=<id>' -d 'client_secret=<secret>' -d 'grant_type=client_credentials' \
      | jq -r '.access_token' | cut -d'.' -f2 | base64 -d | jq '.aud'
    
    Then make sure that value is in audiences. Keycloak often needs an audience token-mapper to actually emit the expected aud.
  • Note the default client-id-claim is client_id; for Keycloak M2M tokens the client identifier frequently arrives in azp. If you do M2M calls and they’re rejected, you may need client-id-claim: azp.

Recommended action plan

  1. Curl localhost:9600/actuator/health inside the orchestration pod and identify the failing sub-indicator. This immediately tells you storage-vs-something-else.
  2. If searchEngineCheck/indicesCheck is DOWN → fix Elasticsearch/OpenSearch connectivity (URL, credentials, TLS, cross-namespace NetworkPolicy). This is the most probable cause.
  3. If the pod’s own health is UP but Console shows red → fix Console’s readiness URLs / cross-namespace networking (use the /operate and /tasklist context-path URLs above).
  4. Only if logs actually show “Invalid token / Audience mismatch” → adjust audiences (and possibly client-id-claim) to match the real aud/claims in your Keycloak tokens. Do not add Console to the orchestration audiences.

Bottom line: In 8.9 Operate/Tasklist are internal to the single Orchestration Cluster app, so their “unhealthy” status is driven by the cluster’s readiness (almost always Elasticsearch/OpenSearch reachability) or by Console’s health-URL/network configuration across your two namespaces — not by an OIDC audience list that excludes Console.