Appearance
APISIX Configuration Reference
APISIX configuration lives in layers/api-gateway/config/apisix.yaml. This file is mounted into the APISIX container and controls the core proxy, plugin loading, admin API, etcd connection, and observability.
Core Settings
yaml
apisix:
node_listen: 9080
enable_admin: true
enable_websocket: true
admin_listen:
port: 9180
extra_lua_path: "/usr/local/apisix/apisix/plugins/custom/?.lua;/usr/local/apisix/apisix/plugins/custom/?/init.lua;"| Setting | Value | Description |
|---|---|---|
node_listen | 9080 | Main proxy port. All API traffic enters here. |
enable_admin | true | Enables the Admin API for route/consumer management. |
enable_websocket | true | Enables WebSocket proxying. |
admin_listen.port | 9180 | Admin API port. |
extra_lua_path | (see above) | Adds the custom plugin directory to the Lua module search path. |
Deployment
Admin API
yaml
deployment:
admin:
admin_key:
- name: admin
key: $\{\{APISIX_ADMIN_KEY\}\}
role: admin
allow_admin:
- 127.0.0.0/24
- 172.16.0.0/12
- 192.168.0.0/16
- 10.0.0.0/8| Setting | Description |
|---|---|
admin_key | Admin API key. Loaded from APISIX_ADMIN_KEY environment variable. Never hardcode. |
allow_admin | CIDR blocks allowed to access the Admin API. Restricted to private networks. |
DANGER
The Admin API key is set via the $\{\{APISIX_ADMIN_KEY\}\} environment variable. Never commit this value. Use the X-API-KEY header for all Admin API requests.
Example Admin API call:
bash
curl http://localhost:9180/apisix/admin/routes \
-H "X-API-KEY: $APISIX_ADMIN_KEY"etcd Connection
yaml
deployment:
etcd:
host:
- "http://etcd:2379"
prefix: "/apisix"
timeout: 30| Setting | Value | Description |
|---|---|---|
host | http://etcd:2379 | etcd endpoint for APISIX configuration storage. |
prefix | /apisix | Key prefix in etcd. All APISIX config stored under this path. |
timeout | 30 | Connection timeout in seconds. |
Plugins
All enabled plugins are declared in the plugins list. Only plugins listed here can be used in route configurations.
Standard Plugins
Rate Limiting
| Plugin | Description |
|---|---|
limit-count | Fixed window counter rate limiting |
limit-req | Request rate limiting (leaky bucket) |
limit-conn | Concurrent connection limiting |
Authentication
| Plugin | Description |
|---|---|
jwt-auth | JWT validation (used with Keycloak) |
key-auth | API key authentication |
basic-auth | HTTP Basic authentication |
openid-connect | OpenID Connect (Keycloak integration) |
Traffic Management
| Plugin | Description |
|---|---|
proxy-rewrite | Rewrite upstream URI, host, headers |
response-rewrite | Rewrite response headers/body |
redirect | HTTP redirect |
cors | Cross-Origin Resource Sharing |
ip-restriction | IP allowlist/denylist |
request-id | Generate unique request ID |
real-ip | Extract real client IP from proxy headers |
api-breaker | Circuit breaker for upstream failures |
traffic-split | Traffic splitting for canary/A-B deployments |
gRPC Support
| Plugin | Description |
|---|---|
grpc-transcode | Transcode HTTP/JSON to gRPC |
grpc-web | gRPC-Web protocol support |
Logging
| Plugin | Description |
|---|---|
http-logger | Send access logs via HTTP |
tcp-logger | Send access logs via TCP |
udp-logger | Send access logs via UDP |
Observability
| Plugin | Description |
|---|---|
prometheus | Expose Prometheus metrics |
opentelemetry | Distributed tracing via OTel |
Extension
| Plugin | Description |
|---|---|
ext-plugin-pre-req | External plugin runner (pre-request) |
ext-plugin-post-resp | External plugin runner (post-response) |
serverless-pre-function | Inline Lua pre-request |
serverless-post-function | Inline Lua post-response |
Custom Plugins
tenant-rate-limit
Per-tenant sliding window rate limiter backed by Redis. Runs in the access phase at priority 1001 (after auth, before most other plugins).
Source: layers/api-gateway/plugins/tenant-rate-limit/
Schema (from schema.lua):
| Parameter | Type | Default | Description |
|---|---|---|---|
redis_host | string | "gw-redis" | Redis hostname |
redis_port | integer | 6379 | Redis port |
redis_timeout | integer | 1000 | Redis connection timeout (ms) |
redis_pool_size | integer | 100 | Redis connection pool size |
default_count | integer | 100 | Default request limit per window |
time_window | integer | 60 | Time window in seconds |
rejected_code | integer | 429 | HTTP status code for rate-limited requests |
rejected_msg | string | "Rate limit exceeded for tenant" | Error message for rate-limited requests |
tenant_header | string | "X-Tenant-ID" | Header to extract tenant_id from |
allow_missing_tenant | boolean | false | If false, reject requests without tenant_id |
missing_tenant_code | integer | 401 | HTTP status code when tenant_id is missing |
show_limit_header | boolean | true | Include X-RateLimit-* headers in response |
Redis key patterns used:
rl:{tenant_id}:{route_id}:{window}-- sliding window sorted setrl:config:{tenant_id}-- per-tenant limit override
Response headers (when show_limit_header is true):
X-RateLimit-Limit-- max requests per windowX-RateLimit-Remaining-- remaining requestsX-RateLimit-Reset-- Unix timestamp when window resetsX-Tenant-ID-- tenant identifier
Behavior:
- Uses an atomic Redis Lua script for sliding window counting (single round-trip)
- Checks for per-tenant limit override in
rl:config:{tenant_id}before applying default - Fails open if Redis is unreachable (requests are allowed through)
- Caches the Redis script SHA for subsequent calls via
EVALSHA
Route configuration example:
bash
curl http://localhost:9180/apisix/admin/routes/1 \
-H "X-API-KEY: $APISIX_ADMIN_KEY" \
-X PUT -d '{
"uri": "/api/*",
"upstream": {"type": "roundrobin", "nodes": {"ai-gateway:4000": 1}},
"plugins": {
"tenant-rate-limit": {
"default_count": 500,
"time_window": 60,
"redis_host": "gw-redis",
"redis_port": 6379
}
}
}'clickhouse-logger
Batched request log writer to ClickHouse via the HTTP interface. Runs in the log phase at priority 399.
Source: layers/api-gateway/plugins/clickhouse-logger/init.lua
Schema:
| Parameter | Type | Default | Description |
|---|---|---|---|
clickhouse_url | string | "http://clickhouse:8123" | ClickHouse HTTP endpoint |
database | string | "gateway" | Target database |
table | string | "request_log" | Target table (the Buffer table) |
timeout | integer | 5000 | HTTP request timeout (ms) |
batch_max_size | integer | 100 | Flush buffer when this many entries accumulate |
inactive_timeout | integer | 5 | (Schema default; periodic flush runs every 5 seconds) |
buffer_duration | integer | 60 | (Schema default for buffer duration) |
max_retry_count | integer | 3 | Max retries on write failure |
retry_delay | integer | 1 | Delay between retries (seconds) |
tenant_header | string | "X-Tenant-ID" | Header to extract tenant_id from |
Behavior:
- Buffers log entries per Nginx worker process
- Flushes when buffer reaches
batch_max_sizeor every 5 seconds (timer) - Writes to ClickHouse using
INSERT INTO ... FORMAT JSONEachRow - Extracts
tenant_idfrom header, rate-limit plugin context, or JWT payload - Scrubs
Authorizationheader -- never logs tokens or credentials - Log entry fields:
timestamp,tenant_id,route_id,method,path,status_code,latency_ms,request_size,response_size,client_ip,user_agent,trace_id
Route configuration example:
bash
curl http://localhost:9180/apisix/admin/routes/1 \
-H "X-API-KEY: $APISIX_ADMIN_KEY" \
-X PATCH -d '{
"plugins": {
"clickhouse-logger": {
"clickhouse_url": "http://clickhouse:8123",
"database": "gateway",
"table": "request_log",
"batch_max_size": 200
}
}
}'Plugin Attributes
Plugin attributes configure global behavior for plugins (shared across all routes).
prometheus
yaml
plugin_attr:
prometheus:
export_uri: /apisix/prometheus/metrics
export_addr:
ip: 0.0.0.0
port: 9091Exposes Prometheus metrics on a dedicated port (9091) separate from the main proxy port. Scraped by Prometheus at apisix:9091.
opentelemetry
yaml
plugin_attr:
opentelemetry:
resource:
service.name: apisix
collector:
address: otel-collector:4317
request_timeout: 3
trace_id_source: x-request-id
batch_span_processor:
max_queue_size: 1024
batch_timeout: 2
inactive_timeout: 1
max_export_batch_size: 16| Setting | Value | Description |
|---|---|---|
resource.service.name | apisix | Service name in traces |
collector.address | otel-collector:4317 | OTel Collector gRPC endpoint |
collector.request_timeout | 3 | Timeout for sending spans (seconds) |
trace_id_source | x-request-id | Use X-Request-ID header as trace ID |
batch_span_processor.max_queue_size | 1024 | Max queued spans before drop |
batch_span_processor.batch_timeout | 2 | Max wait before sending a batch (seconds) |
batch_span_processor.inactive_timeout | 1 | Timeout for inactive batches (seconds) |
batch_span_processor.max_export_batch_size | 16 | Max spans per export batch |
http-logger
yaml
plugin_attr:
http-logger:
inactive_timeout: 2
batch_max_size: 1000
buffer_duration: 60| Setting | Value | Description |
|---|---|---|
inactive_timeout | 2 | Flush after 2 seconds of inactivity |
batch_max_size | 1000 | Max entries per batch |
buffer_duration | 60 | Max buffer duration in seconds |
Custom Plugin Directory Structure
Custom plugins are loaded from the extra_lua_path directory. Each plugin follows this structure:
layers/api-gateway/plugins/
tenant-rate-limit/
handler.lua # Plugin lifecycle (access phase)
schema.lua # JSON schema for config validation
init.lua # Alternative entry point (same logic)
clickhouse-logger/
init.lua # Plugin lifecycle (log phase) + schema:::note Plugins must be listed in the plugins array in apisix.yaml to be loadable. Adding a plugin directory without listing it in the config will not enable it. :::