For our Blog Visitor only Get Additional 3 Month Free + 10% OFF on TriAnnual Plan YSBLOG10
Grab the Deal

How to Optimize ElasticSearch on Linux Server for Better Speed

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.

Share via:

Prahlad Prajapati

Prahlad is a web hosting specialist and SEO-focused organic growth expert from India. Active in the digital space since 2019, he helps people grow their websites through clean, sustainable strategies. Passionate about learning and adapting fast, he believes small details create big success. Discover his insights on web hosting and SEO to elevate your online presence.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top