Step-by-Step Guide to Configuring a Basic Three-Node Cluster
Learn how to quickly set up a resilient, basic three-node Elasticsearch cluster. This step-by-step tutorial covers essential configuration in `elasticsearch.yml`, bootstrapping cluster discovery using `cluster.initial_master_nodes`, starting the services, and verifying health and shard replication across the nodes using practical cURL commands.
Step-by-Step Guide to Configuring a Basic Three-Node Cluster
A three-node Elasticsearch cluster is the smallest shape I would treat as a real cluster rather than a lab. It can elect a master by majority, keep replicas away from primaries, and survive one node going away if the data and roles are configured sensibly. It is still not magic. Three tiny VMs with full disks will not behave like a resilient search platform just because there are three of them.
This guide uses a basic modern Elasticsearch layout: three nodes, all master-eligible and data-capable, on private network addresses. That is a reasonable starting point for a small environment. Larger production deployments often separate dedicated master nodes, data tiers, ingest nodes, machine learning nodes, and coordinating-only nodes. Start simple here, then split roles when the workload justifies it.
The examples assume Linux hosts and package or archive installs. Adjust service commands for your environment.
Before you edit configuration
You need three separate hosts or VMs. Do not put three “nodes” on one laptop and call it highly available. They may be useful for testing discovery, but they share the same failure domain.
Each host needs:
- The same Elasticsearch version.
- A stable private IP or DNS name.
- Transport connectivity between nodes on port
9300by default. - HTTP access on port
9200from your admin host or load balancer, if needed. - Enough disk for primary and replica shards.
- Time sync through NTP or a similar service.
- A configured data path on reliable local or attached storage.
Recent Elasticsearch distributions include a bundled JDK. If your packaging or version requires an external JDK, use the supported Java version for that Elasticsearch release rather than guessing.
Use private addresses in discovery.seed_hosts. Avoid public IPs unless you have a very specific design and strong network controls.
For this guide, the nodes are:
node-1 10.0.10.11
node-2 10.0.10.12
node-3 10.0.10.13
Configure common cluster settings
On every node, edit elasticsearch.yml. The file location depends on installation method. Package installs commonly use /etc/elasticsearch/elasticsearch.yml; archive installs use config/elasticsearch.yml under the extracted directory.
Set the same cluster name on all three nodes:
cluster.name: my-three-node-cluster
Set discovery seed hosts on all three nodes:
discovery.seed_hosts:
- 10.0.10.11:9300
- 10.0.10.12:9300
- 10.0.10.13:9300
Set the initial master nodes for the first bootstrap only:
cluster.initial_master_nodes:
- node-1
- node-2
- node-3
The values must match node.name, not IP addresses, unless your node names are IP-like strings. This setting is only for forming a brand-new cluster. After the cluster has formed successfully, remove cluster.initial_master_nodes from all nodes and keep it out of future restarts. Leaving it around can cause confusion during disaster recovery or accidental re-bootstrap attempts.
Bind to the private interface or a safe host value:
network.host: 10.0.10.11
http.port: 9200
transport.port: 9300
Use each node’s own IP for network.host. 0.0.0.0 is convenient in examples, but in production it can expose Elasticsearch on interfaces you did not intend. If security auto-configuration is enabled in your version, you will also need to account for TLS certificates, enrollment, and authentication.
Configure each node name
On node 1:
node.name: node-1
network.host: 10.0.10.11
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
On node 2:
node.name: node-2
network.host: 10.0.10.12
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
On node 3:
node.name: node-3
network.host: 10.0.10.13
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
If you run multiple nodes on one machine for a test, use separate path.data and path.logs values. For real clusters, one node per host is usually the cleaner model.
Choose node roles
For a small three-node cluster, all three nodes can carry the standard roles:
node.roles: [ master, data, ingest, remote_cluster_client ]
This gives you three master-eligible nodes, so master election works by majority. With three master-eligible nodes, the cluster can lose one and still elect or keep a master. If two are gone, it cannot safely make cluster-state decisions.
For a larger cluster, dedicated master-eligible nodes are often better because heavy data or ingest work cannot starve master duties. That is a scaling refinement, not a requirement for this basic setup.
Check OS and network basics
Before starting Elasticsearch, test transport connectivity between every pair of nodes:
nc -vz 10.0.10.11 9300
nc -vz 10.0.10.12 9300
nc -vz 10.0.10.13 9300
Run those from each node where possible. Firewalls and cloud security groups must allow node-to-node transport traffic. HTTP port 9200 is for clients and admin calls; transport port 9300 is what cluster nodes use to talk to each other.
Also check file ownership on data and log directories. The Elasticsearch process must be able to write to both.
Start the nodes
For package installs with systemd:
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch
sudo journalctl -u elasticsearch -f
For archive installs during testing:
bin/elasticsearch
Start all three nodes. They do not have to start in a perfect order, but watch the logs. You want to see the cluster form once and nodes join it. If a node says it cannot discover a master, focus on node.name, cluster.name, discovery.seed_hosts, transport connectivity, and TLS/security settings.
Once the cluster forms, remove cluster.initial_master_nodes from every node’s config and restart later during a planned window if needed. Do not remove it while you are still trying to bootstrap for the first time.
Verify cluster health
From a host that can reach port 9200:
curl -s "http://10.0.10.11:9200/_cluster/health?pretty"
A brand-new cluster with no user indices may show green with zero shards. The fields to check are status, number_of_nodes, and number_of_data_nodes.
For a compact view:
curl -s "http://10.0.10.11:9200/_cat/health?v"
Then verify node membership:
curl -s "http://10.0.10.11:9200/_cat/nodes?v&h=ip,name,roles,master,cpu,heap.percent,ram.percent,disk.used_percent"
You should see all three nodes. One node will have the elected master marker. All three should show the roles you expect.
Create a test index with replicas
Create a test index to confirm shard placement:
curl -X PUT "http://10.0.10.11:9200/test-data-index?pretty" -H 'Content-Type: application/json' -d '{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}'
Check shards:
curl -s "http://10.0.10.11:9200/_cat/shards/test-data-index?v"
With three primary shards and one replica, you should see six shard copies spread across nodes. Elasticsearch will avoid placing a replica on the same node as its primary.
If the cluster is yellow, ask why:
curl -X GET "http://10.0.10.11:9200/_cluster/allocation/explain?pretty" -H 'Content-Type: application/json' -d '{}'
Common causes are not enough eligible data nodes, disk watermarks, disabled allocation, or allocation awareness rules that do not match your node attributes.
Test one-node failure behavior
In a non-production test, stop one node:
sudo systemctl stop elasticsearch
Check health from another node:
curl -s "http://10.0.10.12:9200/_cluster/health?pretty"
The cluster should still have a master because two of three master-eligible nodes remain. Depending on timing, shard relocation, and replica placement, health may be green or yellow while Elasticsearch reacts. Start the node again and watch recovery:
sudo systemctl start elasticsearch
curl -s "http://10.0.10.12:9200/_cat/recovery?v&active_only=true"
This test is worth doing before the cluster matters. It teaches you what normal recovery looks like, so a real incident is less surprising.
A few production guardrails
Enable and understand security for your Elasticsearch version. Do not expose an unauthenticated HTTP API to the internet or a broad internal network.
Take snapshots before you depend on the cluster. Replicas protect against node loss; snapshots protect against deletion, corruption, and operational mistakes.
Monitor disk usage, JVM heap pressure, node count, cluster health, pending tasks, and snapshot success. A three-node cluster is resilient only if it has enough capacity to recover.
Keep shard counts modest. Many small shards create overhead. A basic cluster can be overwhelmed by thousands of tiny indices even when the data volume is not large.
Finally, document the bootstrap settings and remove cluster.initial_master_nodes after formation. Most three-node setup problems come from small mismatches: node names that do not match bootstrap names, blocked transport ports, reused data directories from old clusters, or assumptions about security defaults. Check those first before changing more exotic settings.
Security notes for current Elasticsearch releases
Many modern Elasticsearch installations enable security features during setup. That means HTTP calls may require HTTPS, a CA certificate, and credentials. If your cluster was auto-configured with TLS, a health check may look more like this:
curl --cacert /etc/elasticsearch/certs/http_ca.crt \
-u elastic \
https://10.0.10.11:9200/_cluster/health?pretty
Do not disable security just to make a tutorial command work. Instead, adjust the examples for your certificate paths and service accounts. For production, create named users or API keys with the privileges they need instead of using the built-in superuser for daily work.
Transport TLS can also affect node joins. If nodes cannot join and the logs mention certificate trust, SAN mismatch, handshake failure, or remote transport errors, check certificates before changing discovery settings. A perfect discovery.seed_hosts list will not help nodes that reject each other during TLS handshake.
Common startup failures
If the nodes do not form a cluster, check the simple things first.
cluster.name must match on all nodes. A node with a different cluster name will not join just because it appears in the seed host list.
node.name must match the values used in cluster.initial_master_nodes during first bootstrap. If the config says node-1 but bootstrap lists es-node-1, discovery can stall.
The transport port must be reachable between nodes. HTTP access on 9200 is not enough. Use nc, security group inspection, or packet captures if needed.
Data directories must not contain metadata from an old cluster unless you intend to rejoin that exact cluster. Reusing a disk from a previous test can produce confusing errors about cluster UUIDs or unsafe bootstrap.
Memory and bootstrap checks matter when binding to a non-loopback address. Elasticsearch may enforce checks for file descriptors, virtual memory, memory locking, and discovery configuration. Read the startup log rather than retrying blindly.
After the cluster is working
Create a snapshot repository before the cluster carries important data. Replicas are not backups. A bad delete, mapping mistake, or application bug will replicate quickly to every copy.
Record the node names, IPs, roles, data paths, certificate locations, and bootstrap history in your runbook. During an outage, nobody wants to reverse-engineer whether node-2 is supposed to be master-eligible.
Set alerts for node loss, red health, long yellow health, disk watermarks, JVM heap pressure, failed snapshots, and frequent master elections. A three-node cluster gives you room to survive one failure, but only if you notice and repair it before the second failure.
Plan capacity with recovery in mind. If each node runs at very high disk usage, losing one node may leave too little space for replicas to rebuild. Healthy clusters need spare capacity, not just enough room for today’s primaries.
Rolling restart practice
Practice a rolling restart before you need one for a package upgrade. Restart one node, wait for it to rejoin, confirm health and recovery, then move to the next node. Do not restart all three nodes at once unless you are intentionally doing a full-cluster shutdown.
A simple sequence is:
sudo systemctl restart elasticsearch
curl -s "http://10.0.10.11:9200/_cat/nodes?v"
curl -s "http://10.0.10.11:9200/_cluster/health?pretty"
If the cluster has large shards, consider whether delayed allocation should be adjusted before planned restarts. The goal is to avoid unnecessary replica rebuilding when a node will be back in a few minutes. After maintenance, verify that allocation is enabled and temporary settings are removed.
Also test client behavior. Applications should use more than one Elasticsearch endpoint or a load balancer that removes failed nodes. A three-node cluster helps only if clients can reach the remaining healthy nodes when one node is down.
One last habit helps: keep a copy of the final elasticsearch.yml for each node in configuration management. Manual edits made during setup tend to drift, and drift is exactly what makes the next node replacement harder than it needs to be.