Explaining shard allocation decisions in Elasticsearch shows why a shard is unassigned, delayed, throttled, or left on its current node. Use the allocation explain API when cluster health stays red or yellow, a shard does not move after a setting change, or rebalancing does not match the expected placement.

The /_cluster/allocation/explain API evaluates one shard against the current cluster state. It returns can_allocate, node_allocation_decisions, and per-decider NO, THROTTLE, or YES results so the next action follows the blocking rule instead of a guessed allocation change.

Allocation responses can be long on busy clusters. Start with a focused request and add include_yes_decisions or include_disk_info only when the first response does not show enough node context. On secured clusters, use the same https:// endpoint and authentication method that normal cluster administration uses.

Steps to explain shard allocation decisions in Elasticsearch:

  1. List unassigned shards and capture the target index, shard number, and primary or replica role.
    $ curl --silent --show-error --fail "http://localhost:9200/_cat/shards?v=true&h=index,shard,prirep,state,node,unassigned.reason&s=state"
    index        shard prirep state      node unassigned.reason
    logs-2026.05 0     p      UNASSIGNED      INDEX_CREATED
    ##### snipped #####

    Replace http://localhost:9200 with the cluster endpoint. On secured clusters, use https:// plus authentication such as --user or an Authorization: ApiKey header.

  2. Request a focused allocation explanation for the selected shard.
    $ curl --silent --show-error --fail \
      --header "Content-Type: application/json" \
      --request POST \
      "http://localhost:9200/_cluster/allocation/explain?filter_path=index,shard,primary,current_state,unassigned_info.reason,unassigned_info.last_allocation_status,can_allocate,allocate_explanation,node_allocation_decisions.node_name,node_allocation_decisions.node_decision,node_allocation_decisions.deciders.*&pretty" \
      --data '{
      "index": "logs-2026.05",
      "shard": 0,
      "primary": true
    }'
    {
      "index" : "logs-2026.05",
      "shard" : 0,
      "primary" : true,
      "current_state" : "unassigned",
      "unassigned_info" : {
        "reason" : "INDEX_CREATED",
        "last_allocation_status" : "no"
      },
      "can_allocate" : "no",
      "allocate_explanation" : "Elasticsearch isn't allowed to allocate this shard to any of the nodes in the cluster. Choose a node to which you expect this shard to be allocated, find this node in the node-by-node explanation, and address the reasons which prevent Elasticsearch from allocating this shard there.",
      "node_allocation_decisions" : [
        {
          "node_name" : "node-01",
          "node_decision" : "no",
          "deciders" : [
            {
              "decider" : "filter",
              "decision" : "NO",
              "explanation" : "node does not match index setting [index.routing.allocation.include] filters [_name:\"nonexistent_node\"]"
            }
          ]
        }
      ]
    }

    Use the first NO or THROTTLE decider on the intended node as the blocker to investigate. Omitting the request body explains an arbitrary unassigned shard, and current_node can target an assigned shard by node name or node ID.

  3. Expand the response when the first pass does not show enough node context.
    $ curl --silent --show-error --fail \
      --header "Content-Type: application/json" \
      --request POST \
      "http://localhost:9200/_cluster/allocation/explain?include_yes_decisions=true&include_disk_info=true&filter_path=cluster_info.nodes.*.least_available.*,node_allocation_decisions.node_name,node_allocation_decisions.node_decision,node_allocation_decisions.deciders.*&pretty" \
      --data '{
      "index": "logs-2026.05",
      "shard": 0,
      "primary": true
    }'
    {
      "cluster_info" : {
        "nodes" : {
          "node-id-01" : {
            "least_available" : {
              "path" : "/usr/share/elasticsearch/data",
              "total_bytes" : 1963569909760,
              "used_bytes" : 292858593280,
              "free_bytes" : 1670711316480,
              "free_disk_percent" : 85.1,
              "used_disk_percent" : 14.9
            }
          }
        }
      },
      "node_allocation_decisions" : [
        {
          "node_name" : "node-01",
          "node_decision" : "no",
          "deciders" : [
            {
              "decider" : "max_retry",
              "decision" : "YES",
              "explanation" : "shard has no previous failures"
            },
            {
              "decider" : "enable",
              "decision" : "YES",
              "explanation" : "all allocations are allowed"
            },
            {
              "decider" : "filter",
              "decision" : "NO",
              "explanation" : "node does not match index setting [index.routing.allocation.include] filters [_name:\"nonexistent_node\"]"
            },
            {
              "decider" : "disk_threshold",
              "decision" : "YES",
              "explanation" : "enough disk for shard on node, free: [1.5tb], used: [14.9%], shard size: [0b], free after allocating shard: [1.5tb]"
            }
    ##### snipped #####
          ]
        }
      ]
    }

    include_yes_decisions adds allowed deciders, while include_disk_info adds disk usage and shard-size context. Keep the narrower request for routine checks because the expanded response can be much larger.

  4. Match the blocking decider to the smallest safe fix.

    filter usually means correcting index-level or cluster-level allocation filters. same_shard on a single-node cluster means a replica cannot live beside its primary. disk_threshold points to free space or watermark settings. max_retry means the underlying failure must be fixed before POST /_cluster/reroute?retry_failed=true can retry allocation.

  5. Clear the incorrect index allocation filter when filter is the blocker.
    $ curl --silent --show-error --fail \
      --header "Content-Type: application/json" \
      --request PUT \
      "http://localhost:9200/logs-2026.05/_settings?pretty" \
      --data '{
      "index.routing.allocation.include._name": null
    }'
    {
      "acknowledged" : true
    }

    Changing allocation rules can start shard recovery or relocation traffic across disk, CPU, and network resources.

  6. Confirm that the shard leaves UNASSIGNED and reaches STARTED.
    $ curl --silent --show-error --fail "http://localhost:9200/_cat/shards/logs-2026.05?v=true&h=index,shard,prirep,state,node&s=state"
    index        shard prirep state   node
    logs-2026.05 0     p      STARTED node-01

    If the shard stays UNASSIGNED, run the explain request again immediately because the response may move from the original blocker to the next remaining decider.

  7. Verify that the index returns to the expected health state.
    $ curl --silent --show-error --fail "http://localhost:9200/_cluster/health/logs-2026.05?filter_path=status,active_primary_shards,active_shards,unassigned_shards,initializing_shards,relocating_shards&pretty"
    {
      "status" : "green",
      "active_primary_shards" : 1,
      "active_shards" : 1,
      "relocating_shards" : 0,
      "initializing_shards" : 0,
      "unassigned_shards" : 0
    }

    A single-node cluster stays yellow when replicas are configured because Elasticsearch will not place a replica on the same node as its primary. Add another data node or reduce number_of_replicas instead of changing unrelated allocation settings.

  8. Explain an assigned shard with current_node when the issue is placement or rebalancing instead of initial allocation.
    $ curl --silent --show-error --fail \
      --header "Content-Type: application/json" \
      --request POST \
      "http://localhost:9200/_cluster/allocation/explain?filter_path=index,shard,primary,current_state,current_node.name,can_remain_on_current_node,can_rebalance_cluster,can_rebalance_to_other_node,rebalance_explanation,node_allocation_decisions.node_name,node_allocation_decisions.node_decision&pretty" \
      --data '{
      "index": "logs-2026.05",
      "shard": 0,
      "primary": true,
      "current_node": "node-01"
    }'
    {
      "index" : "logs-2026.05",
      "shard" : 0,
      "primary" : true,
      "current_state" : "started",
      "current_node" : {
        "name" : "node-01"
      },
      "can_remain_on_current_node" : "yes",
      "can_rebalance_cluster" : "yes",
      "can_rebalance_to_other_node" : "no",
      "rebalance_explanation" : "This shard is in a well-balanced location and satisfies all allocation rules so it will remain on this node. Elasticsearch cannot improve the cluster balance by moving it to another node. If you expect this shard to be rebalanced to another node, find the other node in the node-by-node explanation and address the reasons which prevent Elasticsearch from rebalancing this shard there."
    }

    When can_remain_on_current_node is no, rerun without a narrow filter_path and inspect can_remain_decisions plus node_allocation_decisions.