elasticsearch-cluster-health — Claude Code skill
Diagnoses yellow or red Elasticsearch clusters in read-only mode. Identifies the root cause behind unassigned shards and suggests exact remediation steps.
npx skills add elastic/agent-skills --skill elasticsearch-cluster-health -a claude-codeWhat elasticsearch-cluster-health does
This skill runs systematic triage on degraded Elasticsearch clusters. It checks overall cluster status with GET /_cluster/health. If health turns yellow or red, it isolates the problem down to a single index, prioritizing system indices such as .security or .fleet-*. The tool operates entirely in read-only mode and never mutates cluster state.
Root cause analysis relies on POST /_cluster/allocation/explain. The skill distinguishes the initial trigger like NODE_LEFT from the actual blocking decider in node_allocation_decisions[].deciders[]. That distinction clarifies whether allocation stalled because of disk watermarks (disk_threshold), allocation filters, or a missing shard copy (no_valid_shard_copy).
Running this against Elastic Cloud Serverless will fail because shard and allocation APIs are inaccessible there. The skill targets self-managed or Elastic Cloud Hosted clusters on Elasticsearch 8.x or 9.x. All diagnostic operations also require the elastic CLI version 0.2 or higher configured with stack es support.
What it helps with
- diagnose yellow and red Elasticsearch cluster states
- localize unassigned shards to a single driving index
- separate reassignment triggers from blocking allocation deciders
- detect data loss risks when primary shards fail to assign
Example prompts for the agent
- The Elasticsearch cluster turned yellow, check what is blocking allocation.
- Investigate why primary shards remain unassigned after a node failure.
How to install elasticsearch-cluster-health
1Skills CLI (Vercel)
npx skills add elastic/agent-skills --skill elasticsearch-cluster-health -a claude-code2Manually: put the files in place
~/.claude/skills/elasticsearch-cluster-health/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/elasticsearch-cluster-health/
1Skills CLI (Vercel)
npx skills add elastic/agent-skills --skill elasticsearch-cluster-health -a codex2Manually: put the files in place
.agents/skills/elasticsearch-cluster-health/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add elastic/agent-skills --skill elasticsearch-cluster-health -a cursor2Manually: put the files in place
.cursor/skills/elasticsearch-cluster-health/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install elastic/agent-skills elasticsearch-cluster-health2Manually: put the files in place
.github/skills/elasticsearch-cluster-health/SKILL.md1Skills CLI (Vercel)
npx skills add elastic/agent-skills --skill elasticsearch-cluster-health -a gemini-cli2Platform CLI
gemini skills install https://github.com/elastic/agent-skills --path skills/elasticsearch/elasticsearch-cluster-healthManually: put the files in place
.devin/skills/elasticsearch-cluster-health/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add elastic/agent-skills --skill elasticsearch-cluster-health -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about elasticsearch-cluster-health
How to install elasticsearch-cluster-health in Claude Code?
Run npx skills add elastic/agent-skills --skill elasticsearch-cluster-health -a claude-code.
What does elasticsearch-cluster-health do?
It diagnoses yellow or red Elasticsearch clusters by finding unassigned shards and parsing allocation deciders in read-only mode, surfacing remediation steps without altering cluster settings.
Can elasticsearch-cluster-health run on Elastic Cloud Serverless?
No. Elastic Cloud Serverless manages shard and cluster APIs internally, making them unavailable. The skill requires self-managed or Elastic Cloud Hosted Elasticsearch 8.x or 9.x along with elastic CLI 0.2 or higher.
Source text
SKILL.md · Apache-2.0
---
name: elasticsearch-cluster-health
description: >
Diagnose a non-green Elasticsearch cluster and surface the single most likely cause
with remediation. Use when an operator reports yellow or red status, unassigned
shards, allocation failures, or wants read-only triage before deeper investigation.
Teaches replica-vs-primary impact, allocation decider classification, and data-loss
awareness.
metadata:
author: elastic
version: 0.1.0
universal: true
compatibility: Elasticsearch 8.x or 9.x, self-managed or Elastic Cloud Hosted; not
applicable to Elastic Cloud Serverless, where cluster, shard, and allocation APIs
are managed internally. Requires the `elastic` CLI ≥ 0.2 with `stack es` support.
---
# Diagnose Cluster Health
Triage a non-green Elasticsearch cluster read-only: localize the problem, classify the allocation decider, and report
the single most likely cause with remediation. Never mutate cluster state — surface findings and let the operator act.
<!-- begin-partial: preamble -->
## Environment Configuration
This skill executes Elasticsearch operations through the `elastic` CLI. If the
[`elastic` CLI](https://github.com/elastic/cli#configuration) is not installed, tell the user what it is needed for. Do
not guess credentials, call the HTTP API directly, or attempt other workarounds.
This skill references operations in HTTP-shorthand form (e.g., `GET /`, `GET /_cat/indices`, `GET /{index}/_mapping`,
`GET /{index}/_settings/index.mode`, `POST /_query`). The [Operations](#operations) table at the end of this document
maps each shorthand to the equivalent `elastic` CLI command — always use the CLI rather than calling the HTTP API
directly.
<!-- end-partial: preamble -->
## Process
1. **Read the overall status.** Call `GET /_cluster/health`. The `status` field is the verdict:
- `green` — every primary and replica is assigned. Report healthy and stop.
- `yellow` — every **primary** is assigned but at least one **replica** is not. Data remains readable; redundancy is
degraded. This is **not** data loss.
- `red` — at least one **primary** is unassigned. Data for that shard is **unavailable**; treat as urgent.
Also read `unassigned_shards`, `initializing_shards`, and `relocating_shards`. The decision: continue only when
status is yellow or red. If `initializing_shards > 0` and `unassigned_shards == 0`, the cluster is recovering on its
own — call `GET /_cat/recovery` to confirm progress, wait, and re-check `GET /_cluster/health` before escalating.
Data needed: cluster-wide `status` and shard counters.
2. **Localize the problem to one index.** Call `GET /_cluster/health?level=indices` and pick the index that drives the
cluster-wide status:
- Any **red** index outranks every yellow index.
- Among reds or yellows, prefer the index with the most `unassigned_shards`.
- A red **system** index (`.security`, `.kibana*`, `.fleet-*`) outranks application indices because the rest of the
stack depends on it.
Optionally call `GET /_cat/shards/{index}?h=index,shard,prirep,state,unassigned.reason` to list every unassigned
shard on that index and see whether failures are primaries (`prirep=p`) or replicas (`prirep=r`).
The decision: focus the next steps on exactly **one** index — the one whose recovery unblocks the cluster.
Data needed: per-index `status` and `unassigned_shards`; shard role (primary vs replica) when available.
3. **Separate trigger from root cause.** Call `POST /_cluster/allocation/explain` with no body so Elasticsearch selects
an unassigned shard, or target the worst shard explicitly:
```json
{ "index": "<index>", "shard": <id>, "primary": <true|false> }
```
Read these fields in order:
- `primary` — `false` means a **replica** is unassigned (typical yellow); `true` means a **primary** is unassigned
(typical red).
- `can_allocate` — top-level allocation verdict (`no`, `yes`, `throttled`, `no_valid_shard_copy`, …).
- `unassigned_info.reason` — what **triggered** reassignment (e.g. `NODE_LEFT`, `INDEX_CREATED`). This is **not** the
root cause when `can_allocate` is `no`; it only explains why the shard became unassigned.
- `allocate_explanation` — human-readable summary; quote it verbatim in the report.
- `node_allocation_decisions[].deciders[]` — per-node decider results. Find deciders with `decision: "NO"`; the
**decider name** (e.g. `disk_threshold`, `filter`, `awareness`) is the root cause class.
The decision:
- **Yellow + `primary: false`** — impact is limited to replica redundancy; no data loss. Continue to step 4 to name
the blocking decider (do **not** stop at `NODE_LEFT`).
- **Red + `primary: true`** — data for that shard is missing. Continue to step 4; if `can_allocate` is
`no_valid_shard_copy`, treat as potential **data loss** immediately.
Data needed: allocation-explain response for one representative unassigned shard on the chosen index.
4. **Classify the decider.** Map the blocking signal to a cause class. Prefer the decider with `decision: "NO"` over the
`unassigned_info.reason` trigger.
| Signal | Cause class | Typical remediation (operator applies) |
| ---------------------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `decider: disk_threshold`, `decision: NO` | Disk high/low watermark exceeded | Free disk on the named node, add data-node capacity, or adjust `cluster.routing.allocation.disk.watermark.*` after confirming usage via `GET /_cat/allocation` |
| `decider: filter` or `decider: awareness`, `decision: NO` | Allocation filtering or zone awareness | Add a node that satisfies `index.routing.allocation.*` / awareness attributes, or adjust index/cluster allocation settings |
| `decider: throttling` or recovery in progress | Transient recovery | Wait; monitor `GET /_cat/recovery` and re-check `GET /_cluster/health` |
| `can_allocate: no_valid_shard_copy` (often with empty `node_allocation_decisions`) | No surviving shard copy | See step 5 — **data loss** scenario |
| `can_allocate: yes` but shard still unassigned | Delayed allocation or cluster state catch-up | Check `unassigned_info.at` delay; wait and re-check |
For disk pressure (common yellow scenario after `NODE_LEFT`): replicas relocate to remaining nodes; if a survivor is
above the **high watermark** (`cluster.routing.allocation.disk.watermark.high`, default 90%), the `disk_threshold`
decider blocks replica allocation even though primaries stay assigned. The fix is disk capacity or watermark relief —
**not** deleting the index or forcing an empty primary.
Data needed: decider name, `explanation` text, and affected node names from `node_allocation_decisions`.
5. **Recommend remediation — read-only triage ends here.** Report the **single most likely cause** (decider class +
verbatim `allocate_explanation`) and one primary remediation path. Match urgency to color and shard role.
**Yellow / replica unassigned (no data loss):**
- State clearly: all primaries are assigned; only replicas are missing; **no data loss**.
- Name the real decider (e.g. disk high watermark on `es-node-2`), not merely “a node left”.
- Recommend: free disk space, expand storage, add data nodes, or adjust disk watermarks after reviewing
`GET /_cat/allocation`.
- Do **not** recommend: deleting the index, `allocate_empty_primary`, force-allocating over a healthy primary, or
restarting the entire cluster without evidence.
**Red / primary unassigned with `no_valid_shard_copy` (data loss risk):**
- State clearly: a **primary** shard is unassigned; queries/routing for that shard fail; treat as **urgent** and
localized to the named index.
- Explain: the only copy was on the departed node; Elasticsearch cannot allocate a primary because no valid copy
exists on any remaining node (`can_allocate: no_valid_shard_copy`).
- Recovery paths in order:
1. **Bring the departed node back** if its data directory is intact — the shard copy returns.
2. **Restore from snapshot** into the index (or a new index followed by reindex) when snapshots exist.
3. **Last resort only:** `POST /_cluster/reroute` with `allocate_empty_primary` — **this creates an empty primary
and permanently loses all documents on that shard**. State data loss explicitly; never present this as the first
or casual fix.
- Do **not** recommend: deleting the index without discussing data loss, or `allocate_empty_primary` without the
data-loss warning.
**Self-healing in progress:**
- When deciders show throttling or active peer recovery, recommend waiting and re-checking read-only APIs above.
Do not execute reroutes, snapshot restores, or settings changes — surface cause and remediation only.
## Guidelines
- **Read-only:** Use only GET/POST explain APIs for triage. Remediation is advice; the operator performs writes.
- **Trigger ≠ cause:** `unassigned_info.reason: NODE_LEFT` explains the event; `node_allocation_decisions` deciders
explain why allocation still fails.
- **Replica vs primary:** Yellow + `primary: false` = redundancy gap, not data loss. Red + `primary: true` = missing
data for that shard.
- **One index, one cause:** Pick the highest-impact index and the strongest NO decider; avoid listing every shard.
- **Cat helpers:** Use `GET /_cat/allocation` for disk percentages per node and `GET /_cat/recovery` for ongoing
recoveries when the decider class is unclear or recovery is in progress.
## Examples
**Yellow — disk watermark after node departure.** Health shows yellow with unassigned replicas on `logs-2025-07`.
Allocation explain returns `primary: false`, `unassigned_info.reason: NODE_LEFT`, but `disk_threshold` decider NO on
`es-node-2` (“above the high watermark … 90%”). Report: no data loss; root cause is disk pressure on the receiving node;
remediate disk/watermark — not “node left” alone.
**Red — primary with no valid copy.** Health shows red on `orders-2025` with one unassigned shard. Explain returns
`primary: true`, `can_allocate: no_valid_shard_copy`, `last_allocation_status: no_valid_shard_copy`. Report: urgent;
primary data missing; restore node or snapshot; mention `allocate_empty_primary` only as last resort with explicit data
loss.
## Operations
| HTTP API (shorthand) | `elastic` CLI command |
| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `GET /_cluster/health` | `elastic es cluster health` |
| `GET /_cluster/health?level=indices` | `elastic es cluster health --level indices` |
| `POST /_cluster/allocation/explain` | `elastic es cluster allocation-explain` |
| `POST /_cluster/allocation/explain` (specific shard) | `elastic es cluster allocation-explain --index '<index>' --shard <id> --primary true` (replica: `false`) |
| `GET /_cat/allocation` | `elastic es cat allocation` |
| `GET /_cat/recovery` | `elastic es cat recovery` |
| `GET /_cat/shards/{index}?h=index,shard,prirep,state,unassigned.reason` | `elastic es cat shards --index '<index>' --h index,shard,prirep,state,unassigned.reason` |
| `POST /_cluster/reroute` (last-resort empty primary — operator only) | `elastic es cluster reroute --commands '<json>'` |
© elastic · Apache-2.0 · elastic/agent-skills