How to create an index lifecycle policy in Elasticsearch

An Index Lifecycle Management (ILM) policy keeps time-series Elasticsearch indices from growing unchecked by automating rollover and retention instead of relying on manual alias swaps and cleanup jobs.

ILM policies live in cluster state and define actions for phases such as hot and delete. For classic rolling indices, the usual flow is to create the policy, attach it through a composable index template, and bootstrap an initial write index whose alias can advance during rollover.

Current Elastic guidance recommends data streams for new append-only time-series workloads because rollover is handled automatically, while alias-based ILM remains the fit when classic indices are still required or documents need in-place updates or deletes. Current rollover docs also deprecate max_size in favor of max_primary_shard_size, and broad patterns such as logs-*-* can collide with built-in templates on current clusters, so this example uses app-logs-*. Security-enabled clusters need the correct HTTPS endpoint plus authentication such as -u username:password or an API key header.

Steps to create an index lifecycle policy in Elasticsearch:

  1. Create the ILM policy defining rollover at 7 days or 25 GB per primary shard and deletion at 30 days.
    $ curl -sS --fail -H "Content-Type: application/json" -X PUT "http://localhost:9200/_ilm/policy/app-logs-policy?pretty&filter_path=acknowledged" -d '{
      "policy": {
        "phases": {
          "hot": {
            "actions": {
              "rollover": {
                "max_age": "7d",
                "max_primary_shard_size": "25gb"
              }
            }
          },
          "delete": {
            "min_age": "30d",
            "actions": {
              "delete": {}
            }
          }
        }
      }
    }'
    {
      "acknowledged" : true
    }

    Current rollover docs mark max_size as deprecated. Use max_primary_shard_size for new policies.

    Secure clusters need the cluster HTTPS endpoint plus authentication on this request.

  2. Read the stored policy back to confirm the phase actions now match the intended rollover and retention rules.
    $ curl -sS --fail "http://localhost:9200/_ilm/policy/app-logs-policy?pretty&filter_path=*.version,*.policy.phases.hot.actions.rollover,*.policy.phases.delete"
    {
      "app-logs-policy" : {
        "version" : 1,
        "policy" : {
          "phases" : {
            "hot" : {
              "actions" : {
                "rollover" : {
                  "max_age" : "7d",
                  "max_primary_shard_size" : "25gb"
                }
              }
            },
            "delete" : {
              "min_age" : "30d",
              "actions" : {
                "delete" : {
                  "delete_searchable_snapshot" : true
                }
              }
            }
          }
        }
      }
    }
  3. Create a composable index template that applies the policy to app-logs-* indices and sets app-logs-write as the rollover alias.
    $ curl -sS --fail -H "Content-Type: application/json" -X PUT "http://localhost:9200/_index_template/app-logs-template?pretty&filter_path=acknowledged" -d '{
      "index_patterns": ["app-logs-*"],
      "priority": 300,
      "template": {
        "settings": {
          "number_of_replicas": 0,
          "index.lifecycle.name": "app-logs-policy",
          "index.lifecycle.rollover_alias": "app-logs-write"
        }
      }
    }'
    {
      "acknowledged" : true
    }

    An application-specific pattern such as app-logs-* avoids the built-in logs templates that current clusters install for logs-*-* and related patterns.

    number_of_replicas is set to 0 here so a one-node validation cluster stays GREEN. Raise it on multi-node clusters.

  4. Simulate the resolved template for the first index generation before creating it.
    $ curl -sS --fail -X POST "http://localhost:9200/_index_template/_simulate_index/app-logs-000001?pretty&filter_path=template.settings.index.lifecycle,template.settings.index.number_of_replicas,overlapping"
    {
      "template" : {
        "settings" : {
          "index" : {
            "lifecycle" : {
              "name" : "app-logs-policy",
              "rollover_alias" : "app-logs-write"
            },
            "number_of_replicas" : "0"
          }
        }
      },
      "overlapping" : [ ]
    }

    Simulation confirms the lifecycle settings that a matching index will inherit and reveals any overlapping templates before live data lands.

  5. Bootstrap the initial write index with the rollover alias marked as the write target.
    $ curl -sS --fail -H "Content-Type: application/json" -X PUT "http://localhost:9200/app-logs-000001?pretty&filter_path=acknowledged,shards_acknowledged,index" -d '{
      "aliases": {
        "app-logs-write": {
          "is_write_index": true
        }
      }
    }'
    {
      "acknowledged" : true,
      "shards_acknowledged" : true,
      "index" : "app-logs-000001"
    }

    Alias-based rollover requires a generation-style index name such as app-logs-000001, the matching index.lifecycle.rollover_alias setting, and the alias marked with is_write_index.

  6. Verify the rollover alias now points at the bootstrapped write index.
    $ curl -sS --fail "http://localhost:9200/_cat/aliases/app-logs-write?h=alias,index,is_write_index&v"
    alias          index           is_write_index
    app-logs-write app-logs-000001 true

    The _cat APIs are intended for human-readable terminal checks. Use JSON alias APIs for automation.

  7. Check the lifecycle state to confirm Elasticsearch is actively managing the index under the new policy.
    $ curl -sS --fail "http://localhost:9200/app-logs-000001/_ilm/explain?pretty&filter_path=indices.*.index,indices.*.managed,indices.*.policy,indices.*.phase,indices.*.action,indices.*.step,indices.*.phase_execution.policy,indices.*.phase_execution.version"
    {
      "indices" : {
        "app-logs-000001" : {
          "index" : "app-logs-000001",
          "managed" : true,
          "policy" : "app-logs-policy",
          "phase" : "hot",
          "action" : "rollover",
          "step" : "check-rollover-ready",
          "phase_execution" : {
            "policy" : "app-logs-policy",
            "version" : 1
          }
        }
      }
    }

    managed set to true confirms the index is enrolled in ILM. check-rollover-ready means the alias wiring is valid and Elasticsearch is waiting for the rollover conditions to be met.

    By default, empty indices do not roll over just because max_age is reached. Add documents first, or include \"min_docs\": 0 in the rollover action when empty-index rollover is required.