How to apply an Elasticsearch index template for Logstash data

Applying an Elasticsearch index template to Logstash indices keeps new indices predictable before the first event lands. A template pins field mappings and shard settings up front, which helps prevent dynamic mapping drift, unexpected text-versus-date conflicts, and inconsistent shard layouts across daily Logstash indices.

Current Elasticsearch releases use composable index templates through /_index_template. The template matches new indices by index_patterns, resolves conflicts with priority, and applies the resulting settings and mappings when the index is created. On the Logstash side, the elasticsearch output decides the final index name, so the template pattern must match the exact value produced by index ⇒.

This workflow targets classic indices rather than data streams. Templates affect only new indices, not existing ones, and current Logstash output behavior matters: leaving manage_template enabled lets the plugin install its own default logstash-* template, while leaving ILM enabled can replace a custom index ⇒ name with a rollover alias. Use an application-specific pattern such as logstash-app-*, set manage_template to false, and disable ILM in this workflow unless the pipeline is intentionally using the separate ILM pattern. On secured clusters, switch the examples to https and add credentials or an API key.

Steps to apply an Elasticsearch index template for Logstash data:

  1. Create an index template that matches the Logstash index name.
    $ curl --silent --show-error --fail \
      --header "Content-Type: application/json" \
      --request PUT "http://elasticsearch.example.net:9200/_index_template/logstash-app?pretty" \
      --data '{
      "index_patterns": ["logstash-app-*"],
      "priority": 300,
      "template": {
        "settings": {
          "number_of_shards": 1,
          "number_of_replicas": 0
        },
        "mappings": {
          "properties": {
            "@timestamp": { "type": "date" },
            "message": { "type": "text" }
          }
        }
      }
    }'
    {
      "acknowledged" : true
    }

    Use an application-specific pattern instead of a broad prefix such as logs-* unless you deliberately intend to overlap Elastic built-in or Fleet-managed templates. Creating or updating index templates also requires an Elasticsearch cluster privilege such as manage_index_templates.

    A single-node validation cluster commonly uses number_of_replicas set to 0 so the new index can become green immediately. Raise the replica count on multi-node clusters.

  2. Update the Logstash output block so the index name matches the template pattern and Logstash does not install or override templates automatically.
    output {
      elasticsearch {
        hosts => ["http://elasticsearch.example.net:9200"]
        index => "logstash-app-%{+yyyy.MM.dd}"
        manage_template => false
        ilm_enabled => false
      }
    }

    manage_template => false keeps the plugin from installing its default logstash-* template. ilm_enabled => false keeps the explicit daily index ⇒ pattern in effect instead of letting an ILM rollover alias take precedence.

    On secured clusters, switch the host to https and add the current TLS and authentication settings such as ssl_enabled, ssl_certificate_authorities, user with password, or api_key.

  3. Test the Logstash pipeline configuration under the same service account the package uses.
    $ sudo -u logstash /usr/share/logstash/bin/logstash --path.settings /etc/logstash --path.data /tmp/logstash-template-configtest --config.test_and_exit
    Using bundled JDK: /usr/share/logstash/jdk
    Configuration OK
    [2026-04-07T14:21:08,214][INFO ][logstash.runner          ] Using config.test_and_exit mode. Config Validation Result: OK. Exiting Logstash

    The temporary --path.data directory must be writable by the logstash user. This check validates pipeline syntax and plugin options only; it does not prove Elasticsearch connectivity or privileges.

    Current Logstash 9.x packages block superuser runs by default, so test the pipeline as the logstash service account unless allow_superuser is intentionally enabled.

  4. Restart the Logstash service so the updated pipeline is loaded.
    $ sudo systemctl restart logstash

    Restarting Logstash briefly pauses ingestion while the service stops and starts the pipeline workers again.

  5. Review recent Logstash service logs for startup, authentication, or bulk-indexing failures.
    $ sudo journalctl --unit logstash --since "5 minutes ago" --no-pager
    Apr 07 14:24:11 host logstash[21457]: [2026-04-07T14:24:11,351][INFO ][logstash.outputs.elasticsearch][main] Elasticsearch pool URLs updated {:changes=>{:removed=>[], :added=>[http://elasticsearch.example.net:9200/]}}
    Apr 07 14:24:11 host logstash[21457]: [2026-04-07T14:24:11,884][INFO ][logstash.javapipeline    ][main] Pipeline started {"pipeline.id"=>"main"}
    ##### snipped #####

    400 mapping errors, template conflicts, and 403 privilege failures usually show up here before they are obvious in the index listing.

  6. Retrieve the stored template definition from Elasticsearch.
    $ curl --silent --show-error --fail \
      "http://elasticsearch.example.net:9200/_index_template/logstash-app?pretty&filter_path=index_templates.name,index_templates.index_template.index_patterns,index_templates.index_template.priority,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.mappings.properties"
    {
      "index_templates" : [
        {
          "name" : "logstash-app",
          "index_template" : {
            "index_patterns" : [
              "logstash-app-*"
            ],
            "template" : {
              "settings" : {
                "index" : {
                  "number_of_shards" : "1",
                  "number_of_replicas" : "0"
                }
              },
              "mappings" : {
                "properties" : {
                  "@timestamp" : {
                    "type" : "date"
                  },
                  "message" : {
                    "type" : "text"
                  }
                }
              }
            },
            "priority" : 300
          }
        }
      ]
    }

    This confirms the stored name, matching pattern, and the exact settings and mappings that Elasticsearch will try to apply to future matching indices.

  7. Simulate template resolution for a Logstash-style index name before relying on live ingestion.
    $ curl --silent --show-error --fail \
      --request POST "http://elasticsearch.example.net:9200/_index_template/_simulate_index/logstash-app-2026.04.07?pretty&filter_path=template.settings.index.number_of_shards,template.settings.index.number_of_replicas,template.mappings.properties,overlapping"
    {
      "template" : {
        "settings" : {
          "index" : {
            "number_of_shards" : "1",
            "number_of_replicas" : "0"
          }
        },
        "mappings" : {
          "properties" : {
            "@timestamp" : {
              "type" : "date"
            },
            "message" : {
              "type" : "text"
            }
          }
        }
      },
      "overlapping" : [ ]
    }

    Simulation shows the resolved result without waiting for a new index to be created. A non-empty overlapping array means another matching template is still in play.

  8. After the next matching event reaches Elasticsearch, list the resulting index.
    $ curl --silent --show-error --fail \
      "http://elasticsearch.example.net:9200/_cat/indices/logstash-app-*?h=health,status,index,docs.count,store.size&v&allow_no_indices=true"
    health status index                   docs.count store.size
    green  open   logstash-app-2026.04.07          1      4.8kb

    If no matching index exists yet, send a fresh event through the pipeline and run the command again. A yellow index on a single-node cluster usually means the template requested replicas that cannot be assigned yet.

  9. Check the new index settings to confirm the template applied the intended shard and replica values.
    $ curl --silent --show-error --fail \
      "http://elasticsearch.example.net:9200/logstash-app-2026.04.07/_settings?pretty&filter_path=*.settings.index.number_of_shards,*.settings.index.number_of_replicas"
    {
      "logstash-app-2026.04.07" : {
        "settings" : {
          "index" : {
            "number_of_shards" : "1",
            "number_of_replicas" : "0"
          }
        }
      }
    }

    Replace the date-based index name with the current index returned by the previous step when Logstash is rotating indices daily.

  10. Check the new index mappings to confirm Elasticsearch applied the template's field types.
    $ curl --silent --show-error --fail \
      "http://elasticsearch.example.net:9200/logstash-app-2026.04.07/_mapping?pretty&filter_path=*.mappings.properties.@timestamp,*.mappings.properties.message"
    {
      "logstash-app-2026.04.07" : {
        "mappings" : {
          "properties" : {
            "@timestamp" : {
              "type" : "date"
            },
            "message" : {
              "type" : "text"
            }
          }
        }
      }
    }

    If the mapping does not match the template, the index was usually created before the template existed or a higher-priority overlapping template won the match.