How to create and manage index templates in Elasticsearch

Index templates in Elasticsearch apply the same shard settings, refresh interval, field mappings, and aliases whenever a new matching index is created. They are useful when an application owns a family of time-sliced indices and each new index must start with the same structure instead of relying on repeated create-index JSON.

Modern Elasticsearch uses composable templates through the /_index_template API. A template matches names through index_patterns, can store settings, mappings, and aliases under template, and can assemble reusable component templates with composed_of when several index families share the same blocks.

Template changes affect only indices or data stream backing indices created after the template is saved. Use application-specific patterns such as app-logs-* to avoid built-in Elastic templates, and use HTTPS plus credentials on secured clusters with a role that has the manage_index_templates cluster privilege.

Steps to create and manage index templates in Elasticsearch:

  1. Create the first version of the index template for an application log pattern.
    $ curl --silent --show-error --fail --header "Content-Type: application/json" --request PUT "http://localhost:9200/_index_template/app-logs-template?create=true&pretty" --data '{
      "index_patterns": ["app-logs-*"],
      "priority": 300,
      "version": 1,
      "_meta": {
        "description": "Defaults for application log indices"
      },
      "template": {
        "settings": {
          "number_of_shards": 1,
          "number_of_replicas": 0,
          "refresh_interval": "30s"
        },
        "mappings": {
          "properties": {
            "timestamp": { "type": "date" },
            "level": { "type": "keyword" },
            "message": { "type": "text" }
          }
        }
      }
    }'
    {
      "acknowledged" : true
    }

    The create=true query parameter prevents accidental replacement. Omit it only when intentionally updating an existing index template. A non-overlapping pattern such as app-logs-* avoids built-in templates; use a priority above 500 only when deliberately overriding a shared built-in or Fleet-managed pattern.

  2. Simulate template resolution for a matching index name.
    $ curl --silent --show-error --fail --request POST "http://localhost:9200/_index_template/_simulate_index/app-logs-2026.04?pretty&filter_path=template.settings.index.number_of_shards,template.settings.index.number_of_replicas,template.settings.index.refresh_interval,template.mappings.properties,overlapping"
    {
      "template" : {
        "settings" : {
          "index" : {
            "refresh_interval" : "30s",
            "number_of_shards" : "1",
            "number_of_replicas" : "0"
          }
        },
        "mappings" : {
          "properties" : {
            "level" : {
              "type" : "keyword"
            },
            "message" : {
              "type" : "text"
            },
            "timestamp" : {
              "type" : "date"
            }
          }
        }
      },
      "overlapping" : [ ]
    }

    Simulation returns the configuration Elasticsearch would apply without creating the index. Remove filter_path when troubleshooting overlap or merge details.

  3. Create a test index that matches the template pattern.
    $ curl --silent --show-error --fail --request PUT "http://localhost:9200/app-logs-2026.04?pretty"
    {
      "acknowledged" : true,
      "shards_acknowledged" : true,
      "index" : "app-logs-2026.04"
    }

    Index templates apply only at index-creation time, so creating a test index proves the template can be used by new matching indices.

  4. Verify the inherited shard, replica, and refresh settings on the test index.
    $ curl --silent --show-error --fail "http://localhost:9200/app-logs-2026.04/_settings?pretty&filter_path=*.settings.index.number_of_shards,*.settings.index.number_of_replicas,*.settings.index.refresh_interval"
    {
      "app-logs-2026.04" : {
        "settings" : {
          "index" : {
            "refresh_interval" : "30s",
            "number_of_shards" : "1",
            "number_of_replicas" : "0"
          }
        }
      }
    }

    Using number_of_replicas 0 keeps a one-node validation cluster green. Raise the replica count when another data node should hold a copy.

  5. Verify the inherited field mappings on the test index.
    $ curl --silent --show-error --fail "http://localhost:9200/app-logs-2026.04/_mapping?pretty&filter_path=*.mappings.properties"
    {
      "app-logs-2026.04" : {
        "mappings" : {
          "properties" : {
            "level" : {
              "type" : "keyword"
            },
            "message" : {
              "type" : "text"
            },
            "timestamp" : {
              "type" : "date"
            }
          }
        }
      }
    }
  6. Retrieve the stored template definition for review.
    $ curl --silent --show-error --fail "http://localhost:9200/_index_template/app-logs-template?pretty&filter_path=index_templates.name,index_templates.index_template.index_patterns,index_templates.index_template.priority,index_templates.index_template.version,index_templates.index_template._meta,index_templates.index_template.template.settings.index.number_of_shards,index_templates.index_template.template.settings.index.number_of_replicas,index_templates.index_template.template.settings.index.refresh_interval"
    {
      "index_templates" : [
        {
          "name" : "app-logs-template",
          "index_template" : {
            "index_patterns" : [
              "app-logs-*"
            ],
            "template" : {
              "settings" : {
                "index" : {
                  "number_of_shards" : "1",
                  "number_of_replicas" : "0",
                  "refresh_interval" : "30s"
                }
              }
            },
            "priority" : 300,
            "version" : 1,
            "_meta" : {
              "description" : "Defaults for application log indices"
            }
          }
        }
      ]
    }
  7. Update the template with a higher version and additional defaults.
    $ curl --silent --show-error --fail --header "Content-Type: application/json" --request PUT "http://localhost:9200/_index_template/app-logs-template?pretty" --data '{
      "index_patterns": ["app-logs-*"],
      "priority": 300,
      "version": 2,
      "_meta": {
        "description": "Defaults for application log indices"
      },
      "template": {
        "settings": {
          "number_of_shards": 1,
          "number_of_replicas": 0,
          "refresh_interval": "5s"
        },
        "mappings": {
          "properties": {
            "timestamp": { "type": "date" },
            "level": { "type": "keyword" },
            "message": { "type": "text" },
            "service": {
              "properties": {
                "name": { "type": "keyword" }
              }
            }
          }
        },
        "aliases": {
          "app-logs-search": {}
        }
      }
    }'
    {
      "acknowledged" : true
    }

    Updating an index template changes only future indices that match the pattern. Existing indices keep the settings, mappings, and aliases they already received at creation time.

  8. Create a new matching index after the update.
    $ curl --silent --show-error --fail --request PUT "http://localhost:9200/app-logs-2026.05?pretty"
    {
      "acknowledged" : true,
      "shards_acknowledged" : true,
      "index" : "app-logs-2026.05"
    }
  9. Verify the updated refresh interval on the newly created index.
    $ curl --silent --show-error --fail "http://localhost:9200/app-logs-2026.05/_settings?pretty&filter_path=*.settings.index.refresh_interval,*.settings.index.number_of_replicas"
    {
      "app-logs-2026.05" : {
        "settings" : {
          "index" : {
            "refresh_interval" : "5s",
            "number_of_replicas" : "0"
          }
        }
      }
    }
  10. Verify the new alias on the newly created index.
    $ curl --silent --show-error --fail "http://localhost:9200/_alias/app-logs-search?pretty"
    {
      "app-logs-2026.05" : {
        "aliases" : {
          "app-logs-search" : { }
        }
      }
    }
  11. Verify the new field mapping on the newly created index.
    $ curl --silent --show-error --fail "http://localhost:9200/app-logs-2026.05/_mapping?pretty&filter_path=*.mappings.properties.service.properties.name"
    {
      "app-logs-2026.05" : {
        "mappings" : {
          "properties" : {
            "service" : {
              "properties" : {
                "name" : {
                  "type" : "keyword"
                }
              }
            }
          }
        }
      }
    }

    The original app-logs-2026.04 index keeps the older 30s refresh interval and does not gain the new alias or service.name mapping retroactively.

  12. List matching templates to confirm the stored version.
    $ curl --silent --show-error --fail "http://localhost:9200/_index_template/app-logs-*?pretty&filter_path=index_templates.name,index_templates.index_template.version"
    {
      "index_templates" : [
        {
          "name" : "app-logs-template",
          "index_template" : {
            "version" : 2
          }
        }
      ]
    }
  13. Delete the first test index after validation.
    $ curl --silent --show-error --fail --request DELETE "http://localhost:9200/app-logs-2026.04?pretty"
    {
      "acknowledged" : true
    }

    Deleting an index permanently removes the documents and index-level metadata stored in it.

  14. Delete the second test index after validation.
    $ curl --silent --show-error --fail --request DELETE "http://localhost:9200/app-logs-2026.05?pretty"
    {
      "acknowledged" : true
    }
  15. Delete the example template after validation.
    $ curl --silent --show-error --fail --request DELETE "http://localhost:9200/_index_template/app-logs-template?pretty"
    {
      "acknowledged" : true
    }

    Deleting the template stops its defaults from applying to future indices.