Skill for configuring the Apache APISIX external plugin system (ext-plugin-pre-req, ext-plugin-post-req, ext-plugin-post-resp) via the a6 CLI. Covers Plugin Runner architecture, configuration for Go/Java/Python runners, RPC protocol, graceful degradation, and performance considerations.
The APISIX external plugin system lets you run plugins written in Go, Java, Python, or JavaScript via a Plugin Runner process. APISIX communicates with the runner over a Unix socket using FlatBuffers serialization.
Three plugins control when external plugins execute:
| Plugin | Phase | Priority | Description |
|---|---|---|---|
ext-plugin-pre-req | rewrite | 12000 | Before built-in Lua plugins |
ext-plugin-post-req | access | −3000 | After Lua plugins, before upstream |
ext-plugin-post-resp | before_proxy | −4000 | After upstream response received |
All three plugins share the same schema:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
conf | array | No | — | List of external plugins to execute |
conf[].name | string | Yes | — | Plugin identifier (1–128 chars) |
conf[].value | string | Yes | — | JSON string configuration passed to the plugin |
allow_degradation | boolean | No | false | When true, requests continue if runner is unavailable |
┌──────────┐ Unix Socket ┌───────────────┐
│ APISIX │ ◄──────────────► │ Plugin Runner │
│ (Nginx) │ FlatBuffers │ (Go/Java/Py) │
└──────────┘ └───────────────┘
ext-plugin-* trigger, APISIX sends an RPC over Unix socket| Language | Repository | Status |
|---|---|---|
| Go | apache/apisix-go-plugin-runner | GA |
| Java | apache/apisix-java-plugin-runner | GA |
| Python | apache/apisix-python-plugin-runner | Experimental |
| JavaScript | zenozeng/apisix-javascript-plugin-runner | Community |
APISIX manages the runner as a subprocess:
ext-plugin:
cmd: ["/path/to/runner-executable", "run"]
# Go runner
ext-plugin:
cmd: ["/opt/apisix-go-runner", "run"]
# Java runner
ext-plugin:
cmd: ["java", "-jar", "-Xmx1g", "-Xms1g", "/opt/apisix-runner.jar"]
# Python runner
ext-plugin:
cmd: ["python3", "/opt/apisix-python-runner/apisix/main.py", "start"]
For local development, run the runner separately:
# APISIX config.yaml — do NOT set cmd
ext-plugin:
path_for_test: "/tmp/runner.sock"
# Start runner manually
APISIX_LISTEN_ADDRESS=unix:/tmp/runner.sock ./runner run
Pass environment variables to the runner:
nginx_config:
envs:
- MY_ENV_VAR
- DATABASE_URL
a6 route create -f - <<'EOF'
{
"id": "ext-auth",
"uri": "/api/*",
"plugins": {
"ext-plugin-pre-req": {
"conf": [
{"name": "AuthFilter", "value": "{\"token_required\":true}"}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {"backend:8080": 1}
}
}
EOF
a6 route create -f - <<'EOF'
{
"id": "ext-chain",
"uri": "/api/*",
"plugins": {
"ext-plugin-pre-req": {
"conf": [
{"name": "AuthFilter", "value": "{\"token_required\":true}"},
{"name": "RateLimiter", "value": "{\"requests_per_second\":100}"}
],
"allow_degradation": true
}
},
"upstream": {
"type": "roundrobin",
"nodes": {"backend:8080": 1}
}
}
EOF
a6 route create -f - <<'EOF'
{
"id": "full-ext",
"uri": "/api/*",
"plugins": {
"ext-plugin-pre-req": {
"conf": [{"name": "auth-check", "value": "{}"}]
},
"ext-plugin-post-req": {
"conf": [{"name": "request-transform", "value": "{}"}]
},
"ext-plugin-post-resp": {
"conf": [{"name": "response-logger", "value": "{}"}]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {"backend:8080": 1}
}
}
EOF
Execution order: pre-req → (Lua plugins) → post-req → (upstream) → post-resp