Unit 4.2 · Search and Log Analytics: Elasticsearch, OpenSearch, ELK
Operating a Cluster: Health, Sizing, Lifecycle, Snapshots
Green/yellow/red and how to fix each, node roles, heap and shard sizing, disk watermarks, rollover with ILM/ISM (hot → warm → cold → delete), and snapshots to S3.
Start here
The mental model
Cluster health is a traffic light for shard copies. GREEN: every primary and replica shard is assigned. YELLOW: all primaries are fine but some replicas aren't assigned: data is searchable, but one more failure could lose it. RED: at least one primary is missing: some data is unavailable, and searches return partial results.
Most search-cluster problems are capacity problems in disguise: too many shards for the heap, too much data for the disks, or old data kept on expensive nodes. Index LIFECYCLE management automates moving and deleting data as it ages, the way retention works in Kafka (Unit 2.3).
Go deeper
How it works inside
01Node roles
CLUSTER-MANAGER (master) nodes maintain cluster state: indexes, mappings, and which shard lives where. Run 3 dedicated, small ones in production for a stable quorum. DATA nodes hold shards and do the indexing and searching (optionally tiered as hot, warm, and cold). INGEST nodes run ingest pipelines. COORDINATING-only nodes route requests and merge results. Small clusters combine roles; large ones separate them so a heavy search can't starve the managers.
02Heap, memory, and shard sizing
Give the JVM heap about 50% of the node's RAM, at most ~31 GB (above that the JVM loses compressed pointers). The other half is for the OS page cache, which Lucene relies on heavily. Every shard costs heap and file handles regardless of its size, so the classic mistake is thousands of tiny daily indexes × 5 shards each. Guidelines: shards of roughly 10–50 GB, and no more than about 20 shards per GB of heap (fewer is better).
03Disk watermarks
At the LOW watermark (85% disk used by default) the cluster stops allocating new shards to that node. At HIGH (90%) it tries to move shards away. At FLOOD STAGE (95%) every index with a shard on that node becomes READ-ONLY (index.blocks.read_only_allow_delete), and indexing fails cluster-wide for those indexes. Recent versions release the block automatically once disk drops below the high watermark; older ones needed a manual reset.
04Rollover, data streams, and ILM/ISM
For time-series data (logs, metrics, events), don't write to one ever-growing index or rely on date-named indexes. Write to an ALIAS or DATA STREAM that ROLLS OVER to a new backing index when the current one reaches a size or age (e.g. 30 GB or 1 day). Then a lifecycle policy (ILM in Elasticsearch, ISM in OpenSearch) manages each backing index: HOT (fast disks, being written) → WARM (read-only, force-merged, cheaper nodes) → COLD/frozen (object-storage-backed searchable snapshots, or UltraWarm on AWS) → DELETE after the retention period.
05Snapshots
SNAPSHOTS copy indexes incrementally to a repository (S3, GCS, Azure, shared FS). They're the only real backup: replicas don't protect against a bad delete or mapping mistake (Unit 0.3). Schedule them with SLM (Elasticsearch) or ISM snapshot management (OpenSearch); Amazon OpenSearch Service takes automated hourly snapshots too. Restore into the same or a new cluster, which is also how you migrate between clusters.
Do it
Hands-on lab
- 1
Read cluster health and explain yellow
On a single node, any index with replicas is yellow: a replica may never live on the same node as its primary.
_cluster/allocation/explaintells you why a shard is unassigned, and is the first command in any red/yellow incident.terminal$ curl -s -XPUT localhost:9200/logs-test -H 'content-type: application/json' -d '{"settings":{"number_of_replicas":1}}' > /dev/nullcurl -s 'localhost:9200/_cluster/health?pretty' | grep -E 'status|unassigned_shards"'curl -s localhost:9200/_cluster/allocation/explain -H 'content-type: application/json' -d '{"index":"logs-test","shard":0,"primary":false}' | jq -r '.node_allocation_decisions[0].deciders[0].explanation'── expected output ──"status" : "yellow","unassigned_shards" : 1,a copy of this shard is already allocated to this node [[logs-test][0], node[...], [P], s[STARTED]] - 2
The _cat APIs: your dashboard in a terminal
Human-readable views of nodes, shards, indexes, and disk.
?vadds headers ands=sorts.terminal$ curl -s 'localhost:9200/_cat/allocation?v'curl -s 'localhost:9200/_cat/indices?v&s=store.size:desc&h=health,index,pri,rep,docs.count,store.size' | head -4── expected output ──shards disk.indices disk.used disk.avail disk.total disk.percent host node4 12.4mb 41.2gb 58.7gb 99.9gb 41 172.17... 3f1c...health index pri rep docs.count store.sizeyellow logs-test 1 1 0 208bgreen products 1 0 3 9.8kb - 3
An ISM policy: hot → warm → delete
OpenSearch's ISM syntax (Elasticsearch ILM is similar in spirit, different in JSON). The
ism_templateattaches the policy to every new index matchinglogs-shop-*.logs-policy.jsonwhole filejson { "policy": { "description": "ShopLite logs: rollover daily, warm after 3d, delete after 30d", "default_state": "hot", "states": [ { "name": "hot", "actions": [ { "rollover": { "min_size": "30gb", "min_index_age": "1d" } } ], "transitions": [ { "state_name": "warm", "conditions": { "min_index_age": "3d" } } ] }, { "name": "warm", "actions": [ { "read_only": {} }, { "force_merge": { "max_num_segments": 1 } }, { "replica_count": { "number_of_replicas": 1 } } ], "transitions": [ { "state_name": "delete", "conditions": { "min_index_age": "30d" } } ] }, { "name": "delete", "actions": [ { "delete": {} } ] } ], "ism_template": [ { "index_patterns": ["logs-shop-*"], "priority": 100 } ] } }terminal$ curl -s -XPUT localhost:9200/_plugins/_ism/policies/logs-shop -H 'content-type: application/json' -d @logs-policy.json | jq -r ._idcurl -s -XPUT localhost:9200/logs-shop-000001 -H 'content-type: application/json' -d '{"settings":{"plugins.index_state_management.rollover_alias":"logs-shop"},"aliases":{"logs-shop":{"is_write_index":true}}}' | jq .acknowledged── expected output ──logs-shoptrue - 4
Register an S3 snapshot repository and snapshot
Needs the
repository-s3plugin (built into the managed services) and credentials via IAM role. Then snapshot on a schedule and test restores.terminal$ curl -s -XPUT localhost:9200/_snapshot/s3-backups -H 'content-type: application/json' -d '{"type":"s3","settings":{"bucket":"shoplite-search-snapshots","base_path":"prod"}}'curl -s -XPUT 'localhost:9200/_snapshot/s3-backups/snap-2026-09-27?wait_for_completion=true' | jq -r .snapshot.state── expected output ──{"acknowledged":true}SUCCESS
Operate it
Knobs that matter
| Setting | Default | What it does | When to change it |
|---|---|---|---|
| Heap (-Xms/-Xmx) | 1g (in Docker images) | JVM heap for the node. | ~50% of RAM, ≤ 31 GB, Xms = Xmx. |
| cluster.routing.allocation.disk.watermark.* | 85% / 90% / 95% | Disk thresholds for allocation and read-only blocks. | Keep defaults, but alert at 75% and plan capacity well before low. |
| index.routing.allocation.require.<attr> | — | Pin indexes to nodes with an attribute (e.g. temp: warm). | Used by lifecycle policies for hot/warm/cold tiers. |
| cluster.max_shards_per_node | 1000 | Safety limit on shards per data node. | Don't raise it to fix 'too many shards'; reduce shard count instead. |
| Dedicated manager nodes | none | Separate small nodes for cluster state. | 3 in any production cluster with more than a few data nodes. |
3am practice
Failure drills
Each drill is a real failure mode. Read the scenario and the output, decide what's wrong, then reveal the diagnosis.
Drill #1
Logs stop arriving, cluster is fine... ish
At 03:00, logs from every service stop appearing in dashboards. Fluent Bit logs are full of errors.
Drill #2
Red after a node dies
One of three data nodes is terminated by a spot interruption. Health goes red and some product searches return partial results.
The bigger picture
Connects to
System Design · Sharding
Partitioning across separate physical databases — the real 'put more money in the machine' of writes.
Unit 0.3 · Backups & DR
Snapshots are the backup; replicas aren't.
Unit 2.3 · Kafka retention
Same idea: age data out before disks fill.
Observability · Long-term storage
Tiering and retention for metrics and logs at scale.
AWS · Cost & resilience
Why hot/warm/cold tiers are mostly a cost decision.
Prove it
Interview questions
What do yellow and red cluster health mean and how do you troubleshoot?
How do you size shards and why does it matter?
What happens at the flood-stage watermark?