Back to blog
Reliability Engineering September 10, 2026

Elasticsearch Cluster Health & Shard Allocation Monitoring: Unassigned Shards, JVM Heap Pressure, and Slow Queries

Automate WhatsApp Alerts
Start Free ➔

In distributed search engines and high-volume log aggregation pipelines, Elasticsearch serves as the indexing and analytical backbone. Elasticsearch partitions indices across primary and replica shards distributed across cluster data nodes. When nodes run out of disk space, experience JVM Old Generation garbage collection freezes, or suffer shard over-allocation, the cluster status shifts from green to yellow or red.

A red cluster status means primary shards are unassigned, causing immediate query failures and data unavailability. A yellow cluster indicates unassigned replicas, leaving indices vulnerable to data loss during hardware failures. This comprehensive engineering guide breaks down Elasticsearch cluster state dynamics, shard allocation decider mechanics, JVM heap pressure diagnostics, query execution latency profiling, and production incident response runbooks.


1. Elasticsearch Cluster Health Monitoring Architecture

Understanding Elasticsearch reliability requires modeling how master nodes coordinate shard allocation across data tiers:

[Elected Master Node] ──(Cluster State Management)──► [Zen / Raft Coordination Quorum]
                                                                │
                                            ┌───────────────────┴───────────────────┐
                                            ▼                                       ▼
                             [Data Node: Hot Tier]                  [Data Node: Warm / Cold Tier]
                             ├── Primary Shard 0 [Open]             ├── Replica Shard 0 [Allocated]
                             ├── Primary Shard 1 [Open]             ├── Replica Shard 1 [Allocated]
                             └── JVM Heap (Lucene Buffer)           └── Storage (NVMe / EBS)

Cluster Health Status States

  • green: All primary and replica shards are successfully allocated across active nodes.
  • yellow: All primary shards are active, but one or more replica shards are unassigned. Read/write operations succeed, but redundancy is degraded.
  • red: At least one primary shard is unassigned. Queries and indexing requests touching affected indices fail immediately with data loss or partial results.

2. Core Cluster Health Metrics & SRE Threshold Matrix

Monitor cluster stability using the following quantitative signals:

Metric SignalHealthy BaselineWarning StateCritical Incident (Page)Primary Remediation
Unassigned Shards(0)(1 - 5) (Yellow)(> 0) Primary (Red)Execute _cluster/allocation/explain
JVM Heap Utilization(< 70%)(70% - 85%)(> 85%) sustainedCheck Old Gen GC & fielddata caches
Old Generation GC Time(< 2%) of CPU time(2% - 8%)(> 10%) (GC Thrashing)Scale data nodes / shrink query sizes
Search Latency (p99)(< 150\text{ ms})(150\text{ ms} - 500\text{ ms})(> 500\text{ ms})Profile slow queries & shard fanout
Thread Pool Rejections (search/write)(0\text{ / min})(1 - 10\text{ / min})(> 10\text{ / min})Cluster CPU & I/O thread saturation
Pending Cluster Tasks(0)(1 - 10) transient(> 10) sustained (> 1\text{ m})Master node overloaded with metadata

Use the Pingzo SLA Calculator to evaluate how cluster degradation and query latency spikes consume your monthly availability error budgets.


3. Detecting & Diagnosing Unassigned Shards

When unassigned shards appear, never guess the cause or blindly force shard routing. Use the native Cluster Allocation Explain API:

curl -s -X POST "https://es-cluster.internal:9200/_cluster/allocation/explain?pretty"   -H 'Content-Type: application/json'   -d '{
    "index": "logs-production-2026.09.10",
    "shard": 0,
    "primary": true
  }'

Diagnostic Response Breakdown

{
  "index": "logs-production-2026.09.10",
  "shard": 0,
  "primary": true,
  "current_state": "unassigned",
  "unassigned_info": {
    "reason": "NODE_LEFT",
    "at": "2026-09-10T05:30:15.120Z",
    "details": "node [node-data-hot-3] left the cluster"
  },
  "can_allocate": "no",
  "allocate_explanation": "cannot allocate because allocation decider [disk_threshold] returned [NO]",
  "node_allocation_decisions": [
    {
      "node_name": "node-data-hot-1",
      "deciders": [
        {
          "decider": "disk_threshold",
          "decision": "NO",
          "explanation": "the node is above the high disk watermark [89.4% > 85%]"
        }
      ]
    }
  ]
}

Diagnostic Finding: The primary shard cannot allocate because remaining active nodes have breached the high disk watermark.


4. Disk Watermarks and Shard Allocation Locks

Elasticsearch evaluates storage thresholds using three critical watermark boundaries:

[Disk Capacity: 2 TB]
├── 0% - 85%: Normal Operation (Low Disk Watermark)
├── 85%: LOW WATERMARK BREACHED ──► Prevents allocating new shards to this node
├── 90%: HIGH WATERMARK BREACHED ──► Attempts to relocate existing shards away
└── 95%: FLOOD-STAGE WATERMARK ──► BLOCKS ALL WRITES (Sets index.blocks.read_only_allow_delete: true)

