Imagine your web application on React 18 runs in Kubernetes, but every new microservice requires manual CORS, JWT, and rate limiting configuration. Without a centralized gateway, you spend days duplicating configuration and lose traffic control. We went through this on a project with 12 microservices—and chose Apache APISIX. This API Gateway based on OpenResty allowed us to cut the deployment time for new endpoints from 4 hours to 15 minutes and reduce infrastructure load by 30% through efficient rate limiting. APISIX not only routes requests but also provides flexible authentication, rate limiting, and monitoring mechanisms critical for microservice architecture. According to the project's official documentation, it is a dynamic, high-performance API gateway that solves security and observability problems out of the box. Below is practical setup experience, including plugins, monitoring, and canary deployments.
What Problems APISIX Solves
- Dynamic route management without restarts: APISIX stores configuration in etcd and applies changes instantly via watch. No nginx reload—traffic is not interrupted.
- Built-in security: plugins JWT, OIDC, basic auth, IP blacklist—configured via Admin API. We use RS256 for mobile clients.
- Rate limiting with Redis: 100 requests per minute per IP works even under 10k RPS load.
- Canary deployments: via traffic split, you can send 10% of requests to a new service version, analyze metrics, and gradually increase the weight.
How APISIX Solves Dynamic Routing
APISIX uses etcd as a single state store. Each route change via Admin API or Dashboard triggers etcd to automatically propagate the update to all cluster nodes. This allows adding and modifying routes without downtime. Unlike static nginx configuration, APISIX supports flexible routing conditions: by URI, methods, headers, query parameters, and even IP ranges.
Architecture and Installation
Client → APISIX Gateway (nginx/OpenResty) → Upstream Services
↕
ETCD (config store)
↕
APISIX Dashboard
ETCD is mandatory—all configuration (routes, upstreams, plugins) is stored there. Changes apply in real-time via watch.
Docker Compose
version: '3.8'
services:
etcd:
image: bitnami/etcd:3.5
environment:
ALLOW_NONE_AUTHENTICATION: "yes"
ETCD_ADVERTISE_CLIENT_URLS: http://etcd:2379
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
apisix:
image: apache/apisix:3.9.1-debian
volumes:
- ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml
ports:
- "9080:9080" # HTTP
- "9443:9443" # HTTPS
- "9180:9180" # Admin API
- "9091:9091" # Prometheus metrics
depends_on: [etcd]
apisix-dashboard:
image: apache/apisix-dashboard:3.0.1-alpine
volumes:
- ./dashboard_conf/conf.yaml:/usr/local/apisix-dashboard/conf/conf.yaml
ports:
- "9000:9000"
depends_on: [etcd]
Creating a Route via Admin API
BASE_URL="http://localhost:9180/apisix/admin"
KEY="supersecret-admin-key"
# Create upstream
curl -X PUT $BASE_URL/upstreams/1 \
-H "X-API-KEY: $KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "roundrobin",
"nodes": {
"users-service:3000": 1,
"users-service-2:3000": 1
},
"checks": {
"active": {
"http_path": "/health",
"interval": 5,
"healthy": { "successes": 2 },
"unhealthy": { "http_failures": 3 }
}
}
}'
# Create route with JWT and rate limiting
curl -X PUT $BASE_URL/routes/1 \
-H "X-API-KEY: $KEY" \
-H "Content-Type: application/json" \
-d '{
"uri": "/api/v1/users/*",
"methods": ["GET", "POST", "PUT", "DELETE"],
"upstream_id": "1",
"plugins": {
"jwt-auth": {},
"rate-limiting": {
"count": 100,
"time_window": 60,
"key": "remote_addr",
"policy": "redis",
"redis_host": "redis",
"redis_port": 6379
},
"cors": {
"allow_origins": "https://app.company.com",
"allow_methods": "GET,POST,PUT,DELETE",
"allow_headers": "Authorization,Content-Type",
"allow_credential": true
}
}
}'
Plugins for Security and Routing
JWT and OIDC
For authentication we use the jwt-auth plugin with RS256. A consumer is created via Admin API:
curl -X PUT $BASE_URL/consumers/mobile-app \
-H "X-API-KEY: $KEY" \
-d '{
"username": "mobile-app",
"plugins": {
"jwt-auth": {
"key": "mobile-app-key",
"algorithm": "RS256",
"public_key": "-----BEGIN PUBLIC KEY-----\n..."
}
}
}'
For OpenID Connect (OIDC) the setup is slightly more complex: you need to specify the discovery endpoint, client_id, and client_secret. The plugin supports RS256 and automatically passes the access token in a header.
Serverless and Canary
The serverless plugin allows executing custom Lua logic on the rewrite or access phases. For example, add a tenant ID header:
curl -X PUT $BASE_URL/routes/2 \
-H "X-API-KEY: $KEY" \
-d '{
"uri": "/api/v1/transformed",
"upstream_id": "1",
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function(conf, ctx)\n local headers = ngx.req.get_headers()\n ngx.req.set_header(\"X-Tenant-ID\", headers[\"X-User-Tenant\"])\n ngx.req.set_header(\"X-Internal-Key\", \"secret\")\nend"
]
}
}
}'
Canary deployments are implemented via traffic-split: for example, 10% to the new version, and when the x-canary: true header is present, 100% goes to the old version. This allows safe release testing.
How to Configure Canary Deployment in APISIX?
For canary deployment we use the traffic-split plugin. Example configuration: 10% of requests are sent to the new version (upstream tagged canary). When the x-canary: true header is received, traffic goes entirely to the old version. The prometheus plugin collects metrics—when stable, the new version's share is increased. Below is a route configuration fragment:
curl -X PUT $BASE_URL/routes/3 \
-H "X-API-KEY: $KEY" \
-d '{
"uri": "/api/v2/users/*",
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{ "upstream_id": 1, "weight": 90 },
{ "upstream": { "type": "roundrobin", "nodes": { "users-v2:3000": 1 } }, "weight": 10 }
]
}
]
}
}
}'
Monitoring and Comparison with Kong
APISIX Prometheus metrics are exported on port 9091. The official documentation includes a configuration example. For Grafana, use dashboard ID 11719.
| Criterion | APISIX | Kong |
|---|---|---|
| Performance (RPS) | 100k+ per node | 50k+ |
| Configuration | etcd (live) | PostgreSQL/CRI |
| Plugins | 80+ built-in | 200+ (with paid) |
| Kubernetes Ingress | Yes, via Ingress Controller | Yes, via KIC |
| Community | Apache project, active | Open-source + Enterprise |
APISIX outperforms Kong by roughly 2 times on typical loads. Additionally, etcd provides faster dynamic configuration compared to PostgreSQL. For high-load projects with dozens of routes, APISIX is a more efficient choice.
Process Table
| Stage | Duration | Result |
|---|---|---|
| Analysis | 1 day | Route and upstream scheme |
| Design | 1 day | Configuration documentation |
| Implementation | 2-3 days | Working gateway in staging |
| Testing | 1 day | Load test report (k6) |
| Deployment | 1 day | Production with canary deployment |
What's Included and Timelines
- Audit of current infrastructure and selection of routing scheme.
- Deployment of APISIX with ETCD and Dashboard.
- Configuration of plugins: JWT, rate-limit, CORS, OIDC, serverless.
- Integration with Prometheus and Grafana.
- Documentation for routes and plugins.
- Team training: a 2-hour workshop.
- 2 weeks of post-launch support.
Estimated Timelines
Setting up APISIX with Dashboard, basic plugins (JWT, rate-limit, CORS), and health checks—from 2 to 3 days. Full configuration with OIDC, traffic split, and Kubernetes Ingress—from 5 to 7 days.
We guarantee 99.9% SLA on gateway uptime. Our experience: over 50 projects with API Gateway. Get a consultation for an audit of your infrastructure—we will evaluate your project in 1 day free of charge. Contact us—we will help you choose the optimal strategy.







