Removing a node from an Elasticsearch cluster moves shard copies away from a host before it leaves the data and coordination paths. The maintenance is common during host retirement, capacity reduction, and replacement work where search and indexing should continue while one node disappears.
For self-managed clusters, the standard data-node path is a temporary cluster allocation exclusion. The cluster keeps running while the allocator places shards on remaining nodes; master-eligible removals may also need a voting configuration exclusion when quorum would otherwise be lost.
Use an authenticated HTTPS endpoint that reaches a remaining node, and substitute the departing node's real node.name everywhere node-03 appears. Clear every temporary exclusion after the node is offline so later allocation and voting decisions are not constrained.
$ curl --silent --show-error --user "elastic:$ELASTIC_PASSWORD" "https://node-01:9200/_cat/nodes?v&h=ip,name,node.role,master" ip name node.role master 192.0.2.41 node-02 cdfhilmrstw * 192.0.2.43 node-03 cdfhilmrstw - 192.0.2.40 node-01 cdfhilmrstw -
Add --cacert /etc/elasticsearch/certs/http_ca.crt when the cluster CA is not in the host trust store. Use plain http only for clusters that are intentionally unsecured.
$ curl --silent --show-error --user "elastic:$ELASTIC_PASSWORD" "https://node-01:9200/_cluster/health?filter_path=cluster_name,status,timed_out,number_of_nodes,number_of_data_nodes,relocating_shards,initializing_shards,unassigned_shards,number_of_pending_tasks,active_shards_percent_as_number&pretty"
{
"cluster_name" : "search-cluster",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 3,
"number_of_data_nodes" : 3,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"active_shards_percent_as_number" : 100.0
}
Delay the removal if status is red, if primary shards are unassigned, or if cluster-state tasks are still backing up.
$ curl --silent --show-error --include --user "elastic:$ELASTIC_PASSWORD" --request POST "https://node-01:9200/_cluster/voting_config_exclusions?node_names=node-03&timeout=1m" HTTP/1.1 200 OK X-elastic-product: Elasticsearch content-type: text/plain; charset=UTF-8 content-length: 0
Skip this step for master-ineligible nodes and for routine one-at-a-time removals that leave more than half of the voting configuration online.
Related: How to set Elasticsearch voting configuration exclusions
$ curl --silent --show-error --user "elastic:$ELASTIC_PASSWORD" --header "Content-Type: application/json" --request PUT "https://node-01:9200/_cluster/settings" --data '{
"persistent": {
"cluster.routing.allocation.exclude._name": "node-03"
}
}'
{
"acknowledged" : true,
"persistent" : {
"cluster" : {
"routing" : {
"allocation" : {
"exclude" : {
"_name" : "node-03"
}
}
}
}
},
"transient" : { }
}
Use cluster.routing.allocation.exclude._id or cluster.routing.allocation.exclude._ip when a persistent node ID or IP address is safer than node.name. Dedicated master-only and coordinating-only nodes that hold no shards can skip the allocation exclusion.
$ curl --silent --show-error --user "elastic:$ELASTIC_PASSWORD" "https://node-01:9200/_cluster/health?wait_for_no_relocating_shards=true&timeout=30m&filter_path=cluster_name,status,timed_out,number_of_nodes,number_of_data_nodes,relocating_shards,initializing_shards,unassigned_shards,active_shards_percent_as_number&pretty"
{
"cluster_name" : "search-cluster",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 3,
"number_of_data_nodes" : 3,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"active_shards_percent_as_number" : 100.0
}
Increase timeout for large nodes, slow disks, or clusters with throttled recovery. Investigate allocation rules and free capacity if the request times out.
$ curl -sS -u "elastic:$ELASTIC_PASSWORD" "https://node-01:9200/_cat/allocation/node-03?v&h=shards,node" shards node 0 node-03
Do not stop a data node that still shows shard copies unless the maintenance plan accepts the recovery and availability impact.
$ sudo systemctl stop elasticsearch.service
Run this on the node being removed, not on the node used for the cluster API checks.
$ sudo systemctl disable elasticsearch.service Removed "/etc/systemd/system/multi-user.target.wants/elasticsearch.service".
Skip this step when the node is being stopped only for a temporary repair and must rejoin with the same identity.
$ curl --silent --show-error --user "elastic:$ELASTIC_PASSWORD" "https://node-01:9200/_cat/nodes?v&h=ip,name,node.role,master" ip name node.role master 192.0.2.41 node-02 cdfhilmrstw * 192.0.2.40 node-01 cdfhilmrstw -
$ curl --silent --show-error --user "elastic:$ELASTIC_PASSWORD" --header "Content-Type: application/json" --request PUT "https://node-01:9200/_cluster/settings" --data '{
"persistent": {
"cluster.routing.allocation.exclude._name": null
}
}'
{
"acknowledged" : true,
"persistent" : { },
"transient" : { }
}
Clearing the exclusion prevents it from blocking a replacement node that reuses the same node.name or address.
$ curl --silent --show-error --include --user "elastic:$ELASTIC_PASSWORD" --request DELETE "https://node-01:9200/_cluster/voting_config_exclusions?wait_for_removal=true" HTTP/1.1 200 OK X-elastic-product: Elasticsearch content-type: text/plain; charset=UTF-8 content-length: 0
Clusters should normally have an empty voting configuration exclusion list after maintenance ends.
$ curl --silent --show-error --user "elastic:$ELASTIC_PASSWORD" "https://node-01:9200/_cluster/health?filter_path=cluster_name,status,timed_out,number_of_nodes,number_of_data_nodes,relocating_shards,initializing_shards,unassigned_shards,number_of_pending_tasks,active_shards_percent_as_number&pretty"
{
"cluster_name" : "search-cluster",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 2,
"number_of_data_nodes" : 2,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"active_shards_percent_as_number" : 100.0
}