Clearing the Read-Only Flood-Stage Block

When disk usage drops below the high watermark, release the index write block:

curl -s -X PUT "https://es-cluster.internal:9200/*/_settings"   -H 'Content-Type: application/json'   -d '{
    "index.blocks.read_only_allow_delete": null
  }'

5. JVM Heap Pressure & Memory Architecture

Elasticsearch memory is divided between the JVM Heap (managed by Java G1GC) and the Linux OS Page Cache (managed by kernel mmap for Lucene indices):

[Total Server RAM: 64 GB]
├── 32 GB: JVM Heap (Maximum 50% Rule)
│   ├── Lucene In-Memory Term Dictionaries
│   ├── Query Caches & Request Buffers
│   └── Fielddata Cache (Aggregations on text)
└── 32 GB: Linux OS Page Cache
    └── Memory-Mapped Lucene Index Segments (.doc, .pos, .fdt)

The 32 GB Compressed OOPs Threshold

JVM Heap size must never exceed 31–32 GB. Above 32 GB, Java switches from 32-bit Compressed Ordinary Object Pointers (Compressed OOPs) to uncompressed 64-bit pointers. A 33 GB heap consumes more effective memory and runs slower than a 31 GB heap.

Heap Utilization Formulation

[ \text{Heap Utilization (%)} = \frac{\text{JVM Heap Used}}{\text{JVM Heap Committed}} \times 100 ]

When heap utilization exceeds 85% sustained:

  • Stop-the-World GC Pauses: Garbage collection freezes node execution for 2–10 seconds.
  • Node Dropouts: Master node marks the frozen data node as dead, triggering unnecessary shard rebalancing storms.

6. Fielddata vs. Doc Values: Preventing Memory Explosions

Executing aggregations or sorting on analyzed text fields loads fielddata directly into the JVM Heap:

Mapping Anti-Pattern:
"user_name": { "type": "text", "fielddata": true } ──► Loads millions of strings into JVM Heap!
Production Best Practice:
"user_name": {
  "type": "text",
  "fields": {
    "keyword": { "type": "keyword" }  <-- Uses Doc Values on Disk / OS Page Cache!
  }
}

Always aggregate on .keyword fields. Doc values are stored columnar on disk and read via the OS page cache, consuming zero JVM heap.


7. Slow Queries and Search Latency Profiling

Search execution occurs in two phases:

  1. Query Phase: Coordinating node fans out the query to all target shards; shards compute top matching document IDs.
  2. Fetch Phase: Coordinating node requests the actual _source document payloads from specific shards.

[ T_{\text{search}} \approx T_{\text{coordination}} + \max(T_{\text{shard_query}}) + T_{\text{fetch}} + T_{\text{serialization}} ]

Profiling Slow Queries via the _profile API

curl -s -X POST "https://es-cluster.internal:9200/logs-*/_search?pretty"   -H 'Content-Type: application/json'   -d '{
    "profile": true,
    "size": 10,
    "query": {
      "bool": {
        "filter": [
          { "term": { "environment": "production" } },
          { "range": { "@timestamp": { "gte": "now-1h" } } }
        ],
        "must": [
          { "wildcard": { "message": "*database error*" } }
        ]
      }
    }
  }'

Diagnostic Finding: Leading wildcards (*database error*) force full Lucene term index scans, taking 2,400 ms. Replacing with a match query on an analyzed text field reduces execution time to 4 ms.


8. Production-Safe Diagnostic Command Toolkit

Copy-pasteable CLI commands for immediate terminal incident triage:

1. Inspect Cluster Health Summary

curl -s "https://es-cluster.internal:9200/_cluster/health?pretty"

2. List All Unassigned Shards and Their Indices

curl -s "https://es-cluster.internal:9200/_cat/shards?v&h=index,shard,prirep,state,unassigned.reason" | grep UNASSIGNED

3. Inspect Node JVM Heap, CPU, and Disk Usage

curl -s "https://es-cluster.internal:9200/_cat/nodes?v&h=name,role,heap.percent,ram.percent,cpu,disk.used_percent,node.role"
name             role heap.percent ram.percent cpu disk.used_percent node.role
node-master-1    m              42          95   5                35 m
node-data-hot-1  d              78          98  65                82 d
node-data-hot-2  d              91          99  88                89 d

4. Inspect Thread Pool Queue Depths and Rejections

curl -s "https://es-cluster.internal:9200/_cat/thread_pool/search,write?v&h=node_name,name,active,queue,rejected"

9. Prometheus Alerting Rules for Elasticsearch

Using the elasticsearch-exporter, deploy these production PromQL rules:

groups:
  - name: elasticsearch_reliability_alerts
    rules:
      - alert: ElasticsearchClusterRed
        expr: elasticsearch_cluster_health_status{status="red"} == 1
        for: 1m
        labels:
          severity: critical
          tier: storage
        annotations:
          summary: "Elasticsearch Cluster Status is RED on {{ $labels.instance }}"
          description: "One or more primary shards are unassigned. Data is unavailable for incoming queries."

      - alert: ElasticsearchJVMHeapPressureHigh
        expr: (elasticsearch_jvm_memory_used_bytes{area="heap"} / elasticsearch_jvm_memory_max_bytes{area="heap"}) > 0.85
        for: 5m
        labels:
          severity: critical
          tier: storage
        annotations:
          summary: "Elasticsearch Node {{ $labels.node }} JVM Heap > 85%"
          description: "JVM Heap usage is sustained above 85%. Stop-the-world GC pauses imminent."

      - alert: ElasticsearchDiskHighWatermarkBreached
        expr: (elasticsearch_filesystem_data_used_bytes / elasticsearch_filesystem_data_size_bytes) > 0.85
        for: 5m
        labels:
          severity: warning
          tier: storage
        annotations:
          summary: "Elasticsearch Node {{ $labels.node }} Disk Usage > 85%"
          description: "Node disk breached low/high watermark. Shard allocation is disabled on this node."

10. Step-by-Step Incident Response Runbook: Cluster Red & Unassigned Shards

Follow this ordered diagnostic flow when alerted to Elasticsearch cluster degradation:

  1. Check Cluster Status: Execute _cluster/health to identify active, unassigned, and relocating shard counts.
  2. Isolate Failing Shards: Run _cat/shards?h=index,shard,prirep,state,unassigned.reason | grep UNASSIGNED.
  3. Run Allocation Explain: Target an unassigned shard using _cluster/allocation/explain to extract the exact allocation decider refusal reason.
  4. Inspect Node Storage & Heap: Check _cat/nodes for nodes exceeding 85% disk or 85% JVM heap.
  5. Mitigate immediately:
    • If blocked by disk watermarks, delete old indices or attach additional EBS storage volume.
    • If blocked by replica over-allocation (e.g., 2 replicas requested but only 2 nodes exist), reduce replica count:
      curl -s -X PUT "https://es-cluster.internal:9200/logs-*/_settings"        -H 'Content-Type: application/json'        -d '{ "index.number_of_replicas": 1 }'
      
    • If heap is pinned by fielddata aggregations, clear fielddata cache:
      curl -s -X POST "https://es-cluster.internal:9200/_cache/clear?fielddata=true"
      
  6. Trigger Shard Allocation Reroute: Once blockers are cleared, instruct Elasticsearch to retry unassigned shard allocation:
    curl -s -X POST "https://es-cluster.internal:9200/_cluster/reroute?retry_failed=true"
    
  7. Verify that cluster status returns to green, unassigned shards drop to 0, and search latency normalizes.
  8. Document root causes in a post-mortem, establishing automated Index Lifecycle Management (ILM) rollover policies.

11. Production Hardening Checklist for Elasticsearch

  • Dedicated Master Nodes Configured: At least 3 dedicated master nodes provisioned (separate from data nodes) to maintain quorum stability.
  • JVM Heap Capped at 50%: Heap size set between 16 GB and 31 GB with -Xms and -Xmx identical.
  • Index Lifecycle Management (ILM) Active: Hot-to-warm rollover policies configured (50 GB per shard or 30 days retention).
  • Shard Sizing Governed: Target shard size maintained between 20 GB and 50 GB; avoid over-sharding (limit to (< 20) shards per GB of heap).
  • Doc Values Enforced: String fields configured as keyword for aggregations; fielddata: true disabled.
  • Disk Watermarks Monitored: High watermark alerts configured at 80% to prevent flood-stage read-only blocks.
  • Synthetic Search Probing: Continuous canary queries testing query execution latency and cluster availability.

12. Operational Decision Tree

                        [Elasticsearch Cluster Performance Alert]
                                            │
                 ┌──────────────────────────┴──────────────────────────┐
                 ▼                                                     ▼
        [Cluster Status: RED / YELLOW]                        [Search Latency Spikes (p99 > 500ms)]
                 │                                                     │
     Run _cluster/allocation/explain                       Inspect Slow Logs & Thread Pool
                 │                                                     │
     ┌───────────┴───────────┐                             ┌───────────┴───────────┐
     ▼                       ▼                             ▼                       ▼
[Disk Watermark NO]    [Node Left Cluster]           [Heap > 85% (GC Stall)] [Oversharded Fanout]
Clean Old Indices /    Restart Data Node /           Clear Fielddata Cache   Consolidate Shards
Attach Storage         Verify AWS VPC Network        Aggregate on .keyword   Use ILM Rollover

Related SRE & Performance Guides

Zero-Code Uptime Alerts

Stop Finding Out About Outages from Angry Users

Get instant WhatsApp & Discord alerts the second your API, website, or server goes down. Setup in 30 seconds with 60-second checks.

WhatsApp & Discord 60-Second Checks Free Forever Plan
Try Pingzo Free

Know before your users do

Connect official WhatsApp notification channels, Discord webhooks, Telegram bots, and public status pages. Start in 30 seconds.

Create Free Monitor