ELK Stack — Cluster Monitoring
Cluster Monitoring
Monitoring your ELK Stack is essential for detecting issues before they escalate into outages. Elasticsearch exposes a rich set of APIs for cluster health, node statistics, and shard status. Combined with Stack Monitoring in Kibana and external tools like Prometheus, you get full visibility into every component.
Cluster Health API
The simplest starting point — returns a single status for the entire cluster:
curl -s "localhost:9200/_cluster/health" | jq .
{
"cluster_name": "production-elk",
"status": "green",
"number_of_nodes": 9,
"number_of_data_nodes": 6,
"active_primary_shards": 150,
"active_shards": 300,
"relocating_shards": 0,
"initializing_shards": 0,
"unassigned_shards": 0,
"delayed_unassigned_shards": 0,
"number_of_pending_tasks": 0,
"task_max_waiting_in_queue_millis": 0,
"active_shards_percent_as_number": 100.0
}
| Status | Meaning |
|---|---|
| green | All primary and replica shards are assigned |
| yellow | All primaries assigned, some replicas unassigned |
| red | One or more primary shards are unassigned — data loss risk |
Wait for a specific status (useful in scripts):
curl -s "localhost:9200/_cluster/health?wait_for_status=green&timeout=60s"
Cluster Stats
Aggregated statistics across the entire cluster:
curl -s "localhost:9200/_cluster/stats" | jq '.nodes, .indices'
Key fields to monitor:
indices.count— Total number of indicesindices.shards.total— Total shard count (keep under ~1,000 per node)nodes.jvm.mem.heap_used_in_bytes— Aggregate heap usagenodes.fs.total_in_bytes/nodes.fs.available_in_bytes— Disk capacity
Node Stats
Per-node breakdown of JVM, OS, thread pools, indexing, and search:
curl -s "localhost:9200/_nodes/stats" | jq '.nodes | to_entries[0].value | keys'
Key Metrics by Category
| Category | Metric Path | What to Watch |
|---|---|---|
| JVM Heap | jvm.mem.heap_used_percent |
Stay below 75% |
| GC | jvm.gc.collectors.old.collection_time_in_millis |
Spikes indicate memory pressure |
| Indexing | indices.indexing.index_total |
Rate of documents indexed |
| Search | indices.search.query_time_in_millis |
Average search latency |
| Thread Pools | thread_pool.write.rejected |
Rejections mean overload |
| Circuit Breakers | breakers.parent.tripped |
Trips mean queries are too large |
| Disk | fs.total.available_in_bytes |
Alert below 15% free |
Filtered Node Stats
Get only JVM and thread pool stats:
curl -s "localhost:9200/_nodes/stats/jvm,thread_pool" | jq .
_cat APIs
Human-readable tabular output, ideal for quick diagnosis:
Nodes Overview
curl -s "localhost:9200/_cat/nodes?v&h=name,ip,role,heap.percent,cpu,load_1m,disk.used_percent"
name ip role heap.percent cpu load_1m disk.used_percent
es-master-1 10.0.1.10 m 22 3 0.45 32.1
es-data-1a 10.0.1.11 d 61 18 2.31 58.4
es-data-1b 10.0.1.12 d 55 14 1.87 52.7
Shard Allocation
curl -s "localhost:9200/_cat/shards?v&h=index,shard,prirep,state,docs,store,node&s=index"
Index Sizes
curl -s "localhost:9200/_cat/indices?v&h=index,health,pri,rep,docs.count,store.size&s=store.size:desc"
Allocation Summary
curl -s "localhost:9200/_cat/allocation?v"
shards disk.indices disk.used disk.avail disk.total disk.percent host node
50 120gb 130gb 370gb 500gb 26 10.0.1.11 es-data-1a
50 115gb 125gb 375gb 500gb 25 10.0.1.12 es-data-1b
Pending Tasks
curl -s "localhost:9200/_cat/pending_tasks?v"
A growing list of pending tasks indicates the master node is overloaded.
Stack Monitoring in Kibana
Kibana’s built-in Stack Monitoring provides dashboards for Elasticsearch, Logstash, Beats, and APM Server.
Metricbeat-Based Collection (Recommended)
Self-monitoring is deprecated since ES 7.x. Use Metricbeat to ship monitoring data to a dedicated monitoring cluster (or the same cluster for small deployments).
Step 1 — Enable Monitoring Collection on ES
# elasticsearch.yml
xpack.monitoring.collection.enabled: false # Disable self-collection
xpack.monitoring.elasticsearch.collection.enabled: false
Step 2 — Configure Metricbeat on Each ES Node
# metricbeat.yml
metricbeat.modules:
- module: elasticsearch
xpack.enabled: true
period: 10s
hosts: ["https://localhost:9200"]
username: "monitoring_user"
password: "${ES_MONITORING_PASSWORD}"
ssl.verification_mode: certificate
output.elasticsearch:
hosts: ["https://monitoring-cluster:9200"]
username: "metricbeat_writer"
password: "${MB_PASSWORD}"
Step 3 — Open Stack Monitoring in Kibana
Navigate to Stack Monitoring → You should see cluster overview, node stats, index stats, and shard status.
Monitoring Logstash
Use Metricbeat with the logstash module:
metricbeat.modules:
- module: logstash
xpack.enabled: true
period: 10s
hosts: ["localhost:9600"]
Key Logstash metrics:
- Events in/out per second — Pipeline throughput
- Pipeline worker utilization — Are workers busy?
- Queue backpressure — Persistent queue growing?
Monitoring Beats
Beats can self-report monitoring data:
# filebeat.yml
monitoring.enabled: true
monitoring.elasticsearch:
hosts: ["https://monitoring-cluster:9200"]
username: "beats_monitoring"
password: "${BEATS_MON_PASSWORD}"
Prometheus Exporter for Elasticsearch
For teams that use Prometheus + Grafana instead of Stack Monitoring:
Deploy elasticsearch-exporter
docker run -d --name es-exporter \
-p 9114:9114 \
quay.io/prometheuscommunity/elasticsearch-exporter:latest \
--es.uri=http://es-coord-1:9200 \
--es.all \
--es.indices \
--es.shards
Prometheus Scrape Config
# prometheus.yml
scrape_configs:
- job_name: 'elasticsearch'
static_configs:
- targets: ['es-exporter:9114']
scrape_interval: 15s
Grafana Dashboard
Import the community dashboard ID 2322 or ID 14191 from grafana.com/dashboards.
Key Metrics to Watch — Summary
| Metric | Healthy Range | Alert Threshold |
|---|---|---|
| Cluster status | green | yellow/red |
| JVM heap used % | < 75% | > 85% |
| GC old collection time | Low, stable | Spikes > 1s |
| Indexing rate | Stable | Sudden drop/spike |
| Search latency (p99) | < 500ms | > 2s |
| Thread pool write rejections | 0 | > 0 |
| Circuit breaker trips | 0 | > 0 |
| Disk used % | < 80% | > 85% |
| Pending cluster tasks | 0 | > 10 for > 1 min |
| Unassigned shards | 0 | > 0 |
Cluster Alerts
Set up Kibana alerting rules for critical conditions:
- Stack Monitoring → Alerts → Enable built-in rules:
- Cluster health changed
- Nodes changed
- Disk usage threshold
- Memory usage threshold
- CPU usage threshold
- Actions — Send to Slack, PagerDuty, email, or webhook.
# Example: check health via script (cron fallback)
STATUS=$(curl -s localhost:9200/_cluster/health | jq -r '.status')
if [ "$STATUS" != "green" ]; then
echo "ALERT: Cluster status is $STATUS" | mail -s "ES Alert" ops@example.com
fi
Quick Reference
| Task | Command |
|---|---|
| Cluster health | GET _cluster/health |
| Cluster stats | GET _cluster/stats |
| Node stats (all) | GET _nodes/stats |
| Node stats (JVM) | GET _nodes/stats/jvm |
| Cat nodes | GET _cat/nodes?v |
| Cat shards | GET _cat/shards?v |
| Cat indices | GET _cat/indices?v&s=store.size:desc |
| Cat allocation | GET _cat/allocation?v |
| Pending tasks | GET _cat/pending_tasks?v |
| Thread pool stats | GET _cat/thread_pool?v&h=name,active,rejected,completed |
Next Steps
Now that you can observe your cluster, the next step is making sure it has enough resources. Continue to Capacity Planning.