Configure Elasticsearch Index Lifecycle Management (ILM) for automated data retention

Intermediate 25 min May 24, 2026 18 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up Elasticsearch ILM policies to automatically manage index lifecycles through hot, warm, cold, and delete phases. Reduce storage costs and optimize performance by automating data retention and storage tiering.

Prerequisites

  • Running Elasticsearch 7.0+ cluster
  • Cluster admin permissions
  • Basic understanding of Elasticsearch indices

What this solves

Elasticsearch Index Lifecycle Management (ILM) automatically manages your indices through different phases based on age, size, or document count. Without ILM, your indices grow indefinitely, consuming disk space and degrading search performance. ILM policies move indices through hot (active writes), warm (read-only), cold (infrequent access), and delete phases, optimizing storage costs and cluster performance.

Prerequisites and planning

Before configuring ILM policies, you need a running Elasticsearch cluster with proper node roles. Understanding your data patterns helps design effective policies.

Verify cluster health

Check your Elasticsearch cluster status and node configuration.

curl -X GET "localhost:9200/_cluster/health?pretty"
curl -X GET "localhost:9200/_cat/nodes?v&h=node.role,name"

Review existing indices

Examine current indices to understand size patterns and identify candidates for ILM management.

curl -X GET "localhost:9200/_cat/indices?v&s=store.size:desc"
curl -X GET "localhost:9200/_cat/indices?v&h=index,docs.count,store.size,creation.date.string"

Understanding ILM phases and policies

ILM manages indices through four distinct phases, each with specific actions and triggers.

PhasePurposeCommon ActionsTypical Duration
HotActive indexing and frequent queriesRollover, set priority1-7 days
WarmRead-only, less frequent queriesAllocate to warm nodes, force merge7-30 days
ColdInfrequent access, archived dataAllocate to cold nodes, freeze30-90 days
DeleteRemove old dataDelete indexAfter retention period

Step-by-step ILM configuration

Create a basic ILM policy

Define a policy that manages indices through hot, warm, and delete phases based on age and size.

curl -X PUT "localhost:9200/_ilm/policy/logs-policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "5GB",
            "max_age": "1d",
            "max_docs": 10000000
          },
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "1d",
        "actions": {
          "set_priority": {
            "priority": 50
          },
          "allocate": {
            "number_of_replicas": 1,
            "include": {},
            "exclude": {},
            "require": {
              "data_tier": "warm"
            }
          },
          "forcemerge": {
            "max_num_segments": 1
          }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}'

Create an advanced ILM policy with cold phase

Configure a comprehensive policy including cold phase for long-term archival before deletion.

curl -X PUT "localhost:9200/_ilm/policy/application-logs-policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "10GB",
            "max_age": "7d",
            "max_docs": 50000000
          },
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "set_priority": {
            "priority": 50
          },
          "allocate": {
            "number_of_replicas": 1,
            "require": {
              "data_tier": "warm"
            }
          },
          "forcemerge": {
            "max_num_segments": 1
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "set_priority": {
            "priority": 0
          },
          "allocate": {
            "number_of_replicas": 0,
            "require": {
              "data_tier": "cold"
            }
          },
          "freeze": {}
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}'

Verify policy creation

List all ILM policies and examine the policy details to confirm proper configuration.

curl -X GET "localhost:9200/_ilm/policy?pretty"
curl -X GET "localhost:9200/_ilm/policy/logs-policy?pretty"

Applying ILM policies to indices and templates

Create an index template with ILM

Configure an index template that automatically applies ILM policy to new indices matching the pattern.

curl -X PUT "localhost:9200/_index_template/logs-template" -H 'Content-Type: application/json' -d'
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs-policy",
      "index.lifecycle.rollover_alias": "logs"
    },
    "mappings": {
      "properties": {
        "@timestamp": {
          "type": "date"
        },
        "message": {
          "type": "text"
        },
        "level": {
          "type": "keyword"
        }
      }
    }
  },
  "priority": 200,
  "composed_of": [],
  "version": 1
}'

Create the initial index with alias

Set up the bootstrap index and alias for rollover functionality.

curl -X PUT "localhost:9200/logs-000001" -H 'Content-Type: application/json' -d'
{
  "aliases": {
    "logs": {
      "is_write_index": true
    }
  }
}'

Apply ILM policy to existing indices

Attach ILM policies to existing indices that weren't created with templates.

curl -X PUT "localhost:9200/existing-logs-*/_settings" -H 'Content-Type: application/json' -d'
{
  "index.lifecycle.name": "logs-policy"
}'

Monitoring and managing ILM policy execution

Monitor ILM execution status

Check the lifecycle status and phase progression of managed indices.

curl -X GET "localhost:9200/_ilm/status?pretty"
curl -X GET "localhost:9200/logs-*/_ilm/explain?pretty"

Force policy execution for testing

Manually trigger ILM actions to test policy behavior without waiting for timers.

curl -X POST "localhost:9200/_ilm/move/logs-000001" -H 'Content-Type: application/json' -d'
{
  "current_step": {
    "phase": "hot",
    "action": "complete",
    "name": "complete"
  },
  "next_step": {
    "phase": "warm",
    "action": "complete",
    "name": "complete"
  }
}'

Create monitoring queries

Set up queries to track ILM policy effectiveness and identify issues.

curl -X GET "localhost:9200/_cat/indices?v&h=index,health,status,pri,rep,docs.count,store.size,creation.date.string" | grep logs
curl -X GET "localhost:9200/_ilm/explain/logs-*?only_errors=true&pretty"

Advanced ILM configurations

Configure size-based rollover

Create a policy focused on index size rather than age for high-volume applications.

curl -X PUT "localhost:9200/_ilm/policy/metrics-policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "2GB",
            "max_docs": 25000000
          },
          "set_priority": {
            "priority": 100
          }
        }
      },
      "warm": {
        "min_age": "3d",
        "actions": {
          "readonly": {},
          "forcemerge": {
            "max_num_segments": 1
          },
          "shrink": {
            "number_of_shards": 1
          }
        }
      },
      "delete": {
        "min_age": "14d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}'

Set up conditional phase transitions

Configure policies that use multiple conditions for phase transitions.

curl -X PUT "localhost:9200/_ilm/policy/conditional-policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "5GB",
            "max_age": "2d",
            "max_docs": 10000000
          }
        }
      },
      "warm": {
        "min_age": "1d",
        "actions": {
          "allocate": {
            "number_of_replicas": 1,
            "include": {},
            "exclude": {
              "_tier_preference": "data_hot"
            }
          }
        }
      },
      "delete": {
        "min_age": "7d",
        "actions": {
          "wait_for_snapshot": {
            "policy": "nightly-snapshots"
          },
          "delete": {}
        }
      }
    }
  }
}'

Verify your setup

curl -X GET "localhost:9200/_ilm/policy?pretty" | jq '.'
curl -X GET "localhost:9200/_cat/indices?v&s=creation.date" | head -10
curl -X GET "localhost:9200/logs-*/_ilm/explain?pretty" | jq '.indices[] | {index: .index, phase: .phase, action: .action}'
curl -X GET "localhost:9200/_cluster/health?pretty"
Note: If you're setting up monitoring for your ILM policies, consider integrating with Prometheus and Grafana to track index lifecycle metrics and storage utilization.

Common issues

SymptomCauseFix
Policy not executingILM disabled or indices missing policyCheck /_ilm/status and verify policy assignment
Rollover not triggeringWrite index alias not setEnsure alias has is_write_index: true
Phase transitions failingNode allocation requirements not metVerify data tier nodes exist with proper attributes
Indices stuck in error stateConflicting shard allocation rulesCheck /_ilm/explain?only_errors=true for details
Unexpected storage growthDelete phase not configured or delayedReview policy timing and force execution for testing
Performance degradationToo many small indices or segmentsAdjust rollover thresholds and force merge settings

Next steps

Running this in production?

Want this handled for you? Setting up ILM policies once is straightforward. Keeping them tuned, monitored, and optimized for changing data patterns across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle high availability infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.