Jesse A. Green / notebook

Elasticsearch Administrator & Developer Guide

Generated May 2026 from elastic/elasticsearch at 7ccc603

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is the complete operational, architectural, and developer reference for Elasticsearch 9.5.0. It is written for engineers who deploy, operate, secure, tune, and extend Elasticsearch in production — from first cluster bring-up through large-scale multi-region deployments. It tracks source commit 7ccc603b (2026-04-24) on the main branch; release branches present in this source tree are 9.4, 9.3, 9.2, and 8.19.

Note

Every configuration setting, command, flag, and API path in this document has been derived from the Elasticsearch source tree at 7ccc603b. No values are fabricated. Where behavior changed between major versions, the relevant version is noted.


Table of Contents

  1. Architecture Overview
  2. System Requirements
  3. Configuration Reference
  4. Deployment & Scaling
  5. Operations
  6. Performance
  7. Security
  8. Developer Internals
  9. Integration Points

1. Architecture Overview

1.1 What Elasticsearch Is

Elasticsearch is a distributed document store and search engine built on Apache Lucene. Documents are JSON objects stored in indices. Each index is horizontally sharded across cluster nodes for scale, and each shard is replicated for fault tolerance. All reads, writes, and administration go through a single HTTP API on port 9200 (TLS by default since 8.0).

Key characteristics:

  • Eventually consistent search, strongly consistent writes. Writes succeed after being applied on the primary shard plus the configured replica count. Search reads from the last refreshed segment (default 1 second behind writes).
  • No external coordination service. No ZooKeeper, no etcd, no Consul. Master election and cluster-state replication are handled by the in-tree coordinator (server/src/main/java/org/elasticsearch/cluster/coordination/), which uses a Raft-inspired protocol.
  • Security on by default. Since 8.0, every fresh single-node and multi-node start auto-generates a CA, node certificates, and a random password for the elastic superuser.
  • Files are append-only Lucene segments. Updates are copy-on-write; deletes are logical (tombstones) until a background merge rewrites the segment. Never back up a live cluster by copying shard directories.
  • Plugin-based extension model. Every non-core capability — security, ML, SQL, ES|QL, watcher, CCR — is a plugin at the Java level. The boundary between server/ and x-pack/ is a packaging and licensing distinction, not a runtime one.

1.2 Source Tree Layout

The repository (elastic/elasticsearch) is a Gradle monorepo of 40,369 files and 68.6 million characters. Top-level areas:

Directory Description
server/ Core engine: indexing, search, cluster coordination, transport, REST dispatch, settings, aggregations, scripting runtime
x-pack/ Commercial features under the Elastic License: security, ML, SQL, ES|QL, EQL, monitoring, watcher, CCR, searchable snapshots, LogsDB, profiling, Prometheus metrics, spatial, vector tile, identity provider, inference
modules/ Bundled open-source extensions always included in the distribution: analyzers, ingest processors, Painless, geo, reindex, runtime fields, repository-azure/gcs, parent-join
plugins/ Optional installable plugins: language analyzers (ICU, Kuromoji, SmartCN, Nori, Phonetic, Stempel), discovery-gce, mapper-murmur3, store-smb
libs/ Shared internal libraries used by server and plugins: core, ssl-config, native (JNI), entitlement, geo, h3, tdigest, lz4, grok, simdvec, log4j, plugin-api
distribution/ Packaging: Docker entrypoint, deb/rpm/zip/tar, server-cli, JVM tools
rest-api-spec/ 600 JSON endpoint specification files — machine-readable ground truth for all REST APIs
build-tools/ / build-tools-internal/ Internal Gradle plugins (not shipped)
benchmarks/ JMH micro-benchmarks
test/ Test framework and fixtures
qa/ Integration test suites: evil-tests, smoke-tests, rolling-upgrade, full-cluster-restart, multi-version

1.3 Process Model

Each Elasticsearch node is a single JVM process. The startup chain from distribution/tools/server-cli/ is:

  1. Locate JDK (bundled at jdk/ unless ES_JAVA_HOME is set).
  2. Merge JVM options from config/jvm.options, files under config/jvm.options.d/, and automatic ergonomics (heap sizing from container memory limits if not explicitly set).
  3. Launch org.elasticsearch.bootstrap.Elasticsearch, which runs bootstrap checks, binds network ports, and starts subsystems.
  4. If telemetry.tracing.enabled: true, attach the APM instrumentation agent.

Once running, a node hosts these concurrent subsystems in-process:

Subsystem Transport Notes
HTTP layer Netty HTTP/1.1 + HTTP/2 on 9200 Client REST traffic
Transport layer Netty TCP on 9300 Inter-node RPC: cluster-state, search fan-out, index operations
Indexing engine One Lucene IndexWriter per primary shard Translog-backed for durability
Cluster coordinator In-process Master election, state publication, membership
Shard allocator In-process Placement decisions, rebalancing
Scheduler In-process ILM, SLM, transforms, watcher, rollup
Plugin hosts In-process Security, ML, SQL, ES|QL, fleet, etc.

1.4 Node Roles

A node's responsibilities are declared by the node.roles list in elasticsearch.yml. A node may hold multiple roles.

Role Description Production guidance
master Eligible to be elected cluster master Require exactly 3 in production for HA quorum
data Generic data tier (deprecated as sole role) Prefer explicit tier roles below
data_content Non-time-series data (catalogs, user data) Standard default for regular indices
data_hot Newest, most-written time-series data Fastest NVMe disks; highest IOPS
data_warm Recent, less-frequently-written data Cheaper spinning/SATA SSD acceptable
data_cold Rarely queried historical data Often backed by searchable snapshots
data_frozen Extremely cold, query-on-demand only Searchable snapshots only; near-zero heap overhead
ingest Runs ingest pipelines on incoming documents Co-locate with data_hot unless heavy enrichment
ml Machine learning jobs, anomaly detection, NLP inference Requires extra heap; X-Pack only
transform Continuous and pivot transforms Usually co-located with data nodes in small clusters
remote_cluster_client Can connect to and search remote clusters Required on nodes that issue CCS queries
voting_only Participates in master election but cannot be elected Efficient quorum padding with minimal resources

An empty node.roles: [] declares a coordinating-only node: routes client traffic and reduces search results but holds no shards and is never master-eligible.

Recommended layout for a production hot/warm cluster:

3 × dedicated-master  node.roles: [master]
N × hot-data          node.roles: [data_hot, data_content, ingest, transform]
M × warm/cold-data    node.roles: [data_warm, data_cold]
2 × coordinating      node.roles: []                        (optional)
K × ML                node.roles: [ml, remote_cluster_client]  (optional)

1.5 Cluster State and Coordination

The cluster state is a single versioned document held in memory on every node and persisted to disk by the elected master. It contains:

  • Cluster metadata (name, UUID)
  • Index metadata (mappings, settings, aliases, data stream definitions)
  • Routing table (which shard copy lives on which node, shard states)
  • Node membership list
  • Ingest pipeline definitions
  • Index templates and component templates
  • ILM policies, SLM policies, transform configs, ML configs
  • Security role mappings

Master Election

Masters elect via the coordinator (server/src/main/java/org/elasticsearch/cluster/coordination/):

  • Terms — monotonically increasing integers. Every new election bumps the term.
  • Voting configuration — set of master-eligible nodes whose majority constitutes a quorum. Evolves with the cluster state as nodes join or leave.
  • Publication — two-phase commit: send diff to followers → wait for acknowledgment from quorum → commit. A committed state is durable before the commit returns.
  • Follower check — elected master pings all followers periodically. Unreachable followers are removed.
  • Leader check — each follower pings the master. If the master is unreachable, followers trigger re-election.
  • Bootstrap — cluster.initial_master_nodes is consumed only when nodes boot with no existing cluster state. Remove it from all configs after the cluster first forms.

Important

cluster.initial_master_nodes must be set only once, at initial cluster formation, and must name only the nodes physically present at that moment. After the cluster forms, remove this setting from every node's elasticsearch.yml. If left in place it can cause a split-brain when those named nodes are later replaced.

Shard Allocation

The allocator runs as a stack of AllocationDecider implementations followed by a balancer:

  1. Hard constraints — index.routing.allocation.require.*, include.*, exclude.*, node role compatibility, version compatibility, data-tier filters.
  2. Disk watermarks — stop placing new shards at low (85%), begin migrating existing shards at high (90%), block writes at flood_stage (95%).
  3. Zone awareness — cluster.routing.allocation.awareness.attributes: zone spreads replicas across declared failure domains.
  4. Balance scoring — BalancedShardsAllocator equalizes weighted shard count, write load, and disk usage across nodes.
  5. Desired balance (9.x) — asynchronous desired-balance computation decouples re-planning from actual shard moves, improving responsiveness under load.

Diagnose any unassigned shard:

GET /_cluster/allocation/explain

1.6 Storage: Indices, Shards, and Segments

An index is a logical namespace split into one or more primary shards. Each primary is a self-contained Lucene index. Writes always go to the primary first, then propagate to replicas. Reads can be served by any live copy.

Lucene Segment Lifecycle

  1. Documents are written to an in-memory indexing buffer and simultaneously to the translog (write-ahead log on disk).
  2. At refresh (index.refresh_interval, default 1s), the buffer flushes to a new on-disk segment and becomes searchable.
  3. Segments are immutable. Updates write a new version; the old version is tombstoned. Deletes mark tombstones.
  4. Background merges consolidate small segments into larger ones, physically removing tombstoned documents.
  5. At flush, Lucene commits and the translog is truncated.

Shard Count Trade-offs

Factor Fewer large shards More smaller shards
Heap usage per node Lower Higher
Intra-query parallelism Lower Higher
Small-query fan-out overhead Lower Higher
Recovery time Slower per shard Faster per shard
Rebalance granularity Coarse Fine

Target shard size: 10–50 GB of post-merge on-disk data. Query-heavy catalogs: lower end. Bulk log archives: upper end.

Maximum shards per node: aim for ≤ 20 × heap_GB. Hard ceiling ~1,000–2,000 shards per node before GC and state-management pressure becomes significant.

1.7 Module and Plugin Inventory

Core Server Packages (key areas)

Package path Contents
server/src/main/java/org/elasticsearch/common/settings/ 24 files, 405K chars: Setting, Settings, AbstractScopedSettings, KeyStoreWrapper, ClusterSettings, IndexScopedSettings
server/src/main/java/org/elasticsearch/search/internal/ 20 files, 167K chars: ShardSearchRequest, SearchContext, ContextIndexSearcher
server/src/main/java/org/elasticsearch/search/aggregations/pipeline/ 39 files, 74K chars: all pipeline aggregation implementations
server/src/main/java/org/elasticsearch/injection/guice/internal/ 25 files, 91K chars: internal Guice DI for plugin wiring
server/src/main/java/org/elasticsearch/common/cli/ EnvironmentAwareCommand, KeyStoreAwareCommand — base classes for all bin/ tools
server/src/main/java/org/elasticsearch/plugins/internal/ 12 files: internal plugin system APIs

X-Pack Plugins (commercial, Elastic License)

