How do legacy _template definitions interact with composable _index_template definitions?
answer
- two tiers, not one contest
- the newer tier wins whenever it matches
- the old tier merged everything that matched
- an implicit merge must be made explicit
basics
~20 sComposable templates win outright: if any composable index template matches a new index name, Elasticsearch ignores every legacy _template for that index. Legacy templates are deprecated, merge all matches by order, and cannot back data streams.
solid answer
~50 sThe two systems sit in separate tiers rather than competing. Elasticsearch first looks for composable templates under `_index_template`; if one matches the new index name it wins and legacy templates are not consulted at all. Legacy `_template` definitions are only used when no composable template matches. Their semantics also differ: legacy templates **merge every match**, ordered by the `order` field with higher values applied later and overriding, whereas composable templates select a single winner by `priority` and merge only inside it via `composed_of`. That difference is the whole difficulty of migration. A family configured by three overlapping legacy templates cannot be translated one-for-one; you must materialise the merge explicitly — extract the shared parts into component templates and reference them from one composable template per family. Legacy templates are deprecated (they emit deprecation warnings) and cannot be used with data streams, so on Elasticsearch 8.x and 9.x migrating is the direction of travel, not an option.
code
bash · 14 lines# inventory both tiers before touching anything
GET _template
GET _index_template
# rebuild the implicit legacy merge as an explicit composition
PUT _index_template/logs-app
{
"index_patterns": ["logs-app-*"],
"priority": 500,
"composed_of": ["cluster-defaults", "logs-common", "logs-app-fields"]
}
# prove it matches what the legacy tier produced
POST _index_template/_simulate_index/logs-app-2024.06.02go deeper
You are unlikely to be asked this. Knowing that _index_template is the current API and _template is the deprecated one is enough at this stage.
Be able to state the tier rule - composable wins whenever it matches, legacy is the fallback - and contrast merge-all-by-order with single-winner-by-priority.
Own the migration: inventory both tiers, reproduce the implicit merge as component templates, verify with simulate against a real recent index, and cut over family by family.
Frame it as upgrade debt with a deadline. Deprecated templates block version upgrades and data streams, so schedule the conversion, define who owns each family and set the verification gate that makes cutover boring.
## Two generations, two matching rules Elasticsearch has carried two template systems since 7.8. The older one, registered at `PUT _template/<name>`, is now called a **legacy** or v1 template. The newer one, at `PUT _index_template/<name>`, is the **composable** template, and it brought component templates and `priority` with it. They do not merge with each other. The resolution rule is a strict tier check at index creation: 1. Does any composable index template match this index name? If yes, that tier decides — the highest-`priority` composable template applies and legacy templates are ignored entirely. 2. Only if no composable template matches does Elasticsearch fall back to legacy templates. Within the legacy tier the semantics are the old ones: **all** matching legacy templates are merged, sorted by the `order` field, with higher `order` applied later and therefore overriding lower ones. Within the composable tier, exactly one template applies. ## Why that makes migration awkward The two tiers encode opposite philosophies. Legacy templates compose implicitly through overlapping patterns — you add a template matching `logs-*` and it layers onto everything already matching `logs-app-*`. Composable templates compose explicitly through `composed_of` — overlap between index templates is a conflict, not a feature, and equal-priority overlap is rejected outright. So a mechanical conversion of each legacy template into a composable one is usually wrong. Three legacy templates matching `*`, `logs-*` and `logs-app-*` previously all contributed to `logs-app-1`. Convert them naively and only one of the three will apply; the other two contribute nothing, silently. The correct translation is: - inspect the current effective result — the merged configuration the legacy tier actually produced for a representative index name; - split that result into **component templates** by concern (cluster-wide settings, shared field schema, per-family fields); - create **one composable index template per index family**, with narrow `index_patterns`, an explicit `priority`, and a `composed_of` list that reproduces the old layering in order; - verify with `POST _index_template/_simulate_index/<name>` that the composed output matches what the legacy tier produced. ## The dangerous middle state During migration both tiers exist, and the tier rule creates a sharp edge: **the moment a composable template matches a name, the legacy templates for that name stop contributing.** Not "stop overriding" — stop contributing entirely. If your new composable template covers settings but you forgot the analyzer that a legacy `*` template used to supply, indices created from that point on quietly lose the analyzer. That makes the safe sequence: build the composable template, simulate the names it will claim, diff the composed result against the mapping of a recent index created under the legacy tier, and only then apply it. The diff is the whole safety net. Do it per index family and cut families over one at a time rather than in a big bang, so a mistake affects one family's next index rather than every family's. ## Practical mechanics `GET _template` lists the legacy templates, `GET _index_template` the composable ones — worth running on any cluster you inherit, because clusters accumulate templates from long-departed teams and from installed integrations. Applying legacy templates raises deprecation warnings, so grepping the deprecation log is a decent inventory of what is still live rather than merely stored. Two hard constraints push the timeline. Data streams can only be backed by composable templates — a legacy template cannot declare `data_stream` — so any move to data streams forces the migration for that family. And legacy templates are deprecated, meaning they are on a removal path; leaving them in place is accruing an upgrade blocker, not holding a stable position. ## What to say in an interview The three points that matter are: composable beats legacy outright rather than merging with it; legacy merges all matches by `order` while composable picks one winner by `priority`; and therefore the migration has to materialise an implicit merge into explicit components, verified with the simulate API before cutover. Candidates who can explain *why* the merge semantics differ — implicit composition through overlap versus explicit composition through `composed_of` — are demonstrating that they have actually done it rather than read about it.
- Why can a legacy template not be converted one-for-one into a composable template?Because legacy templates composed implicitly through overlapping patterns — three templates could all contribute to one index. Composable templates pick a single winner, so overlap contributes nothing. You must materialise the old merged result as component templates referenced in order from one index template per family.
- What forces the migration regardless of appetite for it?Data streams can only be backed by composable index templates, so any family moving to data streams must convert. Legacy templates are also deprecated and emit deprecation warnings, which makes leaving them in place an upgrade blocker rather than a stable position.
- How do you make the cutover safe for a family currently served by several legacy templates?Build the composable template, simulate the index names it will claim, and diff the composed settings and mappings against a recently created index from the legacy tier. Cut families over one at a time so any omission affects one family's next index rather than every family at once.
saying these in an interview costs you the question
- Thinks legacy and composable templates merge together
- Says the legacy order field breaks ties between composable templates
- Converts each legacy template into one composable template one-for-one
- Assumes a legacy template can back a data stream
- Cuts every index family over to composable templates in one change