Background
If you work with microservices, you can’t avoid an API gateway. Whether you use Spring Cloud Gateway, Envoy, or hand-roll something with Nginx, you need a unified entry point to route requests to backend services.
Kong is one of the most mature open-source solutions in this space — 40K+ stars on GitHub, backed by a commercial company, with a solid plugin ecosystem and wide adoption at scale. But many developers stop at “oh, it’s just a gateway” without digging into its architecture, plugin system, or configuration model. This article does a systematic breakdown across architecture, plugins, configuration, load balancing, and more.
What is Kong
Kong is an open-source API gateway built on Nginx + OpenResty (LuaJIT). Its core capabilities cover route forwarding, authentication, traffic control, and observability. The fundamental problem it solves: extracting cross-cutting concerns like auth, rate limiting, and logging from individual business services and consolidating them at the gateway layer, so business services can focus purely on business logic.
| Scenario | Without gateway | With Kong |
|---|---|---|
| 10 microservices need auth | Each service implements it | Kong handles it uniformly, services focus on business |
| Rate limit an endpoint | Write rate limiting code in the service | Configure a Kong plugin |
| Log all requests | Add logging code to every service | Kong collects them uniformly, zero business impact |
| Canary release | Change code or Nginx config | Kong splits traffic by weight, config takes effect immediately |
The traditional approach means building the same thing in every service. Kong centralizes these shared capabilities at the gateway layer. The more services you have, the bigger the payoff.
Architecture Breakdown
Overall Architecture
Kong Gateway’s architecture has four layers. The top layer is the Nginx reverse proxy, handling all inbound requests. The second layer is the OpenResty/LuaJIT runtime, providing the Lua execution environment. The third layer is Kong Core — the route matching engine and plugin system (auth, rate limiting, logging, transformation plugins all run here). Kong Core also exposes an Admin API for configuration management. The bottom layer is the data store, supporting PostgreSQL, Cassandra, or declarative config files. The data plane’s job is to forward requests to the upstream services (User Service, Order Service, etc.).
Why Nginx + OpenResty
Kong didn’t go with Java or Go — it chose Lua, a relatively niche language. The reason: OpenResty. OpenResty is an enhanced Nginx that embeds LuaJIT into Nginx’s request processing lifecycle, bringing three key capabilities:
- Extremely high performance: Nginx is already known for high concurrency, and LuaJIT runs close to C speed
- Non-blocking I/O: OpenResty wraps all network operations as non-blocking — a request waiting for a database response doesn’t block other requests
- Lifecycle hooks: Every Nginx processing phase (rewrite, access, content, log) can be extended with Lua
Kong is essentially an Nginx config generator plus a plugin execution engine, written in Lua and running on OpenResty.
Core Abstraction Model
Kong’s configuration revolves around five core concepts. A Service abstracts an upstream backend service, including URL, protocol, and timeout settings. A Route defines request matching rules — which requests get routed to which Service, based on path, Host, HTTP Method, etc. One Service can have multiple Routes. An Upstream is a collection of Targets (backend instance addresses) responsible for load balancing strategy and health checks. A Consumer represents an API caller identity, identified via API Key, JWT, etc. A Plugin abstracts cross-cutting logic and can be attached to a Service, Route, or Consumer.
A practical example:
1 | # Define a service |
The design logic is clean: Service abstracts the backend, Route abstracts matching rules, Plugin abstracts cross-cutting logic — decoupled, each doing its own job.
Request Lifecycle
When a request arrives at Kong, it goes through these phases in order:
- SSL/TLS handshake (certificate phase): mTLS plugin can execute here
- URI rewrite (rewrite phase): URL rewrite plugins execute here
- Route matching: Kong finds the matching Route based on Host/Path/Method
- Auth (access phase): key-auth, jwt, oauth2 plugins execute
- Rate limiting (access phase): rate-limiting, quota plugins execute
- Request transformation (access phase): request-transformer plugin executes
- Forward request to upstream service
- Receive upstream response
- Response header handling (header_filter phase): cors, response-transformer plugins execute
- Response body handling (body_filter phase): can modify response content
- Logging (log phase): tcp-log, http-log, file-log plugins execute
- Return response to client
The access phase is where most of the action happens — most plugins execute here. Each plugin has a priority; higher priority plugins run first.
Plugin System
The plugin system is Kong’s biggest differentiator from other gateways. Understanding the plugin system is the key to truly understanding Kong.
Plugin Scope
Kong supports layered plugin configuration at four levels, from broadest to narrowest. Global plugins apply to all requests. Service-level plugins apply to all requests for a specific Service. Route-level plugins apply to requests matching a specific Route. Consumer-level plugins apply to requests from a specific Consumer.
This layered design is extremely flexible in practice. Enable CORS globally, apply stricter rate limiting for the payment service, relax quota limits for VIP consumers — all through different plugin layer combinations.
Plugin Execution Mechanism
Each plugin is essentially a Lua module implementing specific hook functions:
1 | -- A minimal Kong plugin example |
Kong provides a PDK (Plugin Development Kit) that wraps all common operations:
| PDK Function | Purpose |
|---|---|
kong.request.get_header() |
Get request header |
kong.request.get_body() |
Get request body |
kong.response.exit() |
Return response directly |
kong.service.request.set_header() |
Modify headers sent to upstream |
kong.client.get_consumer() |
Get current consumer info |
kong.ip.get_source() |
Get client’s real IP |
Built-in Plugin Categories
Kong’s built-in plugins cover several major categories.
Authentication:
| Plugin | Description |
|---|---|
| key-auth | API Key auth — simplest and most common |
| jwt | JWT Token authentication |
| oauth2 | Full OAuth 2.0 flow |
| basic-auth | HTTP Basic authentication |
| hmac-auth | HMAC signature authentication |
| ldap-auth | LDAP directory authentication |
| openid-connect | OIDC authentication (integrates with Okta, Auth0, etc.) |
| mtls-auth | Mutual TLS authentication |
Traffic control:
| Plugin | Description |
|---|---|
| rate-limiting | Rate limiting by IP/Consumer/Service |
| request-size-limiting | Limit request body size |
| proxy-cache | Response caching |
| canary | Canary release — traffic splitting by weight |
Transformation:
| Plugin | Description |
|---|---|
| request-transformer | Modify request headers, body, URL params |
| response-transformer | Modify response headers, body |
| request-validator | JSON Schema request validation |
| correlation-id | Generate unique request identifier |
| cors | CORS configuration |
Observability:
| Plugin | Description |
|---|---|
| prometheus | Expose Prometheus metrics |
| datadog | Datadog APM integration |
| zipkin | Distributed tracing |
| tcp-log | Send logs via TCP |
| http-log | Send logs via HTTP |
| file-log | Write to local file |
| kafka-log | Send to Kafka |
Building a Custom Plugin
The built-in plugins cover most scenarios, but sometimes you need your own. Say you want to add internal audit logging for all requests passing through the gateway — that requires a custom plugin.
Directory structure:
1 | kong-plugin-audit-log/ |
handler.lua contains the main logic, collecting request info in the log phase and sending it to the audit system:
1 | local AuditLog = { |
schema.lua defines the plugin’s configuration validation rules:
1 | return { |
Install and enable the plugin:
1 | # Package |
Multi-Language Plugin Support
Not everyone knows Lua, so Kong later added External Plugins support for other languages:
| Language | Method | Description |
|---|---|---|
| Go | Go PDK | Communicates with Kong via gRPC |
| Python | Python PDK | Also via gRPC |
| JavaScript | JS PDK | Also via gRPC |
| WebAssembly | WASM | Next-gen extension method |
The principle is the same: Kong calls the external plugin service via gRPC during plugin execution; the external service processes and returns the result. Performance is lower than native Lua plugins, but the development barrier is much lower — teams don’t need to learn Lua to extend Kong.
Configuration Management
Kong supports three configuration modes for different deployment scenarios.
Traditional Database Mode (DB Mode)
All configuration (Service, Route, Plugin, Consumer) is stored in PostgreSQL or Cassandra and managed via Admin API. Changes take effect immediately without a restart. Multiple Kong nodes share the same configuration. This suits dynamic environments where services frequently come and go. The trade-off is dependency on a database — if the database goes down, existing request forwarding continues (Kong has a local cache), but configuration changes become impossible.
DB-less Declarative Mode
All configuration lives in a YAML file, loaded at startup:
1 | # kong.yml |
Start with the config file:
1 | kong start -c kong.conf --declarative-config kong.yml |
No database needed — simpler deployment, config-as-code manageable via Git, naturally suited for GitOps and CI/CD pipelines, and can be placed directly in a Kubernetes ConfigMap. The limitation is that config changes require a reload, and dynamic Consumer registration isn’t supported.
Hybrid Mode
Kong separates the control plane from the data plane. The control plane manages configuration, connects to the database, and exposes the Admin API. The data plane only does proxy forwarding — no database, no Admin API. The control plane pushes config to data planes via mTLS; data planes cache config locally and continue processing requests even if the control plane is temporarily unavailable.
| Component | Responsibility | Characteristics |
|---|---|---|
| Control plane | Config management, database connection | Exposes Admin API, centrally manages all config |
| Data plane | Proxy and forward requests | No database, no Admin API, caches config locally |
This mode is more secure (data plane doesn’t expose management interfaces), lighter to deploy (no database needed), and supports cross-datacenter centralized management. The trade-off is increased architectural complexity and mTLS certificate management.
Load Balancing and Service Discovery
Kong implements load balancing through Upstream objects, supporting multiple algorithms:
| Algorithm | Description | Use case |
|---|---|---|
| round-robin | Round robin | General purpose, most common |
| consistent-hashing | Consistent hashing | Sessions need to stick |
| least-connections | Least connections | Backend instances have uneven performance |
| latency | Lowest latency | Latency-sensitive scenarios |
Consistent hashing deserves special mention — it can hash on different dimensions, giving you a lot of flexibility:
1 | # Hash by Consumer — same consumer always hits the same backend |
Health Checks
Kong provides two health check mechanisms, typically used together. Active checks send probe requests to backend instances at regular intervals, judging health by HTTP status code. Passive checks are based on actual request failures — also called circuit breaking — marking an instance unhealthy when TCP failures, timeouts, or HTTP errors hit a threshold.
1 | # Active check config |
Service Discovery
Kong supports integration with service discovery systems:
| Method | Description |
|---|---|
| DNS SRV | Discover services via DNS records |
| Consul | HashiCorp Consul integration |
| Kubernetes | Use K8s Service names directly |
Kubernetes integration is the most common. In a K8s environment, Kong uses Service names as upstreams and auto-resolves ClusterIPs — no manual backend instance list maintenance:
1 | services: |
Quick Start
One-Command Docker Setup
The simplest approach is Docker Compose for a complete Kong environment:
1 | # docker-compose.yml |
Start:
1 | docker-compose up -d |
Configure Your First API
Use the Admin API to register a service and route, then verify forwarding works:
1 | # 1. Create service |
Add Authentication and Rate Limiting
1 | # 1. Enable key-auth plugin |
DB-less Mode
Skip the database entirely with a declarative config file:
1 | # kong.yml |
1 | # Start in DB-less mode |
Enterprise Scenarios
Multi-Environment Management
Real projects typically have dev, staging, and production environments — separate Kong instances with different ports for isolation:
1 | # Dev Kong |
Canary Releases
Kong’s Canary plugin supports weighted traffic splitting:
1 | # 90% traffic to v1, 10% to v2 |
API Version Management
Use Route priority for API versioning — higher priority routes match first:
1 | # v1 route (lower priority) |
CORS Handling
CORS is common in microservice architectures — handle it uniformly at the gateway layer with the CORS plugin instead of configuring each backend service individually:
1 | curl -X POST http://localhost:8001/services/my-service/plugins \ |
Kong vs Other Solutions
There are several API gateway options. Here’s how the major ones compare:
| Dimension | Kong | Spring Cloud Gateway | Envoy | APISIX |
|---|---|---|---|---|
| Language | Lua | Java | C++ | Lua |
| Performance | Very high | Medium | Very high | Very high |
| Plugin ecosystem | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Dynamic config | ✅ | ✅ | ✅ | ✅ |
| Management UI | Enterprise edition | Build your own | None | Yes |
| Learning curve | Medium | Low (Java ecosystem) | High | Medium |
| K8s integration | Ingress Controller | Weak | Native support | Ingress Controller |
| Best for | General purpose | Spring Cloud stack | Service Mesh | General purpose |
Selection guidance: Java-heavy tech stack → Spring Cloud Gateway. Service Mesh architecture → Envoy. General purpose with strong plugin ecosystem needs → Kong or APISIX. For mid-to-large microservice architectures, Kong is a mature, reliable choice — once service count scales up, the benefits of a unified gateway far outweigh operational costs. Small teams or simple setups might be fine hand-rolling Nginx configs.
Caveats and Limitations
Performance Considerations
Kong’s own latency overhead is typically 1-5ms (excluding plugin processing), but latency climbs noticeably as plugin count increases. Real benchmark data shows: no plugins — ~1ms latency, 50K+ RPS throughput; 3 plugins — ~3ms latency, 30K+ RPS; 5+ plugins — 5-10ms latency, 20K+ RPS. Only enable the plugins you actually need.
Admin API Security
The Admin API is Kong’s management interface, listening on port 8001 by default with full control over configuration. In production, you must restrict access — either internal-only or with authentication:
1 | # Internal access only |
Configuration Backup and Monitoring
In DB mode, back up PostgreSQL regularly. In DB-less mode, make sure kong.yml is version-controlled in Git — losing config is painful. Set up logging and monitoring in production early. The recommended stack: Prometheus plugin for metrics, Grafana for visualization, http-log or kafka-log for request log persistence.