Building External Plugins for APISIX: Deep Dive into Cloud‑Native Gateway Extensions
This article explains APISIX’s multi‑process architecture and request lifecycle, then explores various ways to develop external plugins—including SideCar‑based plugins, WASM modules, and LuaJIT FFI—detailing their implementation steps, advantages, and limitations to help developers choose the optimal approach for extending the cloud‑native gateway.
APISIX Plugin System: External Plugin Practice
Background
APISIX is a dynamic, real‑time, high‑performance cloud‑native gateway built on OpenResty. It serves as a traffic entry point and provides features such as dynamic routing, upstreams, certificates, A/B testing, canary releases, blue‑green deployments, rate limiting, attack protection, metrics collection, monitoring, observability, and service governance.
A major highlight of APISIX is its rich and flexible plugin module. It ships with many built‑in Lua plugins covering authentication, security, observability, traffic management, and multi‑protocol support, which can be used out‑of‑the‑box. Lua plugins support hot‑update and hot‑swap without restarting APISIX. Users can also develop custom plugins in Lua or other languages.
APISIX Multi‑Process Model
Before discussing plugins, it is useful to understand APISIX’s multi‑process model.
master: reads and parses the Nginx configuration, starts and monitors worker processes.
worker: handles requests; workers are independent and each has its own event loop.
luajit: the Lua VM embedded in OpenResty; a Lua coroutine is created for each request, providing isolation.
APISIX Request Lifecycle
OpenResty defines a lifecycle for each request that enters the luajit instance. The request flows through several stages until completion.
APISIX plugin extension points correspond to these OpenResty execution phases, allowing developers to inject custom logic.
Note that the check_schema and before_proxy stages are added by APISIX and do not map to OpenResty phases.
APISIX Plugin System
Lua Plugins
Built‑in plugins are written in Lua; see the official plugin development guide for details.
External Plugins
In addition to Lua plugins, APISIX supports plugins written in non‑Lua languages via two mechanisms:
External plugin
WASM plugin
External Plugin
APISIX can run non‑Lua plugins using a SideCar process. Currently Java, Go, Python, and JavaScript are supported.
Note: APISIX’s SideCar differs from Istio’s SideCar; it is simply a separate process communicating with APISIX via a Unix socket (the Plugin runner).
Example with Go:
APISIX launches and monitors a child process that runs the language runtime; communication occurs over a Unix‑socket RPC. Developers implement request and response filters in the plugin runner and bind them to APISIX plugins prefixed with ext‑plugin :
ext‑plugin‑pre‑req: runs before Lua plugins.
ext‑plugin‑post‑req: runs after Lua plugins.
ext‑plugin‑post‑resp: runs after the upstream response but before Lua plugins.
This approach essentially starts a separate web server in another language and uses Unix‑socket RPC, avoiding network overhead.
Limitations include additional performance cost from socket communication, the need to bind two plugins for request and response handling, and the fact that a simple sidecar web service could be co‑deployed in the same pod.
WASM Plugin
APISIX also supports WebAssembly plugins, allowing developers to write plugins in C/C++, Go, Rust, etc., compile them to WASM, and run them via the embedded Wasmtime engine.
WASM plugins follow the proxy‑wasm specification and use the corresponding SDK. All WASM plugins share a single WASM VM, each with its own VMContext; different routes receive distinct PluginContexts.
Drawbacks:
WASM lacks coroutines; execution blocks the Nginx event loop, potentially increasing latency.
Native Go toolchains are not supported; tinygo must be used, which lacks some features (e.g., cgo, certain runtime packages).
The provided WASM Nginx module does not fully implement the proxy‑wasm ABI.
FFI
Note: FFI (Foreign Function Interface) is provided by LuaJIT, not APISIX.
Beyond the official methods, developers can use LuaJIT’s FFI to call functions from external C libraries, enabling any language that can produce a shared library to be used within a Lua plugin.
-- Declare usage of the ffi library
local ffi = require "ffi"
-- Load a dynamic library
local myffi = ffi.load('myffi')
-- Declare C functions
ffi.cdef[[
int add(int x, int y);
]]
-- Call the C function
local res = myffi.add(1, 2)Examples with Go and Rust are discussed, highlighting cgo.Handle for safe pointer passing in Go and libraries such as mlua, rlua, and rust‑lua‑ffi for Rust integration. A patched OpenResty approach (lua‑resty‑ffi) adds a queue to avoid blocking when invoking non‑Lua runtimes.
Conclusion
This article introduced APISIX’s process model and request lifecycle, then outlined several methods for developing external plugins—SideCar external plugins, WASM modules, and LuaJIT FFI—each with its own trade‑offs. Selecting the appropriate technique helps bridge the gap between Lua and other languages while maintaining optimal performance.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
