A data stream is an append-only alias over a series of hidden backing indices (.ds-<name>-<date>-000001, -000002, …) that roll over automatically. It's the right shape for logs and metrics. A data stream can only be created when a matching index template with "data_stream": {} exists.
Naming¶
Use the <type>-<dataset>-<namespace> scheme, for example logs-opnsense-default. The built-in logs-*-* template has priority 100, so a custom template for your own dataset needs a higher priority to win.
Create the index template¶
PUT _index_template/logs-opnsense
{
"index_patterns": ["logs-opnsense-*"],
"data_stream": {},
"priority": 200,
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"index.default_pipeline": "opnsense",
"index.lifecycle.name": "logs"
},
"mappings": {
"dynamic": false,
"properties": {
"@timestamp": { "type": "date" },
"message": { "type": "match_only_text" },
"observer": { "properties": {
"hostname": { "type": "keyword" },
"type": { "type": "keyword" },
"ingress": { "properties": { "interface": { "properties": { "name": { "type": "keyword" } } } } }
} },
"process": { "properties": { "name": { "type": "keyword" }, "pid": { "type": "long" } } },
"log": { "properties": { "syslog": { "properties": { "severity": { "properties": { "name": { "type": "keyword" } } } } } } },
"source": { "properties": { "ip": { "type": "ip" }, "port": { "type": "long" } } },
"destination": { "properties": { "ip": { "type": "ip" }, "port": { "type": "long" } } },
"network": { "properties": { "transport": { "type": "keyword" } } },
"event": { "properties": { "action": { "type": "keyword" } } },
"error": { "properties": { "message": { "type": "match_only_text" } } }
}
}
}
}
Choices worth knowing about:
number_of_replicas: 0suits a single-node cluster. With1, every index stays yellow because the replica has nowhere to go. Use1or more on multi-node clusters.index.default_pipelineconnects the template to the ingest pipeline, so shippers don't have to name it.index.lifecycle.nameattaches an ILM policy. Without one, the data stream rolls over and grows forever."dynamic": falsekeeps unexpected fields in_sourcewithout mapping them, which prevents field explosion (see disk space).
Preview the result¶
This shows the merged settings and mappings a new backing index would get, without creating anything:
POST _index_template/_simulate_index/logs-opnsense-default
Create the data stream¶
Indexing the first document creates it automatically. To create it explicitly:
PUT _data_stream/logs-opnsense-default
GET _data_stream/logs-opnsense-default
Changing mappings later¶
Template changes only apply to new backing indices. To apply them now, force a rollover:
POST logs-opnsense-default/_rollover