Removing master-eligible nodes without adjusting cluster coordination can strand an Elasticsearch cluster without enough votes to publish cluster-state changes or elect a master. Voting configuration exclusions are the manual safety control for maintenance windows where automatic shrink is not enough, such as shrinking the voting set below three nodes or taking half or more of the master-eligible nodes out of service in a short period.

Elasticsearch stores the current voting configuration in cluster metadata and updates it through the /_cluster/voting_config_exclusions API. When a POST request returns 200 OK, current Elastic docs guarantee that the specified node has been removed from the voting configuration and will not rejoin it until the exclusions are cleared.

Recent self-managed package installs usually expose HTTPS and authentication by default, so the curl examples may need https://, --cacert, and credentials instead of plain http. This API applies only to master-eligible nodes, the exclusion list persists until it is cleared, and the list is capped by cluster.max_voting_config_exclusions which defaults to 10, so clean up exclusions after the node is removed or when the maintenance window ends.

Steps to set Elasticsearch voting configuration exclusions:

  1. Check cluster health before changing the voting configuration.
    $ curl -sS "http://localhost:9200/_cluster/health?filter_path=cluster_name,status,number_of_nodes,number_of_data_nodes,number_of_pending_tasks&pretty"
    {
      "cluster_name" : "search-cluster",
      "status" : "green",
      "number_of_nodes" : 3,
      "number_of_data_nodes" : 3,
      "number_of_pending_tasks" : 0
    }

    Delay the maintenance if status is red, if a master is not elected, or if cluster-state tasks are still backing up.

  2. List the nodes and identify the exact node.name or persistent node ID for the master-eligible node to remove.
    $ curl -sS "http://localhost:9200/_cat/nodes?v&h=id,ip,node.role,master,name"
    id   ip         node.role   master name
    EsJ3 192.0.2.41 cdfhilmrstw *      node-01
    bPfA 192.0.2.42 cdfhilmrstw -      node-02
    5LCz 192.0.2.43 cdfhilmrstw -      node-03

    The compact node.role string contains m on master-eligible nodes. On clusters with more than three master-eligible nodes, removing fewer than half of them at once usually does not require this API because the voting configuration shrinks automatically.

    Use node_ids instead of node_names when the node name can change but the persistent ID is known.

  3. Review the current voting configuration exclusions before adding another entry.
    $ curl -sS "http://localhost:9200/_cluster/state?filter_path=metadata.cluster_coordination.voting_config_exclusions&pretty"
    {
      "metadata" : {
        "cluster_coordination" : {
          "voting_config_exclusions" : [ ]
        }
      }
    }

    Clusters should normally keep this list empty. The limit comes from cluster.max_voting_config_exclusions, which defaults to 10.

  4. Add the departing node to the voting configuration exclusions list.
    $ curl -sS -D - -o /dev/null -X POST "http://localhost: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

    Current Elastic docs state that an HTTP 200 OK response guarantees the node has been removed from the voting configuration. If the request times out or returns a different status, retry or resolve the cluster issue before stopping the node.

  5. Confirm the exclusion is recorded in cluster coordination metadata.
    $ curl -sS "http://localhost:9200/_cluster/state?filter_path=metadata.cluster_coordination.voting_config_exclusions&pretty"
    {
      "metadata" : {
        "cluster_coordination" : {
          "voting_config_exclusions" : [
            {
              "node_id" : "5LCzbbBUQsiIapX4IBIfBw",
              "node_name" : "node-03"
            }
          ]
        }
      }
    }

    The API also accepts comma-separated values so multiple nodes can be excluded together when the maintenance plan requires it.

  6. Stop the excluded Elasticsearch node only after the exclusion request succeeds.
    $ sudo systemctl stop elasticsearch

    Do not stop the node before the exclusion request returns 200 OK, and do not take down too many remaining master-eligible nodes or the cluster can lose quorum.

  7. Verify the remaining cluster still has an elected master.
    $ curl -sS "http://localhost:9200/_cat/master?v"
    id                     host       ip         node
    EsJ3x_b5Rjipi5HB219Rbg 192.0.2.41 192.0.2.41 node-01

    A populated row confirms that the remaining voters still elected a master after the excluded node left.

  8. Confirm the node count reflects the removal.
    $ curl -sS "http://localhost:9200/_cluster/health?filter_path=cluster_name,status,number_of_nodes&pretty"
    {
      "cluster_name" : "search-cluster",
      "status" : "green",
      "number_of_nodes" : 2
    }
  9. Clear the voting configuration exclusions after the excluded node has left the cluster.
    $ curl -sS -D - -o /dev/null -X DELETE "http://localhost: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

    wait_for_removal=true is the default and is the safer choice for permanent removal. Use wait_for_removal=false only when the node is expected to return or the exclusion was added in error.

  10. Verify the voting configuration exclusions list is empty again.
    $ curl -sS "http://localhost:9200/_cluster/state?filter_path=metadata.cluster_coordination.voting_config_exclusions&pretty"
    {
      "metadata" : {
        "cluster_coordination" : {
          "voting_config_exclusions" : [ ]
        }
      }
    }