ILM vs Data Stream Lifecycle
Elastic.Ingest.Elasticsearch supports two approaches to data retention management: ILM (Index Lifecycle Management) and Data Stream Lifecycle (DSL).
ILM is the traditional approach for managing index lifecycle on self-managed and Elastic Cloud clusters. It defines phases (hot, warm, cold, frozen, delete) with actions and conditions.
The simplest way to add ILM is through the BootstrapStrategies.DataStreamWithIlm factory:
var strategy = IngestStrategies.DataStream<LogEntry>(
LoggingContext.LogEntry.Context,
BootstrapStrategies.DataStreamWithIlm("my-policy", hotMaxAge: "30d", deleteMinAge: "90d"));
var options = new IngestChannelOptions<LogEntry>(transport, strategy, LoggingContext.LogEntry.Context);
For indices, use BootstrapStrategies.IndexWithIlm:
var strategy = IngestStrategies.Index<Product>(
CatalogContext.Product.Context,
BootstrapStrategies.IndexWithIlm("my-policy", hotMaxAge: "30d", deleteMinAge: "90d"));
var options = new IngestChannelOptions<Product>(transport, strategy, CatalogContext.Product.Context);
The factory creates an IlmPolicyStep that:
- Creates the ILM policy via
PUT _ilm/policy/{name} - Skips on serverless (where ILM is not supported)
- Checks if the policy already exists before creating (idempotent)
- Is ordered before
ComponentTemplateStepsince the component template references the policy by name
For full control over ILM phases:
var policyJson = """
{
"phases": {
"hot": { "actions": { "rollover": { "max_age": "7d", "max_primary_shard_size": "50gb" } } },
"warm": { "min_age": "30d", "actions": { "shrink": { "number_of_shards": 1 } } },
"delete": { "min_age": "90d", "actions": { "delete": {} } }
}
}
""";
var bootstrap = new DefaultBootstrapStrategy(
new IlmPolicyStep("my-policy", policyJson),
new ComponentTemplateStep("my-policy"),
new DataStreamTemplateStep()
);
var strategy = IngestStrategies.DataStream<LogEntry>(
LoggingContext.LogEntry.Context, bootstrap);
var options = new IngestChannelOptions<LogEntry>(transport, strategy, LoggingContext.LogEntry.Context);
The ComponentTemplateStep automatically adds "index.lifecycle.name" to the settings component template when BootstrapContext.IlmPolicy is set (and not on serverless).
DSL is the serverless-compatible alternative to ILM. It specifies a data retention period embedded directly in the index template.
The simplest approach -- pass a retention period to IngestStrategies.DataStream:
var strategy = IngestStrategies.DataStream<LogEntry>(LoggingContext.LogEntry.Context, "30d");
var options = new IngestChannelOptions<LogEntry>(transport, strategy, LoggingContext.LogEntry.Context);
using var channel = new IngestChannel<LogEntry>(options);
This produces a template with:
{
"template": { "lifecycle": { "data_retention": "30d" } },
"data_stream": {}
}
For explicit bootstrap strategy composition:
var strategy = IngestStrategies.DataStream<LogEntry>(
LoggingContext.LogEntry.Context,
BootstrapStrategies.DataStream("30d"));
var options = new IngestChannelOptions<LogEntry>(transport, strategy, LoggingContext.LogEntry.Context);
| Feature | ILM | DSL |
|---|---|---|
| Serverless support | No | Yes |
| Multiple phases | Yes (hot/warm/cold/frozen/delete) | No (retention only) |
| Rollover conditions | Yes (age, size, docs) | Automatic |
| Complexity | Higher | Lower |
| Recommended for | Self-managed, complex lifecycle | Serverless, simple retention |
Recommendation: Use DSL (IngestStrategies.DataStream with a retention period) for new projects unless you need multi-phase ILM policies. DSL works on both serverless and self-managed clusters.