Plugin Purpose
x-pack/plugin/security/ Authentication (native, LDAP, AD, PKI, SAML, OIDC, JWT, Kerberos), authorization (roles, FLS/DLS), TLS auto-config, audit logging, API keys, service accounts
x-pack/plugin/ml/ Anomaly detection, data frame analytics, trained models (PyTorch NLP), inference tasks
x-pack/plugin/esql/ ES|QL query language engine — piped, columnar query processing
x-pack/plugin/sql/ SQL query layer with JDBC and ODBC drivers
x-pack/plugin/eql/ Event Query Language for sequence-based threat detection
x-pack/plugin/inference/ ML inference service: 13 service implementations, pluggable inference backends
x-pack/plugin/transform/ Pivot and continuous data transforms
x-pack/plugin/watcher/ Alerting/Watcher: scheduled watches, conditions, actions (email, Slack, PagerDuty, webhook, index)
x-pack/plugin/ccr/ Cross-cluster replication: leader/follower index streaming
x-pack/plugin/rollup/ Time-series rollup jobs (legacy; prefer downsampling for new deployments)
x-pack/plugin/blob-cache/ 5 files, 56K chars: blob cache for searchable snapshots
x-pack/plugin/searchable-snapshots/ Searchable snapshots: cold/frozen indices served from snapshot repositories
x-pack/plugin/logsdb/ LogsDB index mode — optimized mapping and codec for log workloads
x-pack/plugin/ent-search/ Enterprise Search features
x-pack/plugin/profiling/ Universal Profiling: continuous profiling data ingestion and flamegraph rendering
x-pack/plugin/prometheus/ Prometheus-compatible metrics endpoint
x-pack/plugin/spatial/ Spatial/GIS: shape queries, geo_shape, cartesian fields
x-pack/plugin/vector-tile/ Vector tile search for map rendering
x-pack/plugin/identity-provider/ SAML identity provider (Elasticsearch as IdP)
x-pack/plugin/core/ Shared X-Pack core: scheduler, ML job config, security core types

Bundled Modules

Module Contents
modules/ingest-common/ 45 ingest processors: append, bytes, community_id, convert, csv, date, date_index_name, dissect, dot_expander, drop, enrich, fail, fingerprint, foreach, grok, gsub, html_strip, join, json, kv, lowercase, network_direction, pipeline, registered_domain, remove, rename, reroute, script, set, set_security_user, sort, split, trim, uppercase, uri_parts, urldecode, user_agent
modules/ingest-geoip/ GeoIP processor (MaxMind GeoLite2 and GeoIP2 databases)
modules/lang-painless/ Painless scripting language (7 API files; used in scripted fields, ingest, transforms, watcher)
modules/analysis-common/ Text analysis: tokenizers (standard, letter, whitespace, UAX URL email, path hierarchy, pattern), token filters (stop, stemmer, synonym, ASCII folding, n-gram, edge n-gram, shingle, etc.), char filters
modules/rank-eval/ Ranking evaluation API for offline relevance measurement
modules/repository-azure/ Azure Blob Storage snapshot repository
modules/repository-gcs/ Google Cloud Storage snapshot repository
modules/legacy-geo/ Legacy geo_shape support (pre-BKD shapes)
modules/runtime-fields-common/ Runtime fields evaluation engine
modules/parent-join/ Parent-child document joins via join field type
modules/reindex/ _reindex, _update_by_query, _delete_by_query with throttling and remote reindex
modules/mapper-extras/ Additional field types: rank_feature, rank_features, scaled_float, token_count, annotated_text
modules/percolator/ Percolator queries (store queries, match against documents)
modules/data-streams/ Data stream lifecycle management
modules/apm/ APM / Application Performance Monitoring integration

Optional Plugins

Plugin Purpose
plugins/analysis-icu/ Unicode text analysis using ICU4J
plugins/analysis-kuromoji/ Japanese morphological analysis
plugins/analysis-smartcn/ Smart Chinese analyzer
plugins/analysis-nori/ Korean analysis
plugins/analysis-phonetic/ Phonetic token filters (Soundex, Metaphone, Caverphone, etc.)
plugins/analysis-stempel/ Polish analysis (Stempel stemmer)
plugins/discovery-gce/ Google Compute Engine instance discovery
plugins/mapper-murmur3/ Murmur3 hash field type
plugins/store-smb/ SMB/Windows share as data path (Windows only)
plugins/examples/ Plugin development examples: custom-settings, rest-handler, security-authorization-engine, painless-whitelist, rescore

Shared Libraries (libs/)

Library Purpose
libs/core/ Core Java utilities: Strings, Numbers, DateMath
libs/ssl-config/ SSL/TLS configuration primitives
libs/native/ 10 files, 25K chars: JNI wrappers for OS-level operations (mlockall, prctl, syscall)
libs/entitlement/ 15 config files, 95K chars: runtime security capability system (replaces SecurityManager in 9.x)
libs/geo/ 10 files, 101K chars: geometry utilities (GeoUtils, SpatialUtils, GeometryParser)
libs/h3/ H3 hexagonal grid library for spatial indexing
libs/tdigest/ T-Digest quantile estimation for percentile aggregations
libs/lz4/ LZ4 fast compression (translog and network)
libs/grok/ Grok pattern matching (used by ingest-common grok processor)
libs/plugin-api/ 6 files: public Plugin settings API for third-party plugins
libs/simdvec/ SIMD-accelerated vector operations (for kNN dense vectors)
libs/user-agent-api/ User agent parsing utilities
libs/log4j/ Log4j2 wrapper and configuration
libs/server-launcher-common/ 2 files, 12K chars: shared launcher utilities

1.8 Build System

Elasticsearch uses Gradle with internal build plugins under build-tools-internal/:

Gradle plugin Purpose
elasticsearch.java Standard Java compilation settings, formatting, checkstyle
elasticsearch.build Top-level build lifecycle
elasticsearch.publish Maven Central publishing via nmcp aggregation
elasticsearch.docker-support Multi-arch Docker image builds
elasticsearch.jdk-download Pins and downloads the bundled JDK
elasticsearch.global-build-info Exposes version, git hash, build date
elasticsearch.runtime-jdk-provision Provisions JDK for test clusters
elasticsearch.fips FIPS 140-2 mode build
elasticsearch.internal-testclusters In-process test clusters for integration tests
elasticsearch.run Launches a local dev cluster with ./gradlew run
elasticsearch.run-ccs Launches a two-cluster CCS dev setup
elasticsearch.release-tools Handles release version manipulation and changelogs

2. System Requirements

2.1 Minimum Requirements (Development)

Resource Minimum
CPU architecture x86_64 or aarch64
CPU cores 2
RAM 4 GB (2 GB heap + page cache)
Disk 10 GB, local filesystem
JVM Bundled OpenJDK (included in tarball/Docker image)
OS Linux kernel ≥ 4.4, macOS 12+, Windows Server 2019+

2.2 Production Per-Node Baseline

Resource Recommended
CPU 8–32 cores (data nodes); 4–8 (master nodes)
RAM 32–64 GB per data node; 8–16 GB per master node
JVM heap 50% of RAM, maximum ~30.5 GB (compressed oops boundary)
Disk (hot) NVMe SSD; 1.5–2× raw data size accounting for replicas
Disk (warm/cold) SATA SSD acceptable; same sizing multiplier
Network 10 Gb/s for > 1 TB/day ingest or large multi-node clusters

2.3 Required Kernel Parameters

# /etc/sysctl.d/99-elasticsearch.conf
vm.max_map_count=262144
vm.swappiness=1

Apply without reboot: sysctl -w vm.max_map_count=262144

2.4 Required System Limits

In /etc/security/limits.conf or systemd unit override:

elasticsearch  -  nofile   65535
elasticsearch  -  nproc    4096
elasticsearch  -  memlock  unlimited

systemd:

[Service]
LimitNOFILE=65535
LimitNPROC=4096
LimitMEMLOCK=infinity

2.5 Heap Sizing

Warning

Never set JVM heap above ~30.5 GB. Above this threshold the JVM switches from 4-byte compressed ordinary object pointers to 8-byte full pointers, effectively wasting 30–40% of heap. Add nodes rather than growing heap past this boundary. Set -Xms and -Xmx to the same value to prevent runtime resizing pauses.

# config/jvm.options.d/heap.options
-Xms30g
-Xmx30g

Or via environment: ES_JAVA_OPTS="-Xms16g -Xmx16g"

The remaining RAM (the ~50% not allocated to heap) is reserved for the OS filesystem page cache backing Lucene's memory-mapped segment files.

2.6 Swap

Disable completely:

swapoff -a
# Remove swap entries from /etc/fstab for persistence

Set bootstrap.memory_lock: true in elasticsearch.yml. The node will refuse to start if the lock fails — fix the ulimit, not the config.

