Log with Elasticsearch
Elasticsearch is a popular JSON-based datastore for storing and indexing large volumes of data. It is often used to store logs from various sources and works with tools like Logstash and Kibana to form an entire observability stack known as the Elastic (ELK) Stack.
APISIX supports forwarding its logs directly to Elasticsearch through the elasticsearch-logger plugin. These logs can then be searched, filtered, and visualized through Kibana to gather insights to manage applications.
This guide will show you how to enable the elasticsearch-logger plugin to integrate APISIX with the ELK stack for observability.
Prerequisite(s)
- Install Docker.
- Install cURL to send requests to the services for validation.
- Follow the Getting Started tutorial to start a new APISIX instance in Docker.
Start Elasticsearch and Kibana
Set passwords for the built-in Elasticsearch users used in this local example:
export ELASTIC_PASSWORD="apisix-elastic-pass"
export KIBANA_SYSTEM_PASSWORD="apisix-kibana-pass"
Create a data volume and start Elasticsearch with authentication enabled:
docker volume create apisix-quickstart-elasticsearch-data
docker run -d --name apisix-quickstart-elasticsearch \
--network apisix-quickstart-net \
-p 9200:9200 \
-e discovery.type=single-node \
-e xpack.security.enabled=true \
-e xpack.security.http.ssl.enabled=false \
-e ELASTIC_PASSWORD="${ELASTIC_PASSWORD}" \
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
-v apisix-quickstart-elasticsearch-data:/usr/share/elasticsearch/data \
docker.elastic.co/elasticsearch/elasticsearch:9.4.2
❶ Attach Elasticsearch to the APISIX quickstart network so that APISIX and Kibana can reach it by container name.
❷ Disable HTTP TLS only for this isolated local example. In production, use HTTPS endpoints and keep the plugin's ssl_verify option enabled so that APISIX verifies the Elasticsearch certificate.
❸ Persist Elasticsearch data in the named Docker volume created above.
Wait up to two minutes for Elasticsearch to start, then verify that authentication succeeds:
curl --fail --silent --show-error \
--user "elastic:${ELASTIC_PASSWORD}" \
--retry 24 \
--retry-delay 5 \
--retry-connrefused \
--retry-max-time 120 \
--max-time 5 \
--output /dev/null \
"http://127.0.0.1:9200"
Set the password Kibana uses to connect to Elasticsearch:
curl -fsS -u "elastic:${ELASTIC_PASSWORD}" \
"http://127.0.0.1:9200/_security/user/kibana_system/_password" \
-X POST \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"password": "${KIBANA_SYSTEM_PASSWORD}"
}
EOF
Create a data volume and start the matching Kibana version:
docker volume create apisix-quickstart-kibana-data
docker run -d --name apisix-quickstart-kibana \
--network apisix-quickstart-net \
-p 5601:5601 \
-e ELASTICSEARCH_HOSTS="http://apisix-quickstart-elasticsearch:9200" \
-e ELASTICSEARCH_USERNAME="kibana_system" \
-e ELASTICSEARCH_PASSWORD="${KIBANA_SYSTEM_PASSWORD}" \
-v apisix-quickstart-kibana-data:/usr/share/kibana/data \
docker.elastic.co/kibana/kibana:9.4.2
Enable elasticsearch-logger Plugin
Enable elasticsearch-logger globally and create a sample route to generate logs. Alternatively, you can enable the plugin on a route.
- Admin API
- ADC
Enable the elasticsearch-logger plugin on all routes:
curl "http://127.0.0.1:9180/apisix/admin/global_rules/elasticsearch" -X PUT \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"plugins": {
"elasticsearch-logger": {
"endpoint_addrs": [
"http://apisix-quickstart-elasticsearch:9200"
],
"field": {
"index": "gateway-{%Y.%m.%d}"
},
"auth": {
"username": "elastic",
"password": "${ELASTIC_PASSWORD}"
},
"timeout": 60,
"retry_delay": 1,
"buffer_duration": 60,
"max_retry_count": 0,
"batch_max_size": 5,
"inactive_timeout": 5
}
}
}
EOF
❶ Create a daily index by resolving the date expression, for example, to gateway-2026.07.21.
Create a sample route. Setting pass_host to node sends the upstream hostname in the Host header:
curl "http://127.0.0.1:9180/apisix/admin/routes/observability-logs" -X PUT \
-H "Content-Type: application/json" \
-d '{
"uri": "/get",
"upstream": {
"type": "roundrobin",
"pass_host": "node",
"nodes": {
"postman-echo.com:80": 1
}
}
}'
global_rules:
elasticsearch-logger:
endpoint_addrs:
- http://apisix-quickstart-elasticsearch:9200
field:
index: gateway-{%Y.%m.%d}
auth:
username: elastic
password: apisix-elastic-pass
timeout: 60
retry_delay: 1
buffer_duration: 60
max_retry_count: 0
batch_max_size: 5
inactive_timeout: 5
services:
- name: postman-echo
routes:
- name: observability-logs
uris:
- /get
upstream:
type: roundrobin
pass_host: node
nodes:
- host: postman-echo.com
port: 80
weight: 1
❶ Create a daily index by resolving the date expression, for example, to gateway-2026.07.21.
Synchronize the configurations to APISIX:
adc sync -f adc-elasticsearch.yaml
Customize Log Format
Customize the log format for elasticsearch-logger before generating traffic. The log format of most APISIX logging plugins can be customized locally on the plugin (e.g. bound to a route) and/or globally with plugin metadata.
Add host address, timestamp, and client IP address to the logs with built-in variables:
- Admin API
- ADC
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger" -X PUT \
-H "Content-Type: application/json" \
--data-binary @- <<'EOF'
{
"log_format": {
"host": "$host",
"timestamp": "$time_iso8601",
"client_ip": "$remote_addr"
}
}
EOF
plugin_metadata:
elasticsearch-logger:
log_format:
host: $host
timestamp: $time_iso8601
client_ip: $remote_addr
Synchronize the configurations to APISIX:
adc sync -f adc-plugin-metadata.yaml -f adc-elasticsearch.yaml
Configure Kibana
Send ten requests, then allow the inactive buffer to flush to Elasticsearch:
for i in {1..10}; do
curl -sS "http://127.0.0.1:9080/get" > /dev/null
done
sleep 6
List the date-based indices:
curl -u "elastic:${ELASTIC_PASSWORD}" \
"http://127.0.0.1:9200/_cat/indices/gateway-*?v"
You should see a gateway-YYYY.MM.DD index with documents. Inspect one indexed document:
curl -u "elastic:${ELASTIC_PASSWORD}" \
"http://127.0.0.1:9200/gateway-*/_search?pretty&size=1"
The document _source should contain host, timestamp, and client_ip fields.
View Logs in Kibana
Open Kibana and sign in as elastic with the password stored in ELASTIC_PASSWORD.
Go to Stack Management > Data Views, select Create data view, and configure these values:
- Name:
APISIX logs - Index pattern:
gateway-* - Timestamp field:
timestamp

Select Save data view to Kibana, then go to Discover and select the APISIX logs data view. The indexed gateway requests should be available for searching and filtering.

Next Steps
See elasticsearch-logger plugin reference to learn more about the plugin configuration options.