Keeping time-series data under control in Elasticsearch is easier when rollover and retention happen automatically instead of through manual index housekeeping.

The Index Lifecycle Management (ILM) engine evaluates a policy made of phases (such as hot and delete) and runs the configured actions when an index reaches the defined age/size thresholds. For classic index patterns, rollover is driven by an alias that points to the current write index, while the policy is attached through index settings or a composable index template.

A policy only affects indices that have index.lifecycle.name set, and rollover only works when index.lifecycle.rollover_alias exists as an alias with a write index. Clusters with security enabled require authentication for these API calls, and rollover/delete actions happen asynchronously based on the ILM poll interval rather than instantly.

Steps to create an index lifecycle policy in Elasticsearch:

  1. Create the ILM policy defining rollover at 7 days or 50 GB, delete at 30 days.
    $ curl --silent -H "Content-Type: application/json" --request PUT "http://localhost:9200/_ilm/policy/logs-policy?pretty" --data '{
      "policy": {
        "phases": {
          "hot": {
            "actions": {
              "rollover": { "max_age": "7d", "max_size": "50gb" }
            }
          },
          "delete": {
            "min_age": "30d",
            "actions": { "delete": {} }
          }
        }
      }
    }'
    {
      "acknowledged" : true
    }

    Secure clusters require authentication, so add a suitable Authorization header or a -u user:pass option.

  2. Confirm the policy is stored with the expected phase actions.
    $ curl --silent "http://localhost:9200/_ilm/policy/logs-policy?pretty"
    {
      "logs-policy" : {
        "version" : 1,
        "modified_date" : "2026-01-06T14:40:50.545Z",
        "policy" : {
          "phases" : {
            "hot" : {
              "min_age" : "0ms",
              "actions" : {
                "rollover" : {
                  "max_age" : "7d",
                  "max_size" : "50gb"
                }
              }
            },
            "delete" : {
              "min_age" : "30d",
              "actions" : {
                "delete" : {
                  "delete_searchable_snapshot" : true
                }
              }
            }
          }
        },
        "in_use_by" : {
          "indices" : [ ],
          "data_streams" : [ ],
          "composable_templates" : [ ]
        }
      }
    }
  3. Create a composable index template that applies logs-policy, setting logs-write as the rollover alias for logs-* indices.
    $ curl --silent -H "Content-Type: application/json" --request PUT "http://localhost:9200/_index_template/logs-ilm?pretty" --data '{
      "index_patterns": ["logs-*"],
      "priority": 300,
      "template": {
        "settings": {
          "index.lifecycle.name": "logs-policy",
          "index.lifecycle.rollover_alias": "logs-write"
        }
      }
    }'
    {
      "acknowledged" : true
    }

    Rollover fails if logs-write is not an alias or if it does not point to a write index.

  4. Simulate template resolution for an index name matching the pattern to verify the ILM settings are included.
    $ curl --silent --request POST "http://localhost:9200/_index_template/_simulate_index/logs-000001?pretty"
    {
      "template" : {
        "settings" : {
          "index" : {
            "lifecycle" : {
              "name" : "logs-policy",
              "rollover_alias" : "logs-write"
            },
            "routing" : {
              "allocation" : {
                "include" : {
                  "_tier_preference" : "data_content"
                }
              }
            }
          }
        },
        "aliases" : { }
      },
      "overlapping" : [
        {
          "name" : "logs",
          "index_patterns" : [
            "logs-*-*"
          ]
        },
        {
          "name" : "logs-template",
          "index_patterns" : [
            "logs-*"
          ]
        }
      ]
    }
  5. Bootstrap the initial write index with logs-write configured as the write alias.
    $ curl --silent -H "Content-Type: application/json" --request PUT "http://localhost:9200/logs-000001?pretty" --data '{
      "aliases": { "logs-write": { "is_write_index": true } }
    }'
    {
      "acknowledged" : true,
      "shards_acknowledged" : true,
      "index" : "logs-000001"
    }

    Keep the index name in a rollover-friendly sequence (for example logs-000001, logs-000002) so the rollover action can generate the next index name.

  6. Verify the rollover alias points to the expected write index.
    $ curl --silent "http://localhost:9200/_cat/aliases/logs-write?h=alias,index,is_write_index&v"
    alias      index        is_write_index
    logs-write logs-000001  true
  7. Check the lifecycle state to confirm ILM is managing the index.
    $ curl --silent "http://localhost:9200/logs-000001/_ilm/explain?pretty"
    {
      "indices" : {
        "logs-000001" : {
          "index" : "logs-000001",
          "managed" : true,
          "policy" : "logs-policy",
          "index_creation_date_millis" : 1767710472934,
          "time_since_index_creation" : "7.63s",
          "lifecycle_date_millis" : 1767710472934,
          "age" : "7.63s",
          "phase" : "new",
          "phase_time_millis" : 1767710472837,
          "action" : "complete",
          "action_time_millis" : 1767710472837,
          "step" : "complete",
          "step_time_millis" : 1767710472837,
          "phase_execution" : {
            "policy" : "logs-policy",
            "version" : 1,
            "modified_date_in_millis" : 1767710450545
          }
        }
      }
    }