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 Signal | Healthy Baseline | Warning State | Critical Incident (Page) | Primary Remediation |
|---|---|---|---|---|
| Unassigned Shards | (0) | (1 - 5) (Yellow) | (> 0) Primary (Red) | Execute _cluster/allocation/explain |
| JVM Heap Utilization | (< 70%) | (70% - 85%) | (> 85%) sustained | Check 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:
- Query Phase: Coordinating node fans out the query to all target shards; shards compute top matching document IDs.
- Fetch Phase: Coordinating node requests the actual
_sourcedocument 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:
- Check Cluster Status: Execute
_cluster/healthto identify active, unassigned, and relocating shard counts. - Isolate Failing Shards: Run
_cat/shards?h=index,shard,prirep,state,unassigned.reason | grep UNASSIGNED. - Run Allocation Explain: Target an unassigned shard using
_cluster/allocation/explainto extract the exact allocation decider refusal reason. - Inspect Node Storage & Heap: Check
_cat/nodesfor nodes exceeding 85% disk or 85% JVM heap. - 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"
- 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" - Verify that cluster status returns to
green, unassigned shards drop to 0, and search latency normalizes. - 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
-Xmsand-Xmxidentical. - 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
keywordfor aggregations;fielddata: truedisabled. - 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
- CoreDNS Latency & DNS Resolution Failures in Kubernetes: SERVFAIL Errors, Upstream Timeouts, and Cache Tuning
- PostgreSQL Database Uptime & Query Tail Latency: Lock Contention, Index Bloat, and IOPS Saturation
- Redis Cache Availability & Cluster Saturation: Eviction Policies, Latency Spikes, and Memory Limits
- Infrastructure Observability & Health Checks: Probing Host Saturation and Service Endpoints
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.