To optimize Elasticsearch on Linux server, allocate the JVM heap to ~50% of RAM (max 31 GB), use SSD/NVMe storage, disable swap and Transparent Huge Pages, tune vm.max_map_count and ulimits, design shards correctly (20–50 GB each), and adjust index settings (refresh interval, replicas) for your workload while monitoring GC, I/O, and caches.
Optimizing Elasticsearch on a Linux server means tuning the OS, JVM, and Elasticsearch settings together. In this guide, I’ll walk you through practical, production-proven steps to optimize performance, stability, and cost. Whether you’re indexing logs or powering search, these best practices help you avoid bottlenecks and scale smoothly.
Quick Checklist: Elasticsearch Optimization on Linux
- Use 64-bit Linux, SSD/NVMe, and adequate RAM/CPU.
- Set JVM heap to ~50% of RAM (up to 31 GB). Leave the rest for the OS file cache.
- Disable swap and Transparent Huge Pages (THP). Lock memory.
- Increase vm.max_map_count and file descriptors (ulimits).
- Right-size shards (20–50 GB each) and replicas based on traffic and SLA.
- Use keyword fields for aggregations; avoid fielddata on text.
- For bulk indexing: replicas=0, larger refresh_interval, and _bulk API batches 5–15 MB.
- Monitor GC, I/O wait, cache hit rates, and slow logs. Scale nodes or rebalance shards.
Prepare the Linux Server
Hardware and Storage
- Storage: Prefer NVMe or SSD. Elasticsearch is I/O-intensive.
- CPU: Fewer fast cores often beat many slow ones for search latency.
- RAM: Size for your data and query patterns. Heap max 31 GB; OS page cache needs room to breathe.
- Filesystem: XFS or ext4. Consider noatime to reduce write amplification.
Kernel and sysctl Tuning
Elasticsearch maps many files into memory. Increase map count, set conservative swappiness, and allow ample file handles.
# /etc/sysctl.d/99-elasticsearch.conf
vm.max_map_count=262144
vm.swappiness=1
fs.file-max=2097152
net.core.somaxconn=65535
# Apply immediately
sysctl --system
Disable Swap and Transparent Huge Pages (THP)
# Disable swap now
swapoff -a
# Comment out swap in /etc/fstab to persist
sed -ri 's/^\s*([^#].*\s+swap\s+)/#\1/' /etc/fstab
# Disable THP via systemd unit
cat >/etc/systemd/system/disable-thp.service <<'EOF'
[Unit]
Description=Disable Transparent Huge Pages
After=sysinit.target local-fs.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/enabled'
ExecStart=/bin/sh -c 'echo never > /sys/kernel/mm/transparent_hugepage/defrag'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now disable-thp
File Descriptors and Memory Locking
# /etc/security/limits.d/elasticsearch.conf
elasticsearch soft nofile 65536
elasticsearch hard nofile 65536
elasticsearch soft nproc 65536
elasticsearch hard nproc 65536
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
# Systemd override for the service
systemctl edit elasticsearch
# Add:
[Service]
LimitNOFILE=65536
LimitNPROC=65536
LimitMEMLOCK=infinity
Filesystem Mount Options
- Use noatime on data volumes to reduce metadata writes.
- On SSD/NVMe, keep readahead conservative (e.g., 128):
blockdev --setra 128 /dev/nvme0n1.
Tune the JVM and Elasticsearch Process
Set the Heap Size Correctly
- General rule: Xms = Xmx = ~50% of system RAM up to 31 GB (to preserve compressed OOPs and better GC).
- Leave the other 50% for the OS page cache, file system, and Lucene.
# /etc/elasticsearch/jvm.options.d/heap.options
-Xms16g
-Xmx16g
Garbage Collector and JVM Flags
Elasticsearch 8.x uses G1GC by default. These flags are safe, production-friendly defaults.
# /etc/elasticsearch/jvm.options.d/gc.options
-XX:+UseG1GC
-XX:G1ReservePercent=15
-XX:InitiatingHeapOccupancyPercent=30
-XX:+AlwaysPreTouch
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/lib/elasticsearch/heapdump.hprof
-Xlog:gc*:file=/var/log/elasticsearch/gc.log:time,uptime:filecount=5,filesize=64m
Lock Memory
Prevent the OS from swapping out Elasticsearch memory.
# /etc/elasticsearch/elasticsearch.yml
bootstrap.memory_lock: true
# Ensure systemd allows it (see LimitMEMLOCK above), then:
systemctl daemon-reload
systemctl restart elasticsearch
Production elasticsearch.yml Essentials
# /etc/elasticsearch/elasticsearch.yml
cluster.name: prod-es
node.name: node-1
node.roles: [ data, ingest ] # Separate master-eligible nodes for stability
path.data: [ /data1/es, /data2/es ]
path.logs: /var/log/elasticsearch
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: [ "10.0.0.11", "10.0.0.12", "10.0.0.13" ]
cluster.initial_master_nodes: [ "node-1", "node-2", "node-3" ]
# Safe, helpful defaults
indices.memory.index_buffer_size: 20%
cluster.routing.allocation.disk.watermark.low: 85%
cluster.routing.allocation.disk.watermark.high: 90%
# Advanced; change only if you know the impact
# indices.breaker.total.limit: 70%
# thread_pool.write.queue_size: 1000
Secure clusters with TLS and authentication (x-pack security). While it’s mostly a security feature, preventing unauthorized heavy queries also protects performance.
Design Shards and Indices for Speed
Right-Size Primaries and Replicas
- Shard size target: 20–50 GB per shard for general use. Too many small shards wastes heap and file handles.
- Primaries: Match to data volume and node count; avoid creating dozens of primaries without need.
- Replicas: 1 replica is a good starting point for HA; set 0 during bulk loads and restore after.
Mappings That Avoid Fielddata Traps
Use keyword for aggregations and exact matches; use text for full-text search. Don’t aggregate on text fields (it triggers heavy fielddata).
PUT _index_template/logs_template
{
"index_patterns": ["logs-*"],
"template": {
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "1s"
},
"mappings": {
"properties": {
"message": { "type": "text" },
"service": { "type": "keyword", "doc_values": true },
"status": { "type": "keyword" },
"bytes": { "type": "long" },
"ts": { "type": "date" }
}
}
}
}
Index Sorting, Refresh, and Segment Management
- Index sorting (on timestamp or frequently filtered fields) speeds range queries.
- During heavy indexing, use a higher refresh_interval (e.g., 30s) or -1 temporarily.
- Force-merge to a small number of segments only for read-only indices (e.g., after bulk loads). It is resource-intensive.
PUT logs-2025.01/_settings
{
"index.routing.allocation.require._tier_preference": "data_hot",
"index.refresh_interval": "30s",
"index.sort.field": ["ts"],
"index.sort.order": ["asc"]
}
Use ILM and Rollover
Index Lifecycle Management (ILM) keeps shard sizes healthy and moves data from hot to warm/cold nodes.
PUT _ilm/policy/logs_policy
{
"policy": {
"phases": {
"hot": { "actions": { "rollover": { "max_age": "1d", "max_size": "50gb" } } },
"warm": { "actions": { "allocate": { "include": { "_tier_preference": "data_warm" } } } },
"cold": { "actions": { "allocate": { "include": { "_tier_preference": "data_cold" } } } }
}
}
}
Speed Up Bulk Indexing
Refresh Interval, Replicas, and Bulk Size
- Temporarily set replicas to 0 and refresh_interval to 30s or -1 for large ingests.
- Use the _bulk API with batches around 5–15 MB (not number of documents); parallelize with multiple workers.
- Prefer auto-generated IDs to avoid costly updates on the same document.
# Before bulk
PUT mydata/_settings
{ "index": { "number_of_replicas": 0, "refresh_interval": "30s" } }
# Bulk ingest with proper batching (pseudo command)
curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary @batch.ndjson
# After bulk
PUT mydata/_settings
{ "index": { "number_of_replicas": 1, "refresh_interval": "1s" } }
If durability can be relaxed temporarily, setting translog durability to async during bulk can help, but it risks data loss on crashes. Use with caution.
Accelerate Search Queries
Write Efficient Queries
- Prefer filters (term, range) that can leverage caches; filter context doesn’t affect scoring.
- Avoid leading wildcards on keyword fields. Use n-grams or prefix fields for autocomplete.
- Use search_after for deep pagination; from/size gets expensive at high offsets.
- Pre-filter shards with index sorting and time-based indices to avoid hitting all shards.
Aggregation and Field Choices
- Aggregate on keyword/numeric/date fields with doc_values (default). Avoid aggregations on text.
- Disable norms on keyword fields to save memory.
- Minimize the number of concurrent heavy aggregations; consider composite aggregations or downsampling.
Monitor, Benchmark, and Troubleshoot
Key Metrics to Watch
- Heap usage and GC pauses (gc.log, _nodes/stats). Sustained heap near 75–85% signals pressure.
- I/O wait and disk throughput (iostat, sar). High I/O wait = storage bottleneck.
- Query and indexing throughput/latency (_cat/thread_pool, _cat/nodes, slow logs).
- Shard counts per node and average shard size (_cat/shards). Too many shards hurts stability.
# Useful commands
curl -s localhost:9200/_cat/health?v
curl -s localhost:9200/_cat/nodes?v
curl -s localhost:9200/_cat/shards?v
curl -s localhost:9200/_nodes/stats/jvm,fs,indices?pretty
Slow Logs and Hot–Warm–Cold Architecture
- Enable slow logs for index and search to pinpoint expensive queries or mappings.
- Use hot nodes for writes, warm for queryable historical data, cold for infrequent access; move indices with ILM.
Common Pitfalls to Avoid
- Oversharding: Thousands of tiny shards waste heap and file handles.
- Too-large heap: Going above ~31 GB often worsens GC and reduces performance.
- Fielddata on text: Explodes memory usage; use keyword for aggregations.
- Unbounded wildcards and deep pagination: Rewrite queries for efficiency.
- Leaving swap/THP enabled: Causes jitter and instability under load.
When to Scale vs. Tune
- If heap is consistently high but GC is healthy, consider more data nodes or fewer shards per node.
- If I/O wait dominates, upgrade to NVMe or distribute data across more disks/nodes.
- If query latency spikes on specific aggregations, revisit mappings and pre-aggregation strategies.
Managed Option: Let YouStable Handle It
Don’t want to babysit JVM flags, sysctl, and shard balancing? At YouStable, our Managed VPS and Dedicated Servers run on enterprise SSD/NVMe with tuned kernels, production file systems, and 24×7 engineers who configure Elasticsearch for your workload—heap sizing, ILM, hot–warm–cold tiers, monitoring, and ongoing optimization included.
Practical Configuration Examples
Create a Production-Ready Index
PUT myindex
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "1s"
},
"mappings": {
"properties": {
"title": { "type": "text" },
"title.raw": { "type": "keyword", "ignore_above": 256 },
"category": { "type": "keyword" },
"price": { "type": "scaled_float", "scaling_factor": 100 },
"created_at": { "type": "date" }
}
}
}
Enable Slow Logs
PUT myindex/_settings
{
"index.search.slowlog.threshold.query.warn": "5s",
"index.search.slowlog.threshold.fetch.warn": "1s",
"index.indexing.slowlog.threshold.index.warn": "1s"
}
FAQs: Optimize ElasticSearch on Linux
What is the best JVM heap size for Elasticsearch?
Set Xms and Xmx to the same value, about 50% of system RAM, capped at 31 GB. This preserves compressed object pointers and keeps GC efficient. Leave the remaining RAM for the OS cache and Lucene.
How many shards should my index have?
Aim for shards in the 20–50 GB range. Use enough primaries to distribute load across data nodes but avoid creating too many small shards. ILM with rollover by size/time helps maintain healthy shard sizes automatically.
Should I disable swap and Transparent Huge Pages?
Yes. Swap and THP introduce latency and unpredictability under load. Disable swap, set swappiness low (1), disable THP, and enable bootstrap.memory_lock to keep Elasticsearch memory resident.
How do I speed up bulk indexing?
Temporarily set replicas=0 and refresh_interval to 30s or -1, then use the _bulk API with 5–15 MB batches and parallel workers. Re-enable replicas and normal refresh afterward. Auto-generated IDs also help.
What Linux settings matter most for Elasticsearch performance?
Set vm.max_map_count=262144, increase file descriptors, disable swap and THP, and use SSD/NVMe with noatime. Keep the kernel and filesystem tuned, and monitor I/O wait, GC, and shard counts consistently.
With these steps, you can confidently optimize Elasticsearch on a Linux server for both indexing throughput and low-latency search. If you’d rather focus on your app, YouStable can architect, deploy, and manage a fully optimized Elasticsearch stack tailored to your workload and budget.
Conclusion
Optimizing Elasticsearch on Linux starts with correct JVM heap sizing, disabled or minimized swapping, and sufficient file descriptors so memory and IO are predictable under load.
Combine that foundation with fast SSD storage, balanced shard and index design, and bulk-friendly indexing patterns to prevent hotspots and saturation. Finally, design queries and mappings carefully, monitor key metrics (heap, GC, IO, shard count), and iterate configuration as data and traffic grow to sustain performance.