Why try APISIX?
While using Kong's free version, I ran into limitations like Service & Route having to be bound to Service, no Consumer Group support, and limited functionality. Later I learned about APISIX, an open-source alternative, and decided to test it. After hands-on testing, APISIX performed quite well — it's an Apache Foundation project with an active community, very flexible features supporting Route Only mode, Consumer Group, hot updates, multi-language plugins, and more. A solid API Gateway worth considering.
Setting up Docker-compose and config files
During setup, since the official docs aren't detailed enough and there are some version compatibility issues, it took some time to get the environment running. Related config files are organized and on GitHub for reference:
APISIX vs Kong head-to-head
Let's see what really differs:
| Item | APISIX | Kong (OSS) |
|---|---|---|
| Open-source license | Apache 2.0 | Apache 2.0 |
| Core tech | NGINX + LuaJIT (OpenResty under the hood) | NGINX + LuaJIT (OpenResty under the hood) |
| Config storage | etcd | PostgreSQL / DB-less mode |
| Configuration methods | REST API / etcd / YAML / Dashboard | REST API / YAML / Kong Manager (paid) |
| Plugin system | Hot reload, supports Lua/Go/Java | Lua plugins, also supports hot reload |
| Performance | Excellent, dynamic routing performs well | Stable but more conservative |
| Hot reload | ✅ Plugins load without restart | ✅ Also supports hot reload, but less flexible (kong reload). APISIX edges ahead on automation |
| Admin UI | ✅ Open-source version provides a decent Dashboard | 🔶 Third-party Konga available |
| K8s support | ✅ Native Ingress Controller | ✅ Kong Ingress Controller |
| Multi-language plugins | ✅ Lua / Go / Java / Wasm all work | ❌ Lua only |
| Consumer Group | ✅ Built-in, easy to use | ❌ Not supported in free version |
| Plugin count | 60+, growing | 50+ |
| Rate limiting | Very flexible, supports distributed architecture | Basic features complete, also supports Redis distributed |
| Security plugins | ✅ JWT, key-auth, IP restrictions, WAF — comprehensive | ✅ JWT, ACL, etc. |
| API doc auto-generation | ✅ Built-in | ❌ Need to implement yourself |
| gRPC / WebSocket | ✅ Native, no extra config | 🔶 Need to install plugin |
| Community | Very active, frequent updates from China region | Stable, large global user base |
| Commercial backing | API7.ai | Kong Inc |
| Maintenance complexity | Slightly higher (need to manage etcd) | Simpler (uses PostgreSQL) |
Kong limitations discovered during testing
- Free version doesn't have Route only mode.
- Free version doesn't have Consumer Group.
- Free version's rate limiting is very basic.
- By default doesn't support dynamic routing — even when enabled, no path capture by default (need to write your own plugin).
Kong vs APISIX route configuration comparison
The scenario
Suppose you want to forward /wms/api/item/1 to http://172.62.1.1:1080/api/item/1. Here's how each does it:
Kong's setup (very tedious)
- First create a Service:
POST http://localhost:8001/services
Content-Type: application/json
{
"name": "wms",
"url": "http://localhost:1081/"
}
- Then create a Route:
POST http://localhost:8001/routes
Content-Type: application/json
{
"name": "wms-api-item-1",
"paths": ["~/wms/(?<path>api/item/1)$"],
"strip_path": true,
"path_handling": "v1",
"service": {
"name": "wms"
},
"tags": ["wms"]
}
- Finally, add a plugin to rewrite the path:
POST http://localhost:8001/plugins
Content-Type: application/json
{
"name": "request-transformer",
"service": {
"name": "wms"
},
"tags": ["wms"],
"config": {
"replace": {
"uri": "/api/item/1"
}
}
}
Kong needs 3 API calls, and rewriting paths requires installing an additional plugin — the configuration flow is quite complex.
APISIX's setup (simplified flow)
APISIX supports Route Only mode — one API Call gets it done:
PUT http://127.0.0.1:9180/apisix/admin/routes/api-0001
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"uri": "/wms/api/item/1",
"name": "/wms/api/item/1",
"priority": 10,
"methods": ["GET", "POST", "PUT", "DELETE"],
"plugins": {
"proxy-rewrite": {
"regex_uri": ["^/wms/(.*)", "/$1"]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"172.62.1.1:1080": 1
}
}
}
APISIX needs only 1 API Call. Built-in proxy-rewrite handles path rewriting directly — concise configuration flow.
Configuration complexity comparison
| Item | Kong | APISIX |
|---|---|---|
| Need to create Service first? | ✅ Required | ❌ No, build Route directly |
| Need to create Route | ✅ | ✅ |
| API Call count | 3 (more cumbersome) | 1 (relatively simple) |
| Configuration complexity | More complex | Relatively simple |
Dynamic routing conditions (flexible traffic-shaping mechanism)
One of APISIX's strengths is supporting various conditions for routing decisions — not just URI but also HTTP Method, Header, Query parameters, Host, etc. — for dynamic conditional traffic shaping.
Common dynamic conditions
- HTTP Method: specify GET, POST, PUT, DELETE, etc.
- Header: route based on custom Header content
- Query parameters: decide based on URL parameters
- Host: route based on request Host
- Remote Addr: route based on source IP
Example: routing by Header
PUT http://127.0.0.1:9180/apisix/admin/routes/dynamic-header
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"uri": "/api/v1/resource",
"methods": ["GET"],
"name": "dynamic-header-route",
"priority": 20,
"vars": [
["http_x-user-type", "==", "admin"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"10.0.0.1:8080": 1
}
}
}
With this config, only requests carrying the
X-User-Type: adminheader get routed to the specified upstream.
Example: routing by Query parameter
PUT http://127.0.0.1:9180/apisix/admin/routes/dynamic-query
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"uri": "/api/v1/resource",
"methods": ["GET"],
"name": "dynamic-query-route",
"priority": 10,
"vars": [
["arg_version", "==", "beta"]
],
"upstream": {
"type": "roundrobin",
"nodes": {
"10.0.0.2:8080": 1
}
}
}
This config routes requests with
?version=betato a different upstream.
P.S. APISIX's routing condition design is quite flexible — dynamic traffic shaping based on various request attributes.
APISIX core features
After hands-on use, here are the most useful features:
- Route Only mode: no need to create Service first — configure Route directly. Simplified flow, fewer steps.
- Consumer Group: no UI but operable via API; group management is flexible (though one Consumer can only belong to one Consumer Group).
- Dynamic routing: dynamic shaping based on Header, Query parameters, IP — flexible traffic direction.
- Rate-limit dynamic template Key: supports multi-variable rate limiting (IP+Consumer+Header) — a feature that requires Kong paid version, but APISIX provides in open source.
- Hot-reload plugins: load new plugins or modify config without restart — easier ops, ensures service continuity.
- Multi-language plugins: supports Lua, Go, Java, Wasm.
- Flexible configuration: supports RESTful API, etcd, YAML.
Route Only test
Using the same /wms/api/item/1 redirect example:
PUT http://127.0.0.1:9180/apisix/admin/routes/api-0001
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"uri": "/wms/api/item/1",
"name": "/wms/api/item/1",
"priority": 10,
"methods": ["GET", "POST", "PUT", "DELETE"],
"plugins": {
"proxy-rewrite": {
"regex_uri": ["^/wms/(.*)", "/$1"]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"172.62.1.1:1080": 1
}
}
}
Consumer Group and rate-limit configuration
APISIX's Consumer Group feature is genuinely useful. Despite no UI, API operations are convenient — combined with Plugin Config you can implement group-based rate limiting:
PUT http://localhost:9180/apisix/admin/consumer_groups/free
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"plugins": {}
}
Create a rate-limit Plugin Config:
PUT http://localhost:9180/apisix/admin/plugin_configs/free_ratelimit
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"plugins": {
"key-auth": {},
"limit-count": {
"count": 100,
"time_window": 1,
"rejected_code": 429,
"key": "consumer_name",
"policy": "local",
"group": "daily"
}
}
}
Create a Consumer and apply the group:
PUT http://localhost:9180/apisix/admin/consumers/mark
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"username": "mark",
"plugins": {
"key-auth": {
"key": "mark-api-key"
}
},
"consumer_group": "standard"
}
Note! A consumer can only join one consumer group.
Rate limiting
APISIX excels in rate limiting, supporting "dynamic template key" functionality similar to what Kong only offers in its paid version. You can combine multiple variables in the key field (IP, consumer, header, etc.) for fine-grained group rate limiting.
For example:
PUT /apisix/admin/plugin_configs/pc_free
{
"plugins": {
"limit-count": {
"time_window": 60,
"count": 100,
"rejected_code": 429,
"key_type": "var_combination",
"key": "standard-1-47|$consumer_name",
"policy": "redis",
"redis_host": "192.168.0.15",
"redis_port": 6379,
"redis_timeout": 1000000,
"redis_database": 0
}
}
}
P.S. This had issues in testing — there's likely a bug.
This config dynamically counts per-group based on source IP, consumer name, and the
X-User-Groupheader — limiting data is stored in Redis. This kind of flexible group-based rate limiting requires Kong's enterprise version, but APISIX provides it in open source.
Plugin Config and plugin binding levels
APISIX supports multiple levels of plugin binding for flexible management:
| Level | Binding endpoint | Description |
|---|---|---|
| Global Rule | /apisix/admin/global_rules | Global rules, highest priority |
| Consumer | /apisix/admin/consumers/{username} | Per-user |
| Consumer Group | /apisix/admin/consumer_groups/{groupname} | Group-managed Consumers |
| Route | /apisix/admin/routes/{id} | Per-API-route |
| Service | /apisix/admin/services/{id} | Multiple Routes can share |
Plugin Configs can be reused — Routes/Services directly reference plugin_config_id, dramatically improving operational efficiency!
API grouping and querying
APISIX supports labels — add group tags to Routes, Consumers, Services for easier query and management:
{
"uri": "/wms/api/item/1",
"labels": {
"group": "wms"
},
"name": "get-wms-item-1"
}
To query routes with group "wms":
GET /apisix/admin/routes?label=group==wms
Global plugins and monitoring config
Use Global Rule to apply global plugins like request-id, prometheus, udp-logger:
PUT http://127.0.0.1:9180/apisix/admin/global_rules/1
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"plugins": {
"key-auth": {"_meta": {"disable": false}},
"request-id": {"_meta": {"disable": false}},
"prometheus": {"_meta": {"disable": false}},
"udp-logger": {
"host": "192.168.0.1",
"port": 5000,
"custom_fields": {"client_ip": "$remote_addr"}
}
}
}
Note: APISIX supports multiple Global Rules (for special use cases), but the Dashboard only manages one. Generally, keeping one global_rule (e.g., /global_rules/1) suffices — putting all plugin configs in a single record.
Enable Prometheus monitoring:
PUT http://localhost:9180/apisix/admin/plugins/prometheus
X-API-KEY: <your-api-key>
Content-Type: application/json
{
"enabled": true
}
APISIX limitations (worth knowing)
While APISIX is powerful, there are some limits to be aware of. Whether using KONG or APISIX, both have constraints — for more complex traffic groupings, you may need to write your own plugin or do significant customization:
- One consumer can only join one consumer group
- One route can only apply one plugin config
- Route URIs can't be duplicated
- One Route can apply only one plugin per type
- plugin_config doesn't support group or dynamic-condition switching, but you can use dynamic routing conditions
- One rate-limit setting can only constrain a single time window
- Some plugins have bugs in certain versions
Reflections
After using APISIX for a while, this open-source solution is genuinely solid. Its high flexibility, hot reload, and multi-language plugins make it a quality choice for modern API Gateway. Although some features require API operations and ops complexity is slightly higher, for scenarios needing high flexibility, group-based rate limiting, and dynamic routing, APISIX provides comprehensive solutions. Once you understand these limitations, you can more clearly decide what kind of solution to provide. But for niche API Gateway needs that are too complex, even with paid versions, unless requirements can be adjusted, custom development may still be necessary in the end.




























Comments