Shard allocation filters are the controlled way to keep Elasticsearch shards on the right hardware, drain a node before maintenance, or confine data to a specific rack, zone, or tier without editing every index individually.
Cluster-wide filters are set through the dynamic /_cluster/settings API with cluster.routing.allocation.include, require, and exclude keys. They can match built-in node attributes such as _name, _host_ip, _publish_ip, _ip, _host, _id, and _tier, or custom node.attr.* values such as zone and rack, and they work together with per-index filters and allocation awareness.
Elastic recommends persistent cluster settings for routine allocation changes because transient overrides can disappear if the cluster becomes unstable. Shards only relocate when another node satisfies every routing rule and disk constraint, so an overly strict filter can leave shards UNASSIGNED instead of moving them. Secure clusters usually require https, authentication, and a trusted CA file, while Elastic Cloud Hosted and ECE deployments generally rely on tier-based placement rather than custom node attributes.
Steps to set shard allocation filters in Elasticsearch:
- Check cluster health before changing shard placement.
$ curl -sS "http://localhost:9200/_cluster/health?pretty" { "cluster_name" : "search-cluster", "status" : "green", "timed_out" : false, "number_of_nodes" : 3, "number_of_data_nodes" : 3, "active_primary_shards" : 12, "active_shards" : 24, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "unassigned_primary_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 }Resolve a red cluster, unassigned primary shards, or an ongoing master instability problem before applying a new allocation rule, otherwise relocation results become harder to interpret.
- List the current nodes and note the exact node name, IP, or tier target for the filter.
$ curl -sS "http://localhost:9200/_cat/nodes?v&h=ip,name,node.role,master" ip name node.role master 192.0.2.40 es-data-1 cdfhilmrstw * 192.0.2.41 es-data-2 cdfhilmrstw - 192.0.2.42 es-data-3 cdfhilmrstw -
Use built-in attributes such as _name, _ip, or _tier when a stable node attribute already exists. On secured clusters, switch to https and add the required credentials and CA file for the same request.
- Review node attributes when the filter should match a custom rack or zone value.
$ curl -sS "http://localhost:9200/_cat/nodeattrs?v&h=node,attr,value" node attr value es-data-1 zone rack-a es-data-2 zone rack-b es-data-3 zone rack-c
Custom filters use values from node.attr.* on each node. If no custom attribute is available, keep the filter on a built-in attribute instead of inventing one during live maintenance.
- Read the current explicit cluster allocation filters.
$ curl -sS "http://localhost:9200/_cluster/settings?pretty&flat_settings=true" { "persistent" : { }, "transient" : { } }GET /_cluster/settings returns only explicit overrides by default. An empty response means the cluster is currently using defaults or static settings instead of an API-set filter.
- Apply a persistent allocation filter for the required placement rule.
$ curl -sS -H "Content-Type: application/json" --request PUT "http://localhost:9200/_cluster/settings?pretty&flat_settings=true" --data '{ "persistent": { "cluster.routing.allocation.exclude._name": "es-data-3" } }' { "acknowledged" : true, "persistent" : { "cluster.routing.allocation.exclude._name" : "es-data-3" }, "transient" : { } }Use cluster.routing.allocation.require.zone to keep shards on one attribute value, cluster.routing.allocation.include._tier to allow one or more data tiers, or cluster.routing.allocation.exclude._ip to drain nodes by address. include matches at least one listed value, require demands all listed values, and exclude blocks any listed value.
Comma-separated values and wildcards such as 192.0.2.* are supported where the attribute value makes sense.
Update calls on secured clusters need a credential with cluster manage privilege.
- Read back the active filter to confirm the explicit override is stored.
$ curl -sS "http://localhost:9200/_cluster/settings?pretty&flat_settings=true" { "persistent" : { "cluster.routing.allocation.exclude._name" : "es-data-3" }, "transient" : { } }Elastic recommends persistent settings for normal allocation changes. Avoid transient filters unless the short-lived behavior and rollback path are both deliberate.
- Wait for shard relocation to finish before validating the final placement.
$ curl -sS "http://localhost:9200/_cluster/health?wait_for_no_relocating_shards=true&timeout=30m&pretty" { "cluster_name" : "search-cluster", "status" : "green", "timed_out" : false, "number_of_nodes" : 3, "number_of_data_nodes" : 3, "active_primary_shards" : 12, "active_shards" : 24, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "unassigned_primary_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 }Increase timeout for larger clusters or slower storage. Use /_cat/recovery?v&active_only=true when per-index recovery progress is needed during a long drain.
- Confirm the filter produced the expected node placement.
$ curl -sS "http://localhost:9200/_cat/allocation?v&h=shards,disk.percent,node" shards disk.percent node 12 64 es-data-1 12 67 es-data-2 0 61 es-data-3
If the blocked node still shows shards, another routing rule, disk watermark, or tier constraint is still preventing relocation. Use the allocation explain API before tightening the filter further.
- Clear the explicit filter when maintenance or temporary placement control is complete.
$ curl -sS -H "Content-Type: application/json" --request PUT "http://localhost:9200/_cluster/settings?pretty&flat_settings=true" --data '{ "persistent": { "cluster.routing.allocation.exclude._name": null } }' { "acknowledged" : true, "persistent" : { }, "transient" : { } }Set every active filter key to null when clearing multiple rules in one request, for example both require.zone and exclude._name.
- Confirm the filter is removed and the cluster can rebalance normally again.
$ curl -sS "http://localhost:9200/_cluster/settings?pretty&flat_settings=true" { "persistent" : { }, "transient" : { } }$ curl -sS "http://localhost:9200/_cluster/health?pretty" { "cluster_name" : "search-cluster", "status" : "green", "timed_out" : false, "number_of_nodes" : 3, "number_of_data_nodes" : 3, "active_primary_shards" : 12, "active_shards" : 24, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "unassigned_primary_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 }
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