2.7 Storage Recommendations

  • Local NVMe for data_hot. Network-attached storage introduces latency spikes that impact GC and write durability.
  • XFS or ext4 with noatime mount option.
  • Avoid BTRFS (copy-on-write semantics conflict with Lucene's update model).
  • path.data directory must be owned by the Elasticsearch OS user with 0700 permissions.

2.8 Docker Constraints

  • Set a container memory limit; heap ergonomics reads cgroup memory limits.
  • --ulimit memlock=-1:-1 and --ulimit nofile=65535:65535
  • Set vm.max_map_count=262144 on the host — cannot be set inside the container.
  • Use a named volume or hostPath PVC for path.data.

3. Configuration Reference

Elasticsearch has two configuration surfaces:

  1. elasticsearch.yml — static, per-node, read at startup.
  2. Cluster settings — dynamic, cluster-wide, applied live via PUT /_cluster/settings. Use persistent (survives full restart) rather than transient (lost on full cluster restart; deprecated).

3.1 Node Identity

cluster.name: prod-logs
node.name: es-hot-01

3.2 Node Roles

node.roles:
  - data_hot
  - data_content
  - ingest
  - transform

Valid role values: master, data, data_hot, data_warm, data_cold, data_frozen, data_content, ingest, ml, transform, remote_cluster_client, voting_only. Empty list = coordinating-only.

3.3 Custom Node Attributes

node.attr.zone:     us-east-1a
node.attr.rack:     rack-7
node.attr.storage:  nvme

Referenced by allocation filters and awareness. Visible in _cat/nodeattrs and GET /_nodes.

3.4 Paths

path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
path.repo:
  - /mnt/snapshots

path.data may be a YAML list to stripe across multiple disks.

Note

When path.data contains multiple paths, Elasticsearch stripes new shards across them in round-robin. It does not mirror — if one path fails, the shards on that path are lost. Rely on shard replicas across nodes for redundancy, not multiple local paths.

3.5 Networking

network.host: _site_
http.port: 9200
transport.port: 9300
http.cors.enabled: false
http.compression: true
http.max_content_length: 100mb

network.host special values:

Value Meaning
_local_ Loopback only
_site_ All site-local addresses
_global_ All globally-routable addresses
_[interface]_ Named network interface (e.g., _eth0_)
_ec2:privateIpv4_ EC2 private IPv4 via metadata service
_ec2:publicIpv4_ EC2 public IPv4
_gce:hostname_ GCE instance hostname

To publish a different address than bound (NAT / Docker):

network.host: _local_
network.publish_host: 10.0.1.42

3.6 Discovery and Cluster Formation

discovery.type: multi-node
discovery.seed_hosts:
  - "es-master-01.internal"
  - "es-master-02.internal:9300"
cluster.initial_master_nodes:
  - "es-master-01"
  - "es-master-02"
  - "es-master-03"
Setting Type Default Description
discovery.type static multi-node single-node skips election for dev.
discovery.seed_hosts static ["127.0.0.1","::1"] Initial contact points.
discovery.seed_providers static settings Alternative: file, ec2, gce, azure-classic.
cluster.initial_master_nodes static empty Bootstrap-only. Remove after first cluster formation.
cluster.fault_detection.leader_check.interval dynamic 1s How often followers check the leader.
cluster.fault_detection.leader_check.timeout dynamic 10s Leader check timeout.
cluster.fault_detection.follower_check.interval dynamic 1s How often master checks followers.
cluster.fault_detection.follower_check.timeout dynamic 10s Follower check timeout.
cluster.publish.timeout dynamic 30s Timeout for cluster-state publication.
cluster.election.back_off_time dynamic 100ms Back-off after failed election.
cluster.election.max_timeout dynamic 10s Maximum election timeout.

3.7 Memory and Bootstrap

bootstrap.memory_lock: true
bootstrap.system_call_filter: true

Both are verified at startup; failure aborts the node. Set bootstrap.system_call_filter: false only on kernels that do not support seccomp.

3.8 Allocation and Rebalancing (Cluster Dynamic Settings)

Setting Default Description
cluster.routing.allocation.enable all all, primaries, new_primaries, none
cluster.routing.allocation.disk.watermark.low 85% Stop placing new shards above this disk usage
cluster.routing.allocation.disk.watermark.high 90% Begin migrating shards away above this
cluster.routing.allocation.disk.watermark.flood_stage 95% Block all index writes above this
cluster.routing.allocation.awareness.attributes — Attribute name(s) for shard spread
cluster.routing.allocation.awareness.force.<attr>.values — Explicit list of attribute values
cluster.routing.allocation.balance.shard 0.45f Weight for shard count in balancer
cluster.routing.allocation.balance.write_load 10.0 Weight for write load in balancer (9.x)
cluster.routing.allocation.balance.disk_usage 2e-11 Weight for disk usage in balancer (9.x)
cluster.routing.allocation.node_concurrent_recoveries 2 Concurrent recoveries per node
cluster.routing.allocation.node_initial_primaries_recoveries 4 Concurrent primary recoveries on restart
cluster.routing.rebalance.enable all Enable/restrict automatic rebalancing
indices.recovery.max_bytes_per_sec 40mb Recovery bandwidth throttle per node

3.9 Index-Level Settings

Applied in templates or at index creation time. Dynamic settings can be updated via PUT /{index}/_settings:

Setting Default Description
index.number_of_shards 1 Primary shard count. Immutable after creation.
index.number_of_replicas 1 Replica count. Changeable live.
index.refresh_interval 1s How often to make new writes searchable. -1 = manual only.
index.translog.durability request request (fsync per write) or async (batched fsyncs).
index.translog.flush_threshold_size 512mb Translog size that triggers a Lucene commit.
index.translog.sync_interval 5s Fsync interval for async durability.
index.codec default Lucene codec: default, best_compression (zstd).
index.mapping.total_fields.limit 1000 Maximum mapped fields. Increase for wide JSON.
index.mapping.depth.limit 20 Maximum nested object depth.
index.max_result_window 10000 Maximum from + size. Increase cautiously.
index.max_terms_count 65536 Maximum terms in a terms query.
index.routing.allocation.require.* — Hard allocation requirement.
index.routing.allocation.include.* — Soft inclusion preference.
index.routing.allocation.exclude.* — Hard exclusion.
index.routing.allocation.include._tier_preference — Tier preference list (e.g., data_hot,data_warm).
index.lifecycle.name — ILM policy to apply.
index.blocks.write false Block write operations. Required before _shrink/_split.
index.blocks.read_only_allow_delete false Set automatically by flood-stage watermark.
index.search.slowlog.threshold.query.warn -1 Log queries slower than this at WARN.
index.search.slowlog.threshold.fetch.warn -1 Log fetch phases slower than this at WARN.
index.indexing.slowlog.threshold.index.warn -1 Log indexing operations slower than this at WARN.

3.10 Thread Pools

Thread pool sizes are auto-tuned from CPU count. Override only with profiling data:

Pool Type Default size Queue size Used for
write fixed min(cpu, 32) 10,000 Index, bulk, update, delete
search fixed_auto_queue max(cpu*3/2+1, 5) 1,000 Search, aggregation queries
search_coordination fixed_auto_queue max(cpu/2, 1) 1,000 CCS coordination
get fixed cpu 1,000 Get by ID
analyze fixed 1 16 Analyze API
snapshot scaling min(5, cpu/2) — Snapshot operations
snapshot_meta scaling min(50, cpu*3) — Snapshot metadata
management scaling 1–5 — Cluster management tasks
flush scaling 1–min(5, cpu/2) — Translog flush and fsync
force_merge fixed 1 — Force-merge operations
refresh scaling 1–min(10, cpu/2) — Index refresh

3.11 Slow Logs

PUT /my-index/_settings
{
  "index.search.slowlog.threshold.query.warn":   "10s",
  "index.search.slowlog.threshold.query.info":   "5s",
  "index.search.slowlog.threshold.query.debug":  "2s",
  "index.search.slowlog.threshold.query.trace":  "500ms",
  "index.search.slowlog.threshold.fetch.warn":   "1s",
  "index.indexing.slowlog.threshold.index.warn": "10s",
  "index.indexing.slowlog.level":                "info"
}

Logs emit as structured JSON to logs/<cluster>_index_search_slowlog.json and logs/<cluster>_index_indexing_slowlog.json.

3.12 Runtime Log Level Changes

PUT /_cluster/settings
{
  "transient": {
    "logger.org.elasticsearch.discovery": "DEBUG"
  }
}

3.13 JVM Options

Put overrides in config/jvm.options.d/ (never edit config/jvm.options directly):

# config/jvm.options.d/custom.options
-Xms30g
-Xmx30g
-XX:+UseG1GC
-XX:InitiatingHeapOccupancyPercent=30
-XX:G1ReservePercent=25
-XX:+AlwaysPreTouch
-Djava.io.tmpdir=/var/lib/elasticsearch/tmp
-Des.networkaddress.cache.ttl=60
-Des.networkaddress.cache.negative.ttl=10

3.14 Keystore (Secure Settings)

bin/elasticsearch-keystore create [-p]
bin/elasticsearch-keystore add <setting>
bin/elasticsearch-keystore add-file <setting> <file>
bin/elasticsearch-keystore list
bin/elasticsearch-keystore remove <setting>
bin/elasticsearch-keystore passwd
bin/elasticsearch-keystore upgrade

Reload without restart:

POST /_nodes/reload_secure_settings
{ "secure_settings_password": "optional-keystore-password" }

Common secure settings:

Setting key Purpose
xpack.security.transport.ssl.keystore.secure_password Transport TLS keystore password
xpack.security.http.ssl.keystore.secure_password HTTP TLS keystore password
s3.client.<name>.access_key S3 client access key
s3.client.<name>.secret_key S3 client secret key
azure.client.<name>.account Azure storage account name
azure.client.<name>.key Azure storage account key
gcs.client.<name>.credentials_file GCS service account JSON path
xpack.security.authc.realms.ldap.<name>.secure_bind_password LDAP bind password

3.15 Docker Environment Variable Convention

From EnvironmentAwareCommand.java:

  • ES_SETTING_ prefix variables are translated: remove prefix, replace _ with ., unescape .. back to _, lowercase.
  • ES_SETTING_CLUSTER__NAME=prod-logs → cluster.name: prod-logs
  • ES_SETTING_NODE__NAME=es-01 → node.name: es-01
  • Lowercase dot-separated variables also work directly in the Docker entrypoint.

3.16 Plugin Configuration File

# config/elasticsearch-plugins.yml
plugins:
  - id: analysis-icu
  - id: custom-plugin
    location: https://example.com/my-plugin-9.5.0.zip
  - id: maven-plugin
    location: org.example:my-plugin:1.0.0
  - id: proxied-plugin
    location: https://example.com/plugin.zip
    proxy: https://proxy.internal:3128
proxy: https://proxy.internal:3128

Manual management:

bin/elasticsearch-plugin install analysis-icu
bin/elasticsearch-plugin install https://example.com/plugin.zip
bin/elasticsearch-plugin remove analysis-icu
bin/elasticsearch-plugin list

4. Deployment & Scaling

4.1 Distribution Packages

Elasticsearch ships in three packaging families from distribution/:

Package Description
Docker image docker.elastic.co/elasticsearch/elasticsearch:9.5.0 — bundled JDK, CA/cert bootstrap, auto-config on first start. Preferred for containerized platforms.
.deb / .rpm systemd units, /etc/elasticsearch/ config, /var/lib/elasticsearch/ data, standard Linux service lifecycle.
.tar.gz / .zip Self-contained directory. Good for developers and air-gapped environments.

4.2 Single-Node Development

# elasticsearch.yml
cluster.name: dev-local
node.name: dev-node-1
discovery.type: single-node
xpack.security.enabled: false

Start: bin/elasticsearch (foreground) or bin/elasticsearch -d -p /var/run/elasticsearch.pid (daemon).

4.3 Multi-Node Cluster Bootstrap

Step 1 — Prepare elasticsearch.yml on each node:

cluster.name: prod-logs
node.name: es-master-01          # unique per node
node.roles: [master]
network.host: 10.0.1.11          # unique per node
discovery.seed_hosts:
  - "10.0.1.11"
  - "10.0.1.12"
  - "10.0.1.13"
cluster.initial_master_nodes:
  - "es-master-01"
  - "es-master-02"
  - "es-master-03"
bootstrap.memory_lock: true
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.keystore.path: certs/transport.p12

Step 2 — Generate certificates (on one node, copy to all):

bin/elasticsearch-certutil ca --out elastic-stack-ca.p12 --pass
bin/elasticsearch-certutil cert \
  --ca elastic-stack-ca.p12 \
  --dns es-master-01,es-master-02,es-master-03 \
  --out bundle.zip

Step 3 — Start all nodes, verify cluster formation:

curl -sk -u elastic:$PASSWORD https://es-master-01:9200/_cluster/health?pretty

Step 4 — Remove cluster.initial_master_nodes from all configs and do a rolling restart.

4.4 Docker Compose (Three-Node + Kibana)

The reference compose topology from the source tree:

version: "3.8"
services:
  setup:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
    user: "0"
    command: >
      bash -c '
        bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip &&
        unzip config/certs/ca.zip -d config/certs &&
        bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip \
          --in config/certs/instances.yml \
          --ca-cert config/certs/ca/ca.crt \
          --ca-key config/certs/ca/ca.key &&
        unzip config/certs/certs.zip -d config/certs
      '

  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    environment:
      - node.name=es01
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01,es02,es03
      - discovery.seed_hosts=es02,es03
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es01/es01.key
      - xpack.security.http.ssl.certificate=certs/es01/es01.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es01/es01.key
      - xpack.security.transport.ssl.certificate=certs/es01/es01.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - ${ES_PORT}:9200

  kibana:
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    environment:
      - SERVERNAME=kibana
      - ELASTICSEARCH_HOSTS=https://es01:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
    ports:
      - ${KIBANA_PORT}:5601
    volumes:
      - certs:/usr/share/kibana/config/certs

volumes:
  certs:
  esdata01:
  esdata02:
  esdata03:

4.5 Kubernetes (ECK)

The officially supported operator is Elastic Cloud on Kubernetes (ECK). It manages:

  • Cluster lifecycle (create, scale, upgrade)
  • Rolling upgrades with health gate checks
  • TLS certificate generation and rotation
  • Storage volume management
  • Secret management for passwords and API keys

Minimum ECK manifest for a 3-node cluster:

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: prod-logs
spec:
  version: 9.5.0
  nodeSets:
  - name: masters
    count: 3
    config:
      node.roles: ["master", "voting_only"]
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 20Gi
        storageClassName: standard
  - name: hot-data
    count: 3
    config:
      node.roles: ["data_hot", "data_content", "ingest"]
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 500Gi
        storageClassName: fast-nvme

For bare StatefulSets without ECK, you must manually handle cert rotation, Pod Disruption Budgets, anti-affinity rules, and node naming stability.

4.6 Data Tiers

Elasticsearch supports five data tiers. Each tier is a node.roles value; indices target a tier via index.routing.allocation.include._tier_preference.

Tier Role Storage Use
Content data_content Fast SSD Non-time-series: catalogs, user data
Hot data_hot NVMe Current time-series writes
Warm data_warm SATA SSD Recent, less-frequently queried
Cold data_cold Spinning or object storage Rarely queried historical data
Frozen data_frozen Object storage only Query-on-demand; searchable snapshots

Tier-aware index template:

PUT /_index_template/logs-app
{
  "index_patterns": ["logs-app-*"],
  "data_stream": {},
  "template": {
    "settings": {
      "index.lifecycle.name": "logs-policy",
      "index.routing.allocation.include._tier_preference": "data_hot"
    }
  }
}

4.7 ILM — Index Lifecycle Management

ILM automates the hot → warm → cold → frozen → delete progression:

PUT /_ilm/policy/logs-policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_age": "1d", "max_primary_shard_size": "50gb" },
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "2d",
        "actions": {
          "set_priority": { "priority": 50 },
          "readonly": {},
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "cold": {
        "min_age": "14d",
        "actions": {
          "searchable_snapshot": { "snapshot_repository": "found-snapshots" }
        }
      },
      "frozen": {
        "min_age": "30d",
        "actions": {
          "searchable_snapshot": { "snapshot_repository": "found-snapshots" }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": { "delete": {} }
      }
    }
  }
}

ILM management commands:

GET /_ilm/explain/{index}          # Current phase, action, step
POST /_ilm/retry/{index}           # Retry after a failed step
POST /_ilm/move/{index}            # Manually advance to a specific step
POST /_ilm/stop                    # Pause ILM globally
POST /_ilm/start                   # Resume ILM
GET /_ilm/status                   # ILM running/stopped state

4.8 Data Stream Lifecycle (Simpler Alternative to ILM)

For pure retention + downsampling without the full ILM state machine:

PUT /_data_stream/logs-app-prod/_lifecycle
{
  "data_retention": "90d",
  "downsampling": [
    { "after": "7d",  "fixed_interval": "1h" },
    { "after": "30d", "fixed_interval": "1d" }
  ]
}

4.9 Snapshots and Restore

Snapshots are the only supported backup mechanism. Do not copy shard directories off a live cluster.

Register a Repository

S3:

PUT /_snapshot/my-s3
{
  "type": "s3",
  "settings": {
    "bucket": "my-es-snapshots",
    "base_path": "prod",
    "client": "default",
    "server_side_encryption": true
  }
}

S3 credentials in the keystore:

bin/elasticsearch-keystore add s3.client.default.access_key
bin/elasticsearch-keystore add s3.client.default.secret_key
curl -u elastic:$P https://localhost:9200/_nodes/reload_secure_settings -X POST

Azure:

PUT /_snapshot/my-azure
{
  "type": "azure",
  "settings": {
    "container": "my-es-snapshots",
    "base_path": "prod"
  }
}

GCS:

PUT /_snapshot/my-gcs
{
  "type": "gcs",
  "settings": {
    "bucket": "my-es-snapshots",
    "base_path": "prod",
    "client": "default"
  }
}

Shared filesystem (NFS — prefer object storage):

PUT /_snapshot/my-fs
{
  "type": "fs",
  "settings": {
    "location": "/mnt/snapshots/prod",
    "compress": true
  }
}

Supported repository types: fs, url (read-only), s3, azure, gcs, hdfs (plugin), source (source-only, compressed archival).

SLM — Automated Snapshot Policies

PUT /_slm/policy/daily-snapshots
{
  "schedule":   "0 30 1 * * ?",
  "name":       "<daily-snap-{now/d}>",
  "repository": "my-s3",
  "config": {
    "indices":              ["*"],
    "include_global_state": true,
    "ignore_unavailable":   true
  },
  "retention": {
    "expire_after": "30d",
    "min_count":    5,
    "max_count":    50
  }
}

Restore

POST /_snapshot/my-s3/daily-snap-2026-04-22/_restore
{
  "indices":              "logs-app-*",
  "rename_pattern":       "(.+)",
  "rename_replacement":   "restored-$1",
  "include_global_state": false
}

Searchable Snapshots

Cold/frozen indices can be searchable snapshots — data lives only in the repository and is read on demand. ILM integrates this via the searchable_snapshot action. No replicas are stored locally, so capacity per node can be enormous. The x-pack/plugin/blob-cache/ (56K chars) provides the local disk cache layer for repeated access.

4.10 Cross-Cluster Search (CCS)

Register remote clusters:

PUT /_cluster/settings
{
  "persistent": {
    "cluster.remote.eu-prod.mode":          "proxy",
    "cluster.remote.eu-prod.proxy_address": "eu-prod-proxy.example.com:9400",
    "cluster.remote.us-logs.mode":          "sniff",
    "cluster.remote.us-logs.seeds":         ["us-logs-1.example.com:9300"]
  }
}
  • Sniff mode — discovers all nodes in the remote cluster and opens direct connections. Requires transport port access.
  • Proxy mode — multiplexes all traffic through a single proxy address. Required for cross-VPC/cross-k8s traffic without exposing the transport port.

Search across clusters:

GET /eu-prod:logs-*,us-logs:logs-*,logs-*/_search
{
  "query": { "match": { "message": "error" } }
}

Use ?ccs_minimize_roundtrips=true (the default) — fetches only the final top-N docs from remote clusters, reducing round trips significantly.

4.11 Cross-Cluster Replication (CCR)

CCR streams writes from a leader index to a follower in another cluster in near-real-time.

Use cases:

  • Disaster recovery — follower in a secondary region; promote on primary failure.
  • Read scaling — local followers in app-adjacent regions serve local read traffic.
  • Aggregation — centralize indices from many leader clusters into a single analysis cluster.

Create a follower:

PUT /logs-app-follower/_ccr/follow
{
  "remote_cluster":    "eu-prod",
  "leader_index":      "logs-app-000001",
  "settings":          { "index.number_of_replicas": 0 }
}

Follower indices are read-only on the follower side. To promote (e.g., on DR failover):

POST /logs-app-follower/_ccr/pause_follow
POST /logs-app-follower/_ccr/unfollow

5. Operations

5.1 Health Monitoring

GET /_cluster/health
GET /_cluster/health?level=indices
GET /_health_report                           # richer multi-dimensional health (9.x)
GET /_cluster/state?filter_path=metadata.indices.*.state

Health status:

Status Meaning
green All primary and replica shards allocated
yellow All primaries allocated; at least one replica unassigned
red At least one primary unassigned — reads/writes to affected indices fail

Key diagnostic endpoints:

Endpoint Purpose
GET /_cluster/allocation/explain Explains why a shard is unassigned
GET /_cat/nodes?v Node list with load, heap, roles
GET /_cat/indices?v&s=store.size:desc All indices sorted by size
GET /_cat/shards?v&h=index,shard,prirep,state,node Per-shard state
GET /_cat/allocation?v Disk usage and shard count per node
GET /_cat/thread_pool?v&h=node_name,name,active,queue,rejected Thread pool saturation
GET /_cat/recovery?v&active_only=true In-progress shard recovery
GET /_cat/pending_tasks?v Queued cluster-state tasks
GET /_nodes/stats Full per-node stats (JVM, threadpools, indices, OS, process, transport, HTTP)
GET /_nodes/hot_threads?threads=5 Top CPU consumers across all nodes

Tip

Use GET /_cat/thread_pool?v&h=node_name,name,active,queue,rejected as your first diagnostic when users report search or indexing slowness. A rising rejected count on the search or write pool is the earliest measurable signal of capacity pressure — it appears before latency visibly degrades.

5.2 Rolling Restart (Zero Downtime)

  1. Disable replica allocation to prevent unnecessary shard movement:

    PUT /_cluster/settings
    { "persistent": { "cluster.routing.allocation.enable": "primaries" } }
    
  2. Stop indexing if possible, then flush:

    POST /_flush
    
  3. Stop the target node, upgrade/patch it, start it back.

  4. Wait until the node rejoins:

    GET /_cat/nodes?v
    
  5. Re-enable allocation:

    PUT /_cluster/settings
    { "persistent": { "cluster.routing.allocation.enable": null } }
    
  6. Wait for green health:

    GET /_cluster/health?wait_for_status=green&timeout=5m
    
  7. Move to the next node.

Note

Always upgrade/restart data nodes first, master-eligible nodes last to preserve quorum throughout the window. After re-enabling allocation, peer recovery will replay translog entries and reallocate replicas — throttle recovery bandwidth with indices.recovery.max_bytes_per_sec if the cluster serves production traffic during the window.

5.3 Decommissioning a Node

Route all shards off the node before stopping it:

PUT /_cluster/settings
{ "persistent": { "cluster.routing.allocation.exclude._name": "es-warm-09" } }

Monitor until 0 shards remain:

GET /_cat/allocation?v

For a master-eligible node, also add to voting-config exclusions before stopping:

POST /_cluster/voting_config_exclusions?node_names=es-master-02&timeout=1m

After decommissioning, clear the exclusion:

DELETE /_cluster/voting_config_exclusions

5.4 Graceful Shutdown API (9.x)

The shutdown API allows controlled node removal with proper wait logic:

PUT /_nodes/<node_id>/shutdown
{
  "type":             "remove",     # or "restart", "replace"
  "reason":           "AZ drain 2026-04-23",
  "target_node_name": "es-warm-99"  # for "replace" type
}

Monitor shutdown progress:

GET /_nodes/<node_id>/shutdown

5.5 Shard Shrink, Split, and Reindex

Shrink (reduce shard count — target must divide evenly):

# First: mark source read-only and route to a single node
PUT /source-index/_settings
{
  "index.blocks.write": true,
  "index.routing.allocation.require._name": "es-hot-01"
}
# Then: shrink
POST /source-index/_shrink/target-index
{ "settings": { "index.number_of_shards": 1 } }

Split (increase shard count — target must be a multiple of source):

PUT /source-index/_settings
{ "index.blocks.write": true }
POST /source-index/_split/target-index
{ "settings": { "index.number_of_shards": 4 } }

Reindex (arbitrary shard count change or cross-cluster copy):

POST /_reindex
{
  "source": { "index": "old-index" },
  "dest":   { "index": "new-index" }
}

5.6 Recovering a Lost Master Quorum (Last Resort)

If you permanently lose a majority of master-eligible nodes with their data:

# On the surviving node with the most recent cluster state:
bin/elasticsearch-node unsafe-bootstrap --confirm

Caution

unsafe-bootstrap forms a new cluster from a single node. Any cluster-state changes that were applied on the lost masters but not yet acknowledged by this node are permanently lost. This includes new indices, mapping changes, security configs, and ILM policies added since the last successful publication. Always snapshot first. Restore from snapshots after bootstrapping if necessary.

5.7 Fixing Flood-Stage Write Block

When the flood-stage watermark is hit, all indices become read-only:

  1. Free disk first (delete old indices, add nodes, expand volumes).

  2. Clear the write block:

    PUT /_all/_settings
    { "index.blocks.read_only_allow_delete": null }
    

Running the clear before freeing disk will cause the block to re-apply immediately.

5.8 Force Merge

POST /my-index/_forcemerge?max_num_segments=1

Caution

Only run _forcemerge on read-only indices (ILM warm/cold, closed index, write-blocked). Force-merging active write indices destroys the merge policy geometry, can cause large GC spikes, and the I/O can saturate the node. max_num_segments=1 is appropriate only for truly static data.

5.9 APM and Telemetry

telemetry.agent.server_url: https://apm-server.internal:8200
telemetry.secret_token: secret-token-here        # or telemetry.api_key
telemetry.tracing.enabled: true
telemetry.metrics.enabled: true

APM tracing emits spans for search, indexing, and cluster-state operations. Useful for tracing slow queries from the ES side rather than the client side.

5.10 Prometheus Metrics

The x-pack/plugin/prometheus/ plugin exposes a Prometheus-compatible scrape endpoint. After installing or enabling the plugin, metrics are served at GET /_prometheus/metrics. This covers node stats, index stats, JVM, thread pools, circuit breakers, and more in Prometheus exposition format.

5.11 Monitoring Stack Options

Approach How
Elastic Stack monitoring Metricbeat / Elastic Agent collects _nodes/stats + _cluster/stats into a dedicated monitoring cluster; view in Kibana Stack Monitoring
Prometheus + Grafana Scrape /_prometheus/metrics or use a community exporter
Watcher alerts Schedule watches against cluster health endpoints for self-contained alerting

5.12 Upgrade Procedures

Supported Upgrade Paths

  • Minor upgrades (e.g., 9.4 → 9.5): rolling, online, no reindex required.
  • Major upgrades (e.g., 8.x → 9.x): rolling upgrade from the last minor of the previous major (e.g., 8.19 → 9.0 directly). Earlier minors require upgrading to the last minor first.
  • Index compatibility: indices created in version N are compatible with N and N+1 major only. Indices older than that must be reindexed or retired before a major upgrade.

Check readiness:

GET /_migration/deprecations

Rolling Upgrade Procedure (Minor)

  1. Read release notes and resolve critical deprecations (GET /_migration/deprecations).
  2. Disable shard allocation for replicas: cluster.routing.allocation.enable: primaries.
  3. For each node: stop → upgrade package/image → start → wait for rejoin.
  4. Re-enable allocation after each node, wait for green.
  5. Upgrade master-eligible nodes last.
  6. Verify: GET / returns the new version on all nodes.

Major Upgrade Notes

  • Run mixed major versions for as short a window as possible. The cluster tolerates current major and current major - 1 simultaneously during the rolling window.
  • Test against the Upgrade Assistant in Kibana (reads _migration/deprecations and guides through remediation) well before the maintenance window.
  • Snapshot the cluster before starting.

6. Performance

6.1 Indexing Optimization

Bulk requests:

  • Target 5–15 MB per bulk body and hundreds to low thousands of documents per request.
  • Monitor rejected on the write thread pool; back off with exponential retry on 429.
  • Use async bulk clients (the Elasticsearch Java client, Python async client) to pipeline requests.

Refresh interval:

PUT /my-index/_settings
{ "index.refresh_interval": "30s" }

Set to 30s or -1 during initial bulk loads; return to 1s (or the default) afterward.

Translog durability:

PUT /my-index/_settings
{ "index.translog.durability": "async" }

async batches fsyncs to disk (interval controlled by index.translog.sync_interval: 5s). Throughput can improve 2× at the cost of up to sync_interval of data on a hard crash. Acceptable for log ingestion; not for financial transactions.

Replicas during bulk load:

PUT /my-index/_settings
{ "index.number_of_replicas": 0 }
# ... bulk index ...
PUT /my-index/_settings
{ "index.number_of_replicas": 1 }

Auto-generated IDs:

Let Elasticsearch assign _id when possible — avoids a "does this document exist?" lookup on the shard, saving one read per document.

Segment merges:

Do not over-force-merge. One _forcemerge?max_num_segments=1 at the end of a bulk load on a static dataset is fine. Continuous or repeated force-merging is pathological.

Codec:

PUT /_index_template/logs-compressed
{
  "template": {
    "settings": { "index.codec": "best_compression" }
  }
}

best_compression (zstd) reduces storage 10–30% at a modest CPU cost. Best for warm/cold indices where search speed matters less.

6.2 Search Optimization

Filters vs. queries:

Structure-matched constraints (term, range, exists) in filter context — they are cached and skip relevance scoring:

{
  "query": {
    "bool": {
      "filter": [
        { "term":  { "status": "active" } },
        { "range": { "@timestamp": { "gte": "now-1h" } } }
      ],
      "must": [
        { "match": { "message": "error" } }
      ]
    }
  }
}

Routing:

If your access pattern is always tenant=X, route documents by tenant:

POST /my-index/_doc?routing=tenant-acme
{ ... }

GET /my-index/_search?routing=tenant-acme
{ ... }

Queries with the matching routing only hit the one shard that holds that tenant's data.

Shard request cache:

On by default. Effective for size: 0 (aggregation-only) queries against immutable data. Invalidated on every refresh — raise index.refresh_interval to extend cache lifetime for static data.

Field data and keyword fields:

Avoid sorting or aggregating on text fields — fielddata is disabled by default and loads the entire column into heap. Use a keyword sub-field:

{
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "fields": { "keyword": { "type": "keyword" } }
      }
    }
  }
}

Pagination patterns:

Method Best for
from + size First few pages of small result sets (≤ max_result_window = 10000)
search_after + PIT Deep pagination, large result sets, stable cursor across refreshes
Scroll (legacy) Deprecated; use search_after + PIT for new code

Point-in-time (PIT):

POST /my-index/_pit?keep_alive=5m
# Returns: { "id": "PIT_ID" }

GET /_search
{
  "pit": { "id": "PIT_ID", "keep_alive": "5m" },
  "sort": [{ "@timestamp": "desc" }, { "_shard_doc": "asc" }],
  "search_after": [<last_sort_values>]
}

preference=_local:

For latency-sensitive queries from coordinating nodes that also hold shards, use ?preference=_local to avoid a network hop.

6.3 Memory and GC Tuning

  • Heap: 50% of RAM, max ~30.5 GB. Never exceed this.
  • G1GC (-XX:+UseG1GC) is the default. -XX:InitiatingHeapOccupancyPercent=30 triggers GC sooner to avoid long pauses.
  • -XX:+AlwaysPreTouch pre-faults heap pages at startup, reducing GC latency spikes in early operation.
  • Keep the filesystem page cache large. Never run Elasticsearch on a node that also hosts a page-cache-heavy service (database, HDFS, etc.).
  • Disable swap: swapoff -a + vm.swappiness=1 + bootstrap.memory_lock: true.
  • Monitor GET /_nodes/stats/jvm for heap_used_percent. Sustained > 75% is a warning; > 85% needs action (add nodes or reduce load).

6.4 Circuit Breakers

Circuit breakers prevent OOM by tracking memory used by operations. Key breakers:

Breaker Default limit Purpose
parent 95% heap Overall limit; trips before heap exhaustion
request 60% heap Limits memory for a single request's data structures
fielddata 40% heap Limits fielddata (heap-resident column cache)
in_flight_requests 100% heap Limits in-flight HTTP request bodies
accounting 100% heap Tracks long-lived structures
eql_sequence 50% of request breaker EQL sequence computation limit
model_inference 50% of request breaker ML inference memory limit

Check breaker state:

GET /_nodes/stats/breaker

A circuit_breaking_exception means a single request exceeded the breaker limit. Fix by reducing request size, using filter rather than query, paginating with search_after, or raising the specific breaker limit carefully.

6.5 Dense Vector (kNN) Performance

  • Use dense_vector field type with dims matching your embedding model output.

  • For large corpora, use index: true and HNSW indexing (m and ef_construction parameters). Default m=16, ef_construction=100.

  • Query with knn inside _search:

    {
      "knn": {
        "field":          "embedding",
        "query_vector":   [0.1, 0.2, ...],
        "k":              10,
        "num_candidates": 100
      }
    }
    
  • num_candidates controls recall vs. speed trade-off per shard. Higher = more accurate, more CPU.

  • SIMD acceleration is provided by libs/simdvec/ on x86_64 (AVX2/AVX-512) and aarch64 (NEON). Requires a CPU that supports these; falls back to scalar otherwise.

6.6 Benchmarking

Use Rally (github.com/elastic/rally) — the official benchmarking harness. Built-in tracks include nyc_taxis, http_logs, geopoint, nested, eql, dense_vectors. Build custom tracks from a snapshot of your data and a representative query log.

For micro-benchmarks of core algorithms, benchmarks/ contains JMH benchmarks used by the core team.


7. Security

7.1 Security Model Overview

Security is on by default from 8.0 onward. A freshly started single node auto-configures:

  • TLS on both transport (9300) and HTTP (9200) layers.
  • A CA and per-node certificate.
  • A random password for the elastic superuser, printed to the terminal.
  • An enrollment token for Kibana and joining nodes.

Warning

Never store TLS passwords, API secrets, or LDAP bind passwords in elasticsearch.yml. These would be readable by any process with filesystem access. Always use bin/elasticsearch-keystore add for sensitive values. The keystore is encrypted at rest and only decrypted in memory by the running node.

7.2 Security Feature Toggles

Setting Default Description
xpack.security.enabled true Master switch for authentication, authorization, and audit
xpack.security.autoconfiguration.enabled true Auto-generates CA, certs, and passwords on first start
xpack.security.enrollment.enabled false (set true by autoconfig) Allow generating enrollment tokens
xpack.security.fips_mode.enabled false FIPS 140-2 compliance mode
xpack.security.fips_mode.required_providers — E.g., ["BCFIPS", "BCJSSE"]
xpack.security.hide_settings — Wildcard patterns of settings to redact from node info

7.3 TLS Configuration

Transport TLS (mandatory for multi-node)

xpack.security.transport.ssl.enabled:            true
xpack.security.transport.ssl.verification_mode:  certificate
xpack.security.transport.ssl.keystore.path:      certs/transport.p12
xpack.security.transport.ssl.truststore.path:    certs/transport.p12

Passwords in the keystore:

bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password
bin/elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password

HTTP TLS (client-facing)

xpack.security.http.ssl.enabled:            true
xpack.security.http.ssl.keystore.path:      certs/http.p12
xpack.security.http.ssl.keystore.type:      PKCS12

HTTP SSL settings reference:

Setting Description
xpack.security.http.ssl.keystore.path Path to .jks or .p12. Mutually exclusive with ssl.key.
xpack.security.http.ssl.keystore.type jks or PKCS12 (defaults from extension .p12, .pfx, .pkcs12).
xpack.security.http.ssl.keystore.secure_password Keystore password (in the ES keystore).
xpack.security.http.ssl.keystore.secure_key_password Key-entry password (in the ES keystore).
xpack.security.http.ssl.truststore.path Trusted-CA store path.
xpack.security.http.ssl.truststore.type Truststore format.
xpack.security.http.ssl.truststore.secure_password Truststore password (in the ES keystore).
xpack.security.http.ssl.certificate_authorities PEM CA cert list (alternative to truststore).
xpack.security.http.ssl.verification_mode none, certificate, full (default: full).
xpack.security.http.ssl.cipher_suites Explicit cipher suite list (default: JDK defaults).
xpack.security.http.ssl.supported_protocols E.g., ["TLSv1.3", "TLSv1.2"].

7.4 Certificate Management

# Generate a CA
bin/elasticsearch-certutil ca --out elastic-stack-ca.p12 --pass

# Generate a node certificate from the CA
bin/elasticsearch-certutil cert \
  --ca elastic-stack-ca.p12 \
  --dns node-1.example.com,node-2.example.com \
  --ip 10.0.1.11,10.0.1.12 \
  --out bundle.zip

# Interactive wizard for HTTP TLS
bin/elasticsearch-certutil http

# Generate a CSR for external CA signing
bin/elasticsearch-certutil csr \
  --name node-1 \
  --dns node-1.example.com \
  --out csr.zip

For Kubernetes/ECK, certificate management is fully automated by the operator.

7.5 Authentication Realms

Realms are authentication providers stacked in priority order under xpack.security.authc.realms:

Realm type Purpose Notes
native Built-in user database elastic, kibana_system, logstash_system, etc.
file Local flat-file users Managed with elasticsearch-users. Good for break-glass accounts.
ldap Corporate LDAP or Active Directory Supports user DN template and group-to-role mapping.
active_directory AD-specific wrapper Domain, Global Catalog, DnS domain settings.
pki Client certificate authentication Distinguished name mapped to role.
saml SAML 2.0 service provider Used for Kibana SSO via an IdP (Okta, Azure AD, Ping, etc.).
oidc OpenID Connect Used for Kibana SSO.
kerberos Kerberos/SPNEGO Enterprise SSO.
jwt Signed JWT bearer tokens Service-to-service auth; supports RSA and HMAC signing.

Example realm stack:

xpack.security.authc.realms:
  native:
    native1:
      order: 0
  ldap:
    corporate_ldap:
      order: 1
      url: "ldaps://ldap.example.com:636"
      bind_dn: "cn=elastic-bind,dc=example,dc=com"
      user_search:
        base_dn: "dc=example,dc=com"
      group_search:
        base_dn: "dc=example,dc=com"
      files:
        role_mapping: "config/role_mapping.yml"
  pki:
    pki1:
      order: 2
      certificate_authorities: ["certs/ca.crt"]

7.6 Authorization — Roles

Roles are sets of cluster privileges, index privileges, application privileges, and optional FLS/DLS.

PUT /_security/role/logs_reader
{
  "cluster": ["monitor"],
  "indices": [
    {
      "names":      ["logs-*"],
      "privileges": ["read", "view_index_metadata"],
      "field_security": {
        "grant":  ["@timestamp", "service", "message", "level"],
        "except": []
      },
      "query": "{\"term\":{\"tenant\":\"acme\"}}"
    }
  ]
}

Cluster privileges (common):

Privilege Access
all Full cluster access
monitor GET /_cluster/health, _nodes, _cat/*, _tasks
manage All monitor + manage cluster-level resources
manage_security Full security API access
manage_ilm ILM policy management
manage_slm SLM policy management
manage_ml ML job management
manage_transform Transform management
manage_watcher Watcher management
manage_ccr CCR management

Index privileges (common):

Privilege Access
all Full index access
read Search, get, explain, field caps
write Index, bulk, update, delete
create_doc Index only (no update/delete)
index Index + update
delete Delete documents
create Create index
delete_index Delete index
manage All index management operations
view_index_metadata Index info, mappings, settings, aliases

7.7 Users

# Create
POST /_security/user/alice
{
  "password":  "changeme",
  "roles":     ["logs_reader"],
  "full_name": "Alice Smith",
  "email":     "[email removed]"
}

# Change password
PUT /_security/user/alice/_password
{ "password": "newpassword" }

# Disable
PUT /_security/user/alice/_disable

# Delete
DELETE /_security/user/alice

7.8 API Keys

API keys are preferred for application credentials. They are scoped, expirable, and can be invalidated without rotating passwords.

# Create an API key
POST /_security/api_key
{
  "name":       "my-app-key",
  "expiration": "30d",
  "role_descriptors": {
    "my-app-role": {
      "indices": [{ "names": ["logs-*"], "privileges": ["read"] }]
    }
  }
}
# Response includes "id" and "api_key" (base64-encoded joined as id:api_key)

# Use in requests:
# Authorization: ApiKey <base64(id:api_key)>

# Invalidate
DELETE /_security/api_key
{ "id": "KEY_ID" }

# Query all active keys
GET /_security/api_key?username=alice

7.9 Service Accounts

Service accounts are non-user security principals for infrastructure services (Fleet, Kibana, Logstash). They use file-backed or index-backed tokens, not passwords.

bin/elasticsearch-service-tokens create elastic/fleet-server my-fleet-token
bin/elasticsearch-service-tokens list elastic/fleet-server
bin/elasticsearch-service-tokens delete elastic/fleet-server my-fleet-token

7.10 Audit Logging

xpack.security.audit.enabled: true
xpack.security.audit.logfile.events.include:
  - access_denied
  - authentication_failed
  - run_as_denied
  - tampered_request
  - connection_denied
  - anonymous_access_denied
xpack.security.audit.logfile.events.exclude: []

Audit logs emit as structured JSON to logs/<cluster>_audit.json. Fields include @timestamp, node.id, event.type, event.action, user.name, request.id, url.path, network.client.ip.

7.11 Field-Level and Document-Level Security (FLS/DLS)

FLS — restrict which fields a role can read:

"field_security": {
  "grant":  ["@timestamp", "service", "level"],
  "except": []
}

DLS — restrict which documents a role can read (via a query):

"query": "{\"term\":{\"tenant\":\"acme\"}}"

FLS/DLS are evaluated per-segment at query time. They add overhead proportional to the complexity of the DLS query. Avoid DLS on frequently-updated large indices.

7.12 FIPS Mode

xpack.security.fips_mode.enabled: true
xpack.security.fips_mode.required_providers:
  - "BCFIPS"
  - "BCJSSE"

Requires:

  • A FIPS 140-2 validated JVM (Bouncy Castle FIPS or an NSS-backed JDK).
  • All keystores must be BCFKS format (not PKCS12 or JKS).
  • Passwords must be stored in the ES keystore; the keystore itself must be BCFKS.
  • Build with elasticsearch.fips Gradle plugin for distribution-level FIPS compliance.

7.13 Network Isolation

Caution

Transport port (9300) is internal-only. Firewall it from the internet and from application networks. Only Elasticsearch nodes and monitoring sidecars should reach it. Never expose a cleartext port 9200 to the public internet. Always terminate TLS at Elasticsearch itself or at a re-encrypting load balancer — not a TLS-terminating proxy that communicates plaintext to ES.

Recommended network segmentation:

Internet
  → Reverse proxy (TLS termination, optional WAF)
  → Elasticsearch HTTP port 9200 (TLS, internal network only)

Application servers
  → Elasticsearch HTTP port 9200 (TLS, internal network only)

Elasticsearch nodes
  ↔ Transport port 9300 (TLS, cluster-internal network only, firewall from all other networks)

8. Developer Internals

8.1 Request Lifecycle — Write

  1. HTTP request arrives on a coordinating node's Netty HTTP pipeline.
  2. The REST handler decodes the request and dispatches to TransportBulkAction (or TransportIndexAction for single-doc writes).
  3. The coordinating node resolves the target index/alias, resolves aliases, and computes the shard per document using _routing (defaults to _id).
  4. Per shard, the write is forwarded to the primary shard's node over the transport layer.
  5. The primary applies the write to its Lucene IndexWriter and the translog.
  6. The primary fan-outs to replica shards, waits for wait_for_active_shards acknowledgments.
  7. The response returns to the coordinating node and then to the client.
  8. At the next refresh (periodic or explicit), the new segment becomes searchable.
  9. At flush, Lucene commits and the translog is truncated.
  1. HTTP request arrives on the coordinating node.
  2. Query is parsed and validated against the index mapping.
  3. The coordinating node builds a per-shard execution plan.
  4. Query phase: request fans out to one copy (primary or replica) of each target shard. Each shard executes the query locally, scores documents, and returns the top-N doc IDs + scores.
  5. Reduce phase: the coordinating node merges shard results, computing the global top-N.
  6. Fetch phase: the coordinating node requests the full _source (+ highlighted fields, inner hits) for the final top-N documents from their respective shards.
  7. The merged, fetched response is returned to the client.

Aggregations plug into steps 4 (shard-local partial reduce) and 5 (global reduce). ES|QL uses a different columnar engine that evaluates on the coordinator.

8.3 Coordinator — Quorum Details

Implemented in server/src/main/java/org/elasticsearch/cluster/coordination/:

Class Role
Coordinator Main coordination loop; runs election, publication
ElectionScheduler Times election attempts with configurable back-off
PreVoteCollector Runs a pre-vote round before a full election
Publication Manages the two-phase commit for a single cluster-state update
ClusterFormationFailureHelper Diagnoses and logs cluster-formation problems
FollowersChecker Master-side follower-health checking
LeaderChecker Follower-side leader-health checking
PeerFinder Discovers other master-eligible nodes at startup and on reconnect

8.4 Allocator Internals

server/src/main/java/org/elasticsearch/cluster/routing/allocation/:

Component Description
AllocationDecider Stack of deciders; each returns YES/NO/THROTTLE for a shard→node assignment
BalancedShardsAllocator Balancer that equalizes weighted metrics
DesiredBalanceComputer 9.x: async desired-balance solver decoupled from actual moves
ShardsAllocator Interface implemented by BalancedShardsAllocator
ExistingShardsAllocator Handles recovery of shards from existing copies on restart
DiskThresholdDecider Disk-watermark enforcement
AwarenessAllocationDecider Zone/rack awareness enforcement
FilterAllocationDecider require.*, include.*, exclude.* enforcement
ReplicaAfterPrimaryActiveAllocationDecider Prevents replica allocation before primary is active

8.5 Lucene Integration

  • Each shard holds its own IndexWriter (org.apache.lucene.index.IndexWriter), configured from IndexSettings.
  • Segments are mmap'd via org.apache.lucene.store.MMapDirectory — they are OS page-cached, not JVM-heap-cached. This is why keeping heap at only 50% of RAM is critical.
  • Custom codecs in server/src/main/java/org/elasticsearch/index/codec/:
    • BKD-tree numerics (doc_values for numeric fields).
    • Bitset compression for sorted set doc values.
    • Keyword ordinals (fast cardinality aggregations).
    • best_compression (zstd via Lucene90StoredFieldsFormat).
  • The index.codec setting maps to a registered Lucene codec factory.

8.6 Plugin System

Plugins extend the node via Plugin subclasses registered with the server. All plugins are loaded from the plugins/ directory at startup.

Key extension point interfaces (from server/src/main/java/org/elasticsearch/plugins/):

Interface What it adds
ActionPlugin REST endpoint handlers (RestHandler) and transport actions
AnalysisPlugin Custom tokenizers, token filters, char filters, analyzers
IngestPlugin Custom ingest processors
MapperPlugin Custom field types (Mapper, MetadataFieldMapper)
SearchPlugin Custom queries, suggesters, rescorers, aggregations, fetch sub-phases
DiscoveryPlugin Custom seed providers and network transports
RepositoryPlugin Custom snapshot repositories
ScriptPlugin Custom Painless contexts or script engines
ClusterPlugin Custom shard allocation deciders and collectors
PersistentTaskPlugin Background tasks managed by the cluster state
IndexStorePlugin Custom shard directory implementations

X-Pack features are all Plugin subclasses loaded exactly like third-party plugins. The x-pack/plugin/core/ module provides shared infrastructure for all X-Pack plugins.

8.7 Entitlement System (9.x)

libs/entitlement/ introduces a runtime capability system that replaces the legacy Java SecurityManager (removed in JDK 18+). Plugins declare what they are permitted to do in an entitlement-policy.yaml file:

# Example entitlement policy
entitlements:
  - type: network_socket
    description: "Allow TCP connections for custom transport"
  - type: file_read
    path: /etc/custom-plugin-config
  - type: file_write
    path: /var/run/custom-plugin

The entitlement agent instruments JDK methods at startup and enforces policies at runtime. Internal tooling ships with the build:

  • public-callers-finder — finds third-party calls to JDK methods that need entitlement.
  • securitymanager-scanner — finds code still using the old SecurityManager API.

8.8 Settings System Architecture

From server/src/main/java/org/elasticsearch/common/settings/:

  • Setting<T> — a typed, validated setting with metadata (property scope, default value, validator, updater).
  • Setting.Property — NodeScope (per node), IndexScope (per index), Dynamic (updatable without restart), Deprecated, Filterable (hidden from node info), Consistent (must be same across cluster).
  • AbstractScopedSettings — registry for all Setting instances. Validates incoming config, applies transactional updates.
  • ClusterSettings — NodeScope registry (cluster-dynamic and static node settings).
  • IndexScopedSettings — IndexScope registry.
  • Affix settings — wildcard settings like node.attr.* or s3.client.<name>.endpoint. Matched at registration and at runtime.
  • Setting key pattern — [-\w]+([.][-\w]+)* (from AbstractScopedSettings).
  • Archived settings — settings that are no longer recognized are prefixed with archived. to prevent startup failure while flagging for review.

8.9 REST API Architecture

The REST API surface is specified by 600 JSON files in rest-api-spec/src/main/resources/rest-api-spec/api/. Each file describes:

  • Endpoint name and handler class
  • HTTP methods and path variants
  • Path parameters and query parameters (type, required, default)
  • Request body schema reference
  • Documentation URL

These specs are consumed by:

  • The test harness (rest-api-spec YAML REST tests)
  • Client code generators (Java, Python, JavaScript, Go, Ruby, .NET, PHP, Rust)
  • The Elasticsearch documentation build

REST handlers implement RestHandler and are registered via ActionPlugin.getRestHandlers(). Transport actions implement TransportAction<Request, Response> and are the business logic layer invoked by REST handlers (and directly by inter-node transport calls).

8.10 Ingest Pipeline Architecture

Ingest pipelines run on ingest-capable nodes before documents are indexed. Pipeline execution in modules/ingest-common/:

  1. The write request specifies ?pipeline=my-pipeline or a default pipeline is set on the index.
  2. The coordinating node selects an ingest node and forwards the request.
  3. The ingest node runs the pipeline: each processor receives a IngestDocument and transforms it in place.
  4. On success, the transformed document is forwarded to the shard primary.
  5. On OnFailure, the failure handler pipeline runs.

Processor types available (45 built-in from modules/ingest-common/):

Data transformation: convert, gsub, trim, uppercase, lowercase, split, join, bytes, urldecode, html_strip, uri_parts, base64_encode, base64_decode

Extraction: grok, dissect, csv, kv, json, registered_domain, user_agent, network_direction

Enrichment: enrich (lookup from enrich policy), geoip (from modules/ingest-geoip/), ip_location

Routing: reroute, pipeline, drop

Field manipulation: set, append, remove, rename, dot_expander, date, date_index_name, fingerprint, community_id

Control flow: fail, foreach, script, set_security_user, sort

8.11 Painless Scripting

Painless (modules/lang-painless/) is Elasticsearch's built-in scripting language. It compiles to JVM bytecode via ASM. Key characteristics:

  • Sandboxed: only whitelisted methods and classes are accessible. The whitelist is defined in modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/*.txt files.
  • Statically typed: type inference with explicit type annotation optional.
  • Optimized for ES: built-in access to document fields, scoring context, aggregation state.
  • Contexts: filter, score, update, ingest, field, watcher_condition, agg_combine, agg_reduce, transform.

Example Painless script (scripted field):

{
  "script_fields": {
    "full_name": {
      "script": {
        "source": "doc['first_name'].value + ' ' + doc['last_name'].value"
      }
    }
  }
}

Stored scripts:

PUT /_scripts/my-script
{
  "script": {
    "lang": "painless",
    "source": "ctx._source.views += params.increment"
  }
}

8.12 Aggregation Framework

Aggregations are organized in three tiers:

Tier Type Examples
Bucket Group documents into buckets terms, date_histogram, range, filters, geohash_grid, nested, parent, sampler, composite
Metric Compute a value over a bucket avg, min, max, sum, cardinality, percentiles, stats, extended_stats, geo_centroid, weighted_avg, top_hits, scripted_metric
Pipeline Post-process other aggregations derivative, moving_avg, cumulative_sum, bucket_sort, bucket_selector, serial_diff, avg_bucket, max_bucket, min_bucket, sum_bucket, percentiles_bucket, stats_bucket, extended_stats_bucket (39 files, 74K chars in search/aggregations/pipeline/)

Pipeline aggregations (server/src/main/java/org/elasticsearch/search/aggregations/pipeline/) are evaluated at the coordinating node after all shards have returned their partial results, enabling multi-pass analysis (e.g., moving averages over a date histogram).


9. Integration Points

9.1 REST API Namespaces

The full 600-endpoint surface spans these namespaces:

Namespace Endpoints
Document Index, get, update, delete, bulk, mget, reindex, update_by_query, delete_by_query, termvectors
Search search, msearch, async_search, scroll, pit, knn, search_mvt, terms_enum, count, field_caps, rank_eval, explain, _query (ES|QL), sql
Indices CRUD, open/close, clone, shrink, split, rollover, downsample, flush, forcemerge, refresh, stats, segments, recovery, settings, mapping, aliases, blocks, templates, resolve, validate, analyze
Data Streams CRUD, promote, modify, lifecycle
Cluster health, health_report, state, settings, stats, pending_tasks, reroute, allocation/explain, voting_config_exclusions, remote/info
Nodes info, stats, hot_threads, usage, reload_secure_settings, shutdown
Cat All _cat/* endpoints (aliases, allocation, count, fielddata, health, indices, master, nodeattrs, nodes, pending_tasks, plugins, recovery, repositories, segments, shards, snapshots, tasks, templates, thread_pool, transforms, ml/*, component_templates, circuit_breakers)
Snapshot CRUD repos, CRUD snapshots, restore, verify, status, analyze
SLM CRUD policies, execute, status, stats
ILM CRUD policies, explain, move, retry, start, stop, status
Ingest CRUD pipelines, simulate, geoip databases, ip_location databases
Security user, role, role_mapping, api_key, oauth2/token, saml/, oidc/, privilege, service, cross_cluster/api_key, query/, enroll/, profile, settings
ML anomaly detection jobs, datafeeds, data frame analytics, trained models, inference, calendars, filters, categories, results, model snapshots, memory, start/stop operations, info, preview
Transform CRUD transforms, start, stop, reset, preview, stats, upgrade
Watcher CRUD watches, execute, activate, deactivate, ack, stats, start, stop, delete history
CCR follow, unfollow, pause, resume, forget, stats, info, auto_follow patterns
Rollup CRUD jobs, start, stop, caps, search, stats
Enrich CRUD policies, execute, stats
EQL search, get, get_status, delete
ES|QL _query (synchronous and async)
Connector CRUD connectors, sync_jobs, check_in, last_sync, cancel_sync
Inference CRUD inference endpoints, _inference/{task_type}/{id}
Query Rules CRUD rulesets, test
Synonyms CRUD synonym sets and rules
Fleet global_checkpoints, search, msearch
Searchable Snapshots mount, stats, cache_stats
Profiling flamegraph, stacktraces, topn_functions, status
Shutdown CRUD shutdown markers per node
Licensing get, update, trial, start_basic, start_trial, start_basic, post_start_basic, post_start_trial, delete
Features get, reset
Migration deprecations, system_features
Monitoring bulk
Autoscaling CRUD policies, decisions
Streams CRUD streams
Search Application CRUD search applications, render query, list
Dangling Indices list, import, delete
Text Structure find_structure, find_field_structure, find_message_structure, test_grok_pattern
Graph explore
Security (cross-cluster API keys) create, update, get, query, invalidate

9.2 Client Libraries

Official Elasticsearch clients use the rest-api-spec JSON files for code generation and test coverage:

Language Package
Java co.elastic.clients:elasticsearch-java
Python elasticsearch-py
JavaScript/TypeScript @elastic/elasticsearch
Go go-elasticsearch
Ruby elasticsearch-ruby
.NET NEST / Elasticsearch.Net
PHP elasticsearch-php
Rust elasticsearch-rs

All official clients implement:

  • Automatic node sniffing or static node list.
  • Round-robin or custom node selection.
  • Retry on node failures.
  • Sniff-on-failure.
  • Compression.
  • Connection pooling.

9.3 Kibana Integration

Kibana connects to Elasticsearch as the kibana_system built-in user. Its required index patterns:

Index / Pattern Purpose
.kibana* Kibana saved objects (dashboards, visualizations, index patterns)
.kibana_task_manager* Kibana task manager
.kibana_alerting* Kibana alerting state
.reporting* Kibana reporting
.fleet* Fleet (Elastic Agent) enrollment and policy data
.siem-signals* Security SIEM detection engine alerts
.lists* Security exception lists
metrics-*, logs-*, traces-* Elastic Agent data streams

The kibana_system user must have manage_security on the .kibana* indices for setup. In production, Kibana should connect to Elasticsearch over TLS using the cluster CA.

9.4 Logstash Integration

Logstash uses the logstash_system built-in user for monitoring and a separate API key (or logstash_writer role) for output writes. The Elasticsearch output plugin supports:

  • Bulk writes to data streams or legacy index patterns.
  • Retry with exponential backoff on 429.
  • ILM for automatic index rollover.
  • Pipeline (ingest pipeline) specification per output.
  • TLS verification against the cluster CA.

9.5 Elastic Agent and Fleet

Fleet is the central management plane for Elastic Agents. The Fleet server connects to Elasticsearch via a service account token:

bin/elasticsearch-service-tokens create elastic/fleet-server my-fleet-token

Agents enroll with Fleet Server and send data to Elasticsearch data streams (logs-*, metrics-*, traces-*) using API keys created by Fleet on enrollment. Data streams follow the ECS (Elastic Common Schema) naming convention.

9.6 Ingest Enrichment Pipelines

Enrich policies allow looking up data from an Elasticsearch index during ingest:

PUT /_enrich/policy/ip-to-geo
{
  "geo_match": {
    "indices":        "ip-ranges",
    "match_field":    "cidr",
    "enrich_fields":  ["country", "org", "asn"]
  }
}
POST /_enrich/policy/ip-to-geo/_execute

Then use the enrich processor in a pipeline:

PUT /_ingest/pipeline/enrich-geo
{
  "processors": [
    {
      "enrich": {
        "policy_name":     "ip-to-geo",
        "field":           "src_ip",
        "target_field":    "geo",
        "ignore_missing":  true
      }
    }
  ]
}

9.7 Watcher (Alerting)

Watcher runs scheduled watches that query Elasticsearch, evaluate conditions, and trigger actions:

PUT /_watcher/watch/low-disk-alert
{
  "trigger": {
    "schedule": { "interval": "5m" }
  },
  "input": {
    "http": {
      "request": {
        "host":   "localhost",
        "port":   9200,
        "path":   "/_cat/allocation?v&format=json",
        "scheme": "https",
        "auth": { "basic": { "username": "elastic", "password": "{{ctx.metadata.es_password}}" } }
      }
    }
  },
  "condition": {
    "script": {
      "source": "return ctx.payload.any(n -> Double.parseDouble(n.get('disk.percent', '0')) > 80)"
    }
  },
  "actions": {
    "slack_alert": {
      "slack": {
        "message": {
          "to":   ["#ops-alerts"],
          "text": "Disk usage exceeds 80% on at least one node"
        }
      }
    }
  }
}

9.8 Transforms

Transforms create summary indices from source data. Two modes:

Pivot transform (batch or continuous):

PUT /_transform/hourly-agg
{
  "source":  { "index": ["logs-app-*"] },
  "dest":    { "index": "logs-app-hourly" },
  "sync":    { "time": { "field": "@timestamp", "delay": "60s" } },
  "pivot": {
    "group_by": {
      "hour":    { "date_histogram": { "field": "@timestamp", "calendar_interval": "1h" } },
      "service": { "terms": { "field": "service.name" } }
    },
    "aggregations": {
      "event_count": { "value_count": { "field": "_id" } },
      "avg_duration": { "avg": { "field": "duration_ms" } },
      "p99_duration": { "percentiles": { "field": "duration_ms", "percents": [99] } }
    }
  }
}

Latest transform (takes the latest doc per entity):

PUT /_transform/latest-status
{
  "source": { "index": ["events-*"] },
  "dest":   { "index": "entity-latest-status" },
  "sync":   { "time": { "field": "@timestamp" } },
  "latest": {
    "unique_key": ["entity_id"],
    "sort":       "@timestamp"
  }
}

Troubleshooting Reference

Note

A systematic approach to Elasticsearch problems: (1) check GET /_cluster/health, (2) run GET /_cluster/allocation/explain for unassigned shards, (3) check GET /_cat/thread_pool?v&h=node_name,name,active,queue,rejected for saturation, (4) look at GET /_nodes/hot_threads?threads=5 for hot CPUs, (5) check logs in path.logs for ERROR/WARN entries.

Cluster Health

Symptom First command Common cause
Yellow cluster GET /_cluster/allocation/explain Replica unassigned due to disk watermark, node down, or awareness constraint
Red cluster GET /_cluster/allocation/explain Primary shard lost; node permanently gone
All shards unassigned after restart GET /_cluster/state cluster.initial_master_nodes misconfigured; stale voting config

Caution

A red cluster means at least one primary shard is unassigned. Reads and writes to the affected index will return errors. If the primary was on a permanently lost node, recover it with POST /_cluster/reroute using allocate_stale_primary — but this acknowledges potential data loss for writes that were on the lost node between the last flush and failure. Always snapshot before reroute commands on production clusters.

Node Won't Start — Bootstrap Checks

Check failure Fix
vm.max_map_count too low sysctl -w vm.max_map_count=262144
max file descriptors too low ulimit -n 65535 / LimitNOFILE=65535 in systemd
memory locking failed ulimit -l unlimited / LimitMEMLOCK=infinity
heap not equal Xms/Xmx Set both to the same value
TLS required for multi-node Set xpack.security.transport.ssl.enabled: true

Rejected Execution Exceptions

GET /_cat/thread_pool?v&h=node_name,name,active,queue,rejected

A rising rejected count on write = bulk queue full. On search = search queue full.

Warning

Raising queue_size only delays the problem — it increases memory pressure and hides the real capacity issue. Prefer client-side retry with exponential backoff and horizontal scaling over larger queue sizes.

Fix: add nodes, slow the ingest rate, or use async bulk clients that honor back-pressure.

Circuit Breaker Exceptions

circuit_breaking_exception in the response means a request exceeded a memory breaker. The error message names the specific breaker.

Breaker tripped Common cause Fix
parent Overall heap pressure Add nodes; reduce size in queries
request Single large query/aggregation Use filter context; use search_after; reduce size
fielddata Sorting/aggregating on a text field Use keyword sub-field; disable fielddata
in_flight_requests Very large request bodies Reduce bulk size; compress requests

Slow Queries

  1. Enable slow log (see section 3.11).

  2. Check GET /_nodes/hot_threads?threads=5 for hot search threads.

  3. GET /_tasks?actions=*search*&detailed=true for in-flight slow searches.

  4. Use the Profile API:

    POST /my-index/_search
    { "profile": true, "query": { ... } }
    

    The profile response shows time breakdown per query clause and aggregation phase.

Disk Full — Flood Stage

When the flood-stage watermark is hit, all indices become read_only_allow_delete:

  1. Free disk (delete old indices, expand volumes, add nodes).

  2. Clear the block:

    PUT /_all/_settings
    { "index.blocks.read_only_allow_delete": null }
    

Recovery Too Slow

GET /_cat/recovery?v&active_only=true

Raise recovery bandwidth:

PUT /_cluster/settings
{ "persistent": { "indices.recovery.max_bytes_per_sec": "200mb" } }

Default is 40mb — conservative. Raise temporarily during deliberate rebalancing.

Shard Corruption

A red shard that cannot recover may be corrupted. Last resort:

bin/elasticsearch-shard remove-corrupted-data

This deletes the corrupted segment and forces recovery from a replica. If no replica exists, data is lost.

Split Brain Recovery

If you have two independent clusters with the same cluster.name after a partition (extremely rare with ≥ 3 masters), the path to recovery is:

  1. Stop all nodes.
  2. Identify which side has the most recent data (compare GET /_cluster/state?filter_path=metadata.cluster_uuid,metadata.version).
  3. On the "winner" nodes, proceed normally.
  4. On the "loser" nodes, use bin/elasticsearch-node detach-cluster then add them back to the winner cluster as fresh joiners (they will receive cluster state from the master).

25+ Common Issues and Resolutions

# Issue Diagnosis command Resolution
1 Red cluster GET /_cluster/allocation/explain Restore from snapshot or allocate_stale_primary
2 Yellow cluster (single-node) GET /_cluster/health Set number_of_replicas: 0 on single-node clusters
3 Bootstrap check failure Node logs at startup Fix specific OS/JVM parameter (see table above)
4 master_not_discovered_exception Node logs Check discovery.seed_hosts, network reachability, firewall on port 9300
5 Write rejected _cat/thread_pool Slow ingest, add nodes, increase write queue size (last resort)
6 Search rejected _cat/thread_pool Slow queries, add nodes, increase search queue size (last resort)
7 Circuit breaker on search Error message Reduce size, use filter context, paginate with search_after
8 Circuit breaker on fielddata Error message Use keyword sub-field instead of aggregating on text
9 Slow searches _nodes/hot_threads, slow log Profile, add filter context, use routing, check shard count
10 Disk watermark reached _cat/allocation Delete data, add nodes, adjust watermark, use ILM/searchable snapshots
11 Flood stage — write blocked _cat/indices (all read_only) Free disk, then clear read_only_allow_delete: null
12 Recovery too slow _cat/recovery Raise indices.recovery.max_bytes_per_sec
13 Shard too large _cat/shards Use rollover, reduce max_primary_shard_size in ILM
14 Too many shards _cat/nodes (heap warning) Shrink, reindex, reduce shard count in templates
15 Mapping explosion Mapping size grows unbounded Set index.mapping.total_fields.limit, use dynamic: strict
16 Old indices can't be upgraded _migration/deprecations Reindex to current version before major upgrade
17 TLS cert expired TLS handshake errors in logs Rotate certs with elasticsearch-certutil, update keystore
18 elastic password lost Cannot login bin/elasticsearch-reset-password -u elastic
19 Keystore password lost Node won't start No recovery; regenerate keystore from secrets management
20 Node stuck in INITIALIZING _cat/shards Check source shard; may be waiting for throttle to release
21 split_brain guard (quorum loss) Cluster read-only Restore quorum (add master nodes) or unsafe-bootstrap (data-loss risk)
22 Ingest pipeline failure GET /_ingest/pipeline/{name}/_simulate Check processor logic; add on_failure handler
23 ILM policy stuck GET /_ilm/explain/{index} Check step, retry with POST /_ilm/retry/{index}
24 Snapshot repository verification failure POST /_snapshot/{repo}/_verify Check bucket/container permissions, network access, credentials
25 CCS remote cluster unreachable GET /_remote/info Check proxy/seeds address, firewall on 9300/9400, TLS cert validity
26 ML job anomaly detection failing GET /_ml/anomaly_detectors/{job_id}/_stats Check node roles for ml, check heap availability, check datafeed
27 Transform failing GET /_transform/{id}/_stats Check reason field; inspect source index mapping changes

Quick Reference

Cluster Health One-Liners

curl -sk -u elastic:$P https://localhost:9200/_cluster/health?pretty
curl -sk -u elastic:$P https://localhost:9200/_cat/nodes?v
curl -sk -u elastic:$P https://localhost:9200/_cat/indices?v&s=store.size:desc
curl -sk -u elastic:$P https://localhost:9200/_cluster/allocation/explain?pretty
curl -sk -u elastic:$P https://localhost:9200/_cat/shards?v&h=index,shard,prirep,state,docs,node
curl -sk -u elastic:$P https://localhost:9200/_cat/thread_pool?v&h=node_name,name,active,queue,rejected
curl -sk -u elastic:$P https://localhost:9200/_cat/recovery?v&active_only=true
curl -sk -u elastic:$P https://localhost:9200/_nodes/hot_threads?threads=5
curl -sk -u elastic:$P https://localhost:9200/_health_report?pretty

Essential Environment Variables

Variable Meaning
ES_JAVA_HOME JDK to use. Defaults to bundled jdk/.
ES_JAVA_OPTS Extra JVM flags. Merged with config/jvm.options.
ES_PATH_CONF Config directory. deb/rpm default: /etc/elasticsearch.
ES_TMPDIR JVM temp directory. Default: $ES_HOME/tmp.
ES_HOME Install root. Used by scripts.
ELASTIC_PASSWORD Used by Docker entrypoint to set initial elastic password.

Key Ports

Port Role Access
9200 HTTP REST API (clients) Apps and operations — TLS-terminated
9300 Transport (inter-node) Cluster-internal only — firewall from apps and internet
5601 Kibana Behind SSO / corporate VPN
8200 APM Server (optional) Apps → APM

CLI Tools Summary

Tool Primary use
bin/elasticsearch Start the node
bin/elasticsearch-keystore Manage secure settings
bin/elasticsearch-users Manage file-realm users
bin/elasticsearch-reset-password Reset built-in user passwords
bin/elasticsearch-create-enrollment-token Generate enrollment tokens
bin/elasticsearch-certutil Generate CA and node TLS certificates
bin/elasticsearch-node Offline node maintenance (repurpose, unsafe-bootstrap, detach-cluster)
bin/elasticsearch-shard Offline shard repair (remove-corrupted-data)
bin/elasticsearch-plugin Install/remove plugins
bin/elasticsearch-service-tokens Manage service account tokens
bin/elasticsearch-sql-cli Interactive SQL shell
bin/elasticsearch-croneval Evaluate Cron expressions (Watcher)
bin/elasticsearch-syskeygen Generate Watcher system key

License Tiers

Tier Key features
Basic (free) Core engine, security (auth/authz/TLS/audit), ILM, SLM, data streams, kNN, ES|QL, SQL, monitoring, most Kibana features
Gold Alerting, advanced monitoring, LDAP/AD realm
Platinum Machine learning anomaly detection, CCR, SAML/OIDC/Kerberos, searchable snapshots (frozen tier), FIPS, Graph, custom roles
Enterprise All Platinum features + universal profiling, additional enterprise integrations

Version Reference

Branch Version Status
main 9.5.0 Current development
9.4 9.4.x Maintenance
9.3 9.3.x Maintenance
9.2 9.2.x Maintenance
8.19 8.19.x Legacy LTS