Skip to main content

a6-plugin-ext-plugin

Overview

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:

PluginPhasePriorityDescription
ext-plugin-pre-reqrewrite12000Before built-in Lua plugins
ext-plugin-post-reqaccess−3000After Lua plugins, before upstream
ext-plugin-post-respbefore_proxy−4000After upstream response received

When to Use

  • Implement custom logic in Go, Java, or Python instead of Lua
  • Reuse existing business logic from non-Lua codebases
  • Apply pre-processing (auth, validation) or post-processing (response transform)
  • Teams that prefer statically-typed languages over Lua

Plugin Configuration Reference

All three plugins share the same schema:

FieldTypeRequiredDefaultDescription
confarrayNoList of external plugins to execute
conf[].namestringYesPlugin identifier (1–128 chars)
conf[].valuestringYesJSON string configuration passed to the plugin
allow_degradationbooleanNofalseWhen true, requests continue if runner is unavailable

Plugin Runner Architecture

┌──────────┐ Unix Socket ┌───────────────┐
│ APISIX │ ◄──────────────► │ Plugin Runner │
│ (Nginx) │ FlatBuffers │ (Go/Java/Py) │
└──────────┘ └───────────────┘
  1. APISIX starts the runner as a subprocess (managed lifecycle)
  2. On ext-plugin-* trigger, APISIX sends an RPC over Unix socket
  3. Runner executes external plugins and returns the result
  4. APISIX applies modifications (headers, body, status) to the request/response

RPC Protocol

  • PrepareConf: Syncs plugin configuration → returns a conf token (cached)
  • HTTPReqCall: Per-request execution with serialized HTTP data + conf token
  • ExtraInfo: Runner can request additional data (variables, body, response)

Supported Plugin Runners

LanguageRepositoryStatus
Goapache/apisix-go-plugin-runnerGA
Javaapache/apisix-java-plugin-runnerGA
Pythonapache/apisix-python-plugin-runnerExperimental
JavaScriptzenozeng/apisix-javascript-plugin-runnerCommunity

APISIX Configuration (config.yaml)

Production Setup

APISIX manages the runner as a subprocess:

ext-plugin:
cmd: ["/path/to/runner-executable", "run"]

Runner-Specific Commands

# 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"]

Development Setup (Standalone Runner)

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

Environment Variables

Pass environment variables to the runner:

nginx_config:
envs:
- MY_ENV_VAR
- DATABASE_URL

Step-by-Step Examples

1. Single External Plugin

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

2. Multiple External Plugins with Degradation

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

3. All Three Plugin Types (Full Request Lifecycle)

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

Config Sync Example

version: "1"
routes:
- id: ext-plugin-demo
uri: /api/*
plugins:
ext-plugin-pre-req:
conf:
- name: AuthFilter
value: '{"token_required":true}'
allow_degradation: true
upstream_id: my-upstream

Compatibility Matrix

Featureext-plugin-pre-reqext-plugin-post-reqext-plugin-post-resp
Phaserewriteaccessbefore_proxy
RunsBefore Lua pluginsAfter Lua pluginsAfter upstream response
proxy-mirror
proxy-cache
proxy-control
mTLS to upstream

ext-plugin-post-resp limitation: Uses lua-resty-http internally, which makes it incompatible with proxy-mirror, proxy-cache, proxy-control, and mTLS to upstream.

Performance Considerations

  • Unix socket + FlatBuffers: Low-latency IPC, no TCP overhead
  • Conf token caching: PrepareConf called once per config change, not per request
  • Process management: APISIX sends SIGTERM then SIGKILL (1s grace) on reload
  • Degradation mode: Enable allow_degradation: true for non-critical plugins
  • Connection reuse: Runner should reuse socket connections

Troubleshooting

SymptomCauseFix
failed to receive RPC_PREPARE_CONFRunner not listening or socket path mismatchVerify path_for_test matches APISIX_LISTEN_ADDRESS
503 Service UnavailableRunner crashed or not startedCheck runner logs; verify cmd path is correct
Runner not receiving env varsNginx hides env vars by defaultAdd vars to nginx_config.envs in config.yaml
Slow response timesExternal plugin doing heavy workProfile runner; consider async processing
ext-plugin-post-resp conflictsIncompatible with proxy-* pluginsUse ext-plugin-post-req instead, or remove proxy-mirror/cache

This page is generated from a6-plugin-ext-plugin/SKILL.md in the api7/a6 repository. Browse all skills on the AI Agent Skills page.