---
title: "APISIX — A More Flexible Open-Source API Gateway: Hands-On Test and Comparison with Kong"
description: "After hitting limitations with Kong's free version, here's a real-world test of APISIX. From setup to comparison — why this open-source solution is genuinely impressive."
canonical_url: "https://blog.markkulab.net/en/post/apisix-openresty-gateway"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/en/author/mark-ku"
site: "Mark Ku's Tech Notes"
date_published: "2025-07-20 06:01:00 +0800"
category: "DevOps"
tags: ["apisix", "kong", "api-gateway", "openresty", "devops", "etcd", "rate-limiting"]
language: "en"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "when reusing or quoting, credit the author and link back to the original"
---

# APISIX — A More Flexible Open-Source API Gateway: Hands-On Test and Comparison with Kong

## 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:

[GitHub repo](https://github.com/markku636/apisix)


## 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
1. Free version doesn't have Route only mode.
2. Free version doesn't have Consumer Group.
3. Free version's rate limiting is very basic.
4. 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)

1. First create a Service:

```bash
POST http://localhost:8001/services
Content-Type: application/json

{
  "name": "wms",
  "url": "http://localhost:1081/"
}
```

2. Then create a Route:

```bash
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"]
}
```

3. Finally, add a plugin to rewrite the path:

```bash
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:

```bash
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

```json
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: admin` header get routed to the specified upstream.

### Example: routing by Query parameter

```json
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=beta` to 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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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-Group` header — 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:

```json
{
  "uri": "/wms/api/item/1",
  "labels": {
    "group": "wms"
  },
  "name": "get-wms-item-1"
}
```

To query routes with group "wms":

```bash
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:

```bash
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:

```bash
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](https://github.com/apache/apisix/issues/12431)

---

## 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.

---

## About this article and its author

Originally published on [Mark Ku's Tech Notes](https://blog.markkulab.net/en/post/apisix-openresty-gateway)

License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — when reusing or quoting, credit the author and link back to the original

### About the author

**[Mark Ku](https://blog.markkulab.net/en/author/mark-ku)** — Software Solution Provider

- 10+ years senior software engineer, now an AI Builder
- Focused on large-platform architecture — North-American e-commerce, AI SaaS subscription billing
- Combining AI Agents and automation to build evolvable product foundations

### Free tools built by the author

All of these are free to use:

- [Free PDF Sign Tool](https://blog.markkulab.net/en/tools/pdf-sign): Online PDF sign tool — draw, type, or upload a signature, then drag, resize, and download. Everything runs in your browser; nothing is uploaded.
- [VS Code Refactory](https://blog.markkulab.net/en/tools/refactory): Refactory is a VS Code refactoring extension: 34 actions plus a 37-rule code-smell inspection layer with a Code Health dashboard, across 18 languages, backed by 534 tests. It learns your repo's conventions: where interfaces live, where DI is registered, whether 'use client' belongs. It ranks files by git churn × complexity so you know what to fix first, and hands any smell to the Claude Code already on your machine. Free to use, and your source never leaves your computer.
- [DB-Kit Database Manager](https://blog.markkulab.net/en/tools/db-kit): DB-Kit is a lightweight, cross-platform database manager built with Tauri + Rust + React. Manage MySQL, MariaDB, PostgreSQL, SQL Server, Oracle, SQLite, MongoDB, Redis, Kafka, Elasticsearch and RabbitMQ from one consistent interface: passwords encrypted in the OS keychain, SSH tunnels, full CRUD, a visual query builder, stacked multi-statement result sets, cross-connection data transfer and compare/sync, Excel / CSV import & export, visualized execution plans, ER diagrams, scheduled backups, SQL stress testing with p50–p99 latency percentiles, a 15-rule SQL review engine, Kafka message browsing with monitoring & alerts, a bilingual UI (Traditional Chinese / English), a built-in AI assistant (natural-language SQL, AI review and tuning advice) and the dbk CLI. Free and open source (MIT), with installers for Windows, macOS and Linux.
- [VS Code Super Mermaid](https://blog.markkulab.net/en/tools/super-mermaid): Super Mermaid is a VS Code extension for beautiful Mermaid diagrams out of the box: auto-colored live preview, mouse pan & zoom, high-res PNG / SVG export, 21 templates and multiple themes. Free and open source (MIT).
- [React Super Mermaid](https://blog.markkulab.net/en/tools/react-super-mermaid): react-super-mermaid is an open-source React component library: render beautiful Mermaid diagrams with a single <MermaidViewer>, with built-in colorful / sketch themes, pan & zoom, in-diagram search, and high-res SVG / PNG export. Lightweight, SSR-safe, fully typed. Free and open source (MIT).
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/en/tools/jira-super-mermaid): An Atlassian Forge app: write Mermaid syntax directly inside a Jira issue or a Confluence page and get flowcharts, sequence diagrams, state machines and Gantt charts. 11 diagram types, SVG / PNG export, light and dark themes, full CJK support. Runs on Atlassian: your diagrams live in your own site and the app calls no third-party service. Free, coming soon to the Atlassian Marketplace.
- [Mermaid Live Preview](https://blog.markkulab.net/en/tools/mermaid-preview): Write Mermaid in your browser, see it render instantly, and share the whole diagram as a single link. No sign-up, nothing uploaded to a server, and mermaid.live share links work as-is.
- [React Intl Phone Number](https://blog.markkulab.net/en/tools/react-intl-phone-number): react-intl-phone-number is an open-source React component: framework-agnostic and antd-free, with E.164 in/out, a searchable flag / country-code dropdown, configurable validation levels (strict / mobile-strict / loose), themeable CSS, and i18n — phone logic powered by google-libphonenumber. Lightweight and fully typed. Free and open source (MIT).
- [Uptime Kuma Cluster](https://blog.markkulab.net/en/tools/uptime-kuma-cluster): Turn single-node Uptime Kuma into a highly available cluster: OpenResty + Lua smart load balancing, shared MariaDB state, health checks and automatic failover, plus cluster-management REST APIs. One Docker Compose command to start. Free and open source (MIT).
- [Special Education](https://blog.markkulab.net/en/education): Learning materials crafted for special education students

### Daily podcasts

- [Mark's Tech Insights — Daily AI News](https://blog.markkulab.net/en/category/tech-news): Daily curated AI and tech trends. Catch the latest developments via audio summaries — covering AI applications, software architecture, DevOps, and engineering practice. — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/en/category/ai-stock-chat): Every trading day, an AI-analyzed take on the Taiwan stock market, delivered as a two-host conversation covering the session and the next-day outlook. — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml
- [開源好物週報](https://blog.markkulab.net/en/category/open-source-weekly): A weekly two-host pick of free open-source tools surfaced from real Hacker News, GitHub, and Reddit buzz — what pain they solve and the fastest way to get started. — RSS: https://blog.markkulab.net/open-source-weekly/feed.xml

### Newsletter

[Subscribe to the newsletter](https://blog.markkulab.net/en/subscribe) — Be the first to know about new posts. No spam, unsubscribe anytime.
