The Elasticsearch heap is the JVM-managed memory used for query coordination, aggregations, cluster state, indexing buffers, and various caches. Correctly sizing the heap is one of the highest-leverage tuning decisions for an Elasticsearch cluster - undersized heaps trip circuit breakers and trigger OOMs, while oversized heaps cause long garbage collection pauses and waste memory that Lucene's file-system cache would use more productively.
- Rule 1: Set
-Xmsand-Xmxto the same value to disable heap resizing - Rule 2: Heap should be at most 50% of physical RAM. The other 50% is for Lucene memory-mapped files via the OS page cache
- Rule 3: Stay under ~30-31 GB to keep compressed ordinary object pointers (compressed oops). Above that threshold, the JVM switches to 64-bit pointers and effective memory drops sharply
- Modern versions (7.11+): Auto-sized based on available RAM if no override is set
Modern Heap Configuration (7.x and later)
If no -Xms/-Xmx is configured, Elasticsearch sets the heap automatically based on node role and available RAM. For most workloads the auto-sizing is acceptable for prototyping but should be reviewed before production.
To override, do not edit config/jvm.options directly - add a custom file under config/jvm.options.d/:
-Xms16g
-Xmx16g
JVM options files must use the .options suffix and are processed in lexicographic order. Per-install-method paths:
| Install type | Custom JVM options path |
|---|---|
| tar.gz / zip | config/jvm.options.d/ |
| deb / rpm | /etc/elasticsearch/jvm.options.d/ |
| Docker | bind-mount into /usr/share/elasticsearch/config/jvm.options.d/ |
The ES_JAVA_OPTS environment variable also works but is harder to audit:
export ES_JAVA_OPTS="-Xms16g -Xmx16g"
Sizing Guidelines
| Physical RAM | Recommended heap | Notes |
|---|---|---|
| 8 GB | 4 GB | Min viable for non-trivial workloads |
| 16 GB | 8 GB | Common dev/test node |
| 32 GB | 16 GB | Typical production data node |
| 64 GB | 30 GB | Sweet spot - max compressed oops |
| 128 GB | 30 GB + larger Lucene cache | Don't exceed 30 GB heap |
| 256 GB+ | Run multiple Elasticsearch instances per host with 30 GB heap each |
Above ~30 GB heap, compressed oops disable and the addressable heap drops to roughly 28-32 GB of usable memory until ~50 GB. Anywhere between is wasted - either stay under 30 or commit to ~50+ GB and accept the overhead.
The 30 GB Compressed Oops Threshold
The JVM uses compressed 32-bit object pointers when heap is below a platform-specific threshold (typically 30-32 GB). Above that, pointers grow to 64 bits, increasing memory consumption per object by roughly 25%. The exact crossover depends on the JVM version - check with:
java -Xmx31g -XX:+PrintFlagsFinal -version | grep UseCompressedOops
For Elasticsearch, sticking to -Xmx30g (or -Xmx29g for safety) is the standard production recommendation.
Legacy Heap Configuration (6.x and earlier)
Older versions used ES_HEAP_SIZE:
export ES_HEAP_SIZE=8g
Or by directly editing jvm.options. Modern installations should not use ES_HEAP_SIZE; the variable is deprecated.
Common Pitfalls
- Setting
-Xmxgreater than 50% of RAM. Lucene relies heavily on the OS page cache for fast term dictionary and doc value lookups - starving the cache slows queries dramatically. - Setting
-Xmssmaller than-Xmx. The JVM will resize the heap dynamically, causing GC pause spikes and wasted memory commit/release. - Crossing the 30 GB threshold. The heap looks bigger but addressable memory drops due to disabled compressed oops.
- Setting heap based on data volume. Heap size correlates with concurrent query and aggregation load, not stored data. A 1 TB cold-tier node may run fine on 8 GB heap; a 200 GB hot tier under heavy aggregation may need 30 GB.
Monitoring Heap Health
Useful APIs:
GET /_nodes/stats/jvm?filter_path=nodes.*.jvm.mem
GET /_nodes/stats/jvm?filter_path=nodes.*.jvm.gc
Watch:
- Heap used after GC (should stabilize well below max)
- Young-gen GC frequency and duration
- Old-gen GC count - frequent old GCs are a heap-pressure red flag
- Circuit breaker trips (parent breaker, fielddata, request)
Prevent Heap Misconfiguration with Pulse
Pulse is an AI DBA for Elasticsearch and OpenSearch that tracks -Xms, -Xmx, GC duration, and circuit-breaker activity across every node, flagging:
- Drift between intended and actual heap sizing (a common pattern after auto-sizing kicks in on 7.11+ when a node moves to a host with different RAM)
- Settings that are unsafe for your workload: heap above 31 GB triggers loss of compressed oops; heap above 50% of physical RAM starves Lucene's page cache;
-Xmsnot equal to-Xmxcauses resize-driven GC spikes - The downstream operational impact: old-gen GC frequency, parent/fielddata/request breaker trips, OOM patterns
When a node's working set outgrows its heap, or a config change accidentally puts heap into the wasted 30-50 GB compressed-oops dead zone, Pulse names the misconfiguration with the supporting GC and breaker evidence already collected.
Frequently Asked Questions
Q: How much heap should I give Elasticsearch?
A: At most 50% of physical RAM, and no more than approximately 30 GB to preserve compressed ordinary object pointers. For most production data nodes, 16-30 GB of heap on a 32-64 GB machine is the sweet spot.
Q: Why shouldn't Elasticsearch heap exceed 30 GB?
A: Above roughly 30-31 GB the JVM disables compressed oops, switching from 32-bit to 64-bit object pointers. The addressable heap drops by ~25% until the heap grows past ~50 GB, so any value between is strictly worse than 30 GB.
Q: Should -Xms and -Xmx be the same?
A: Yes. Setting them equal pre-allocates the full heap and disables runtime resizing, which avoids GC pauses and memory churn. Elasticsearch's bootstrap check warns when they differ in production mode.
Q: How do I change the Elasticsearch heap size?
A: Add a file under config/jvm.options.d/ (e.g. heap.options) containing -Xms16g and -Xmx16g. Do not edit jvm.options directly. Restart the node for the change to apply.
Q: Does Elasticsearch auto-configure heap size?
A: Yes, since 7.11 Elasticsearch auto-sizes the heap based on RAM and node roles if no explicit -Xmx/-Xms is provided. Review the chosen value before production - auto-sizing is sensible but rarely optimal for known workloads.
Q: What happens if Elasticsearch heap is too small?
A: Symptoms include frequent old-gen GCs, circuit breaker trips, OOM errors, slow aggregations, and node instability. The fix is to raise heap (within the 30 GB / 50% guardrails) or scale out.
Q: What's the best tool to prevent Elasticsearch heap misconfiguration?
A: Pulse is purpose-built for this. It is an AI DBA for Elasticsearch and OpenSearch that tracks -Xms/-Xmx, GC duration, and circuit-breaker activity on every node, flags heap above the 31 GB compressed-oops threshold or above 50% of RAM, and recommends sizing changes with the GC and OOM evidence already attached.
Related Reading
- Elasticsearch JVM Heap Pressure High: Diagnose >85% pressure
- Elasticsearch JVM Heap Pressure 75% 85% Fix: Warning-threshold remediation
- Elasticsearch Heap Sizing Recommendation: Full sizing guidance
- Elasticsearch bootstrap.memory_lock Setting: Lock heap into RAM
- Elasticsearch GC Overhead Errors: GC-related failures