Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Kong Gateway User Guide

Generated June 2026 from Kong/kong at 6e6c302

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Kong Gateway is a cloud-native, platform-agnostic API, LLM, and MCP gateway built on top of OpenResty (NGINX + LuaJIT). It sits in front of your APIs and microservices and gives you a single place to handle routing, load balancing, authentication, rate limiting, transformations, observability, and AI traffic — all configurable at runtime without restarting or redeploying your services.

This guide covers everything you need to run and operate Kong as a user: installing it, proxying your first API, configuring the building blocks (Services, Routes, Consumers, Upstreams), and using its plugins. Architecture, the full kong.conf reference, deployment topologies, and security hardening live in the companion Admin Guide.

Note

This guide documents the open-source Kong Gateway, version 3.10.0. Everything here is derived directly from the source code at the documented commit. Features that require a Kong Enterprise subscription or Kong Konnect (the SaaS control plane) are called out where relevant but are not the focus of this guide.


Table of Contents

  1. What Kong Does
  2. Quick Start (5 minutes)
  3. Installation
  4. Core Concepts
  5. Everyday Usage
  6. Working with Plugins
  7. The Plugin Catalog
  8. Declarative (DB-less) Configuration
  9. The kong CLI Reference
  10. Common Workflows
  11. Advanced Usage
  12. How Kong Works (Concepts in Depth)
  13. Troubleshooting
  14. FAQ
  15. Reference Tables

1. What Kong Does

When a client calls one of your APIs through Kong, Kong becomes the middle layer between the client and your backend service:

                         ┌─────────────────────────────────────┐
   Client ──request──►   │             Kong Gateway            │  ──►  Your Service
                         │                                     │       (upstream)
                         │  1. Match the Route                 │
                         │  2. Run plugins (auth, rate-limit…) │
                         │  3. Load-balance to an upstream     │
   Client ◄─response──   │  4. Run response plugins            │  ◄──  Your Service
                         └─────────────────────────────────────┘

With Kong you can, without touching your service code:

  • Proxy and route HTTP, HTTPS, gRPC, gRPCs, TCP, TLS, and UDP traffic.
  • Load balance across multiple backend instances with health checks.
  • Authenticate consumers using API keys, JWT, OAuth 2.0, Basic Auth, HMAC, LDAP, or sessions.
  • Control traffic with rate limiting, request size limits, request termination, and IP restriction.
  • Transform requests and responses (headers, bodies, query strings).
  • Observe traffic with Prometheus, Datadog, StatsD, OpenTelemetry, Zipkin, and a variety of logging plugins.
  • Govern AI/LLM traffic through a universal LLM API across OpenAI, Anthropic, Azure, Bedrock, Gemini, Mistral, Cohere, Hugging Face, and more.
  • Extend the gateway with custom plugins in Lua, Go, JavaScript, Python, or WebAssembly.

Kong is the data plane that enforces policy; you describe that policy as data (entities like Services, Routes, and Plugins), and Kong applies it to live traffic in real time.

Tip

Kong runs natively on Kubernetes via the official Kubernetes Ingress Controller. If you run on Kubernetes, you typically configure Kong through Kubernetes resources rather than calling the Admin API directly.


2. Quick Start (5 minutes)

The fastest way to try Kong is the official docker-kong Compose stack, which brings up Kong plus a PostgreSQL database. You will add authentication to a sample API in a few commands.

Step 1 — Start the Gateway

Docker Compose (with database)

git clone https://github.com/Kong/docker-kong
cd docker-kong/compose/
KONG_DATABASE=postgres docker-compose --profile database up

Docker (DB-less)

docker run -d --name kong \
  -e "KONG_DATABASE=off" \
  -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
  -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
  -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
  -p 8000:8000 -p 8001:8001 \
  kong/kong:3.10

Once Kong is up, these ports are available on localhost:

Port Purpose
:8000 Proxy — send your API traffic here
:8443 Proxy over HTTPS
:8001 Admin API — configure Kong (HTTP)
:8444 Admin API over HTTPS
:8002 Kong Manager web UI (Enterprise/Konnect features vary)

Verify Kong is responding:

curl -i http://localhost:8001/
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "version": "3.10.0", "tagline": "Welcome to kong", ... }

Step 2 — Add a Service

A Service points at your upstream API. Here we use the public httpbin test service:

curl -i -X POST http://localhost:8001/services \
  --data name=example-service \
  --data url='https://httpbin.org'
HTTP/1.1 201 Created
...
{ "id": "…", "name": "example-service", "host": "httpbin.org", "port": 443, "protocol": "https", ... }

Step 3 — Add a Route

A Route decides which incoming requests reach that Service:

curl -i -X POST http://localhost:8001/services/example-service/routes \
  --data 'paths[]=/example' \
  --data name=example-route

Step 4 — Send traffic through the proxy

curl -i http://localhost:8000/example/anything
HTTP/1.1 200 OK
...
(httpbin echoes your request — you just proxied through Kong!)

Step 5 — Protect it with an API key

Enable the key-auth plugin on the Route, create a Consumer, and give them a key:

# Enable key authentication on the route
curl -i -X POST http://localhost:8001/routes/example-route/plugins \
  --data name=key-auth

# Create a consumer
curl -i -X POST http://localhost:8001/consumers \
  --data username=alice

# Issue an API key to that consumer
curl -i -X POST http://localhost:8001/consumers/alice/key-auth \
  --data key=secret-api-key-123

Now the route is locked down:

curl -i http://localhost:8000/example/anything          # 401 Unauthorized
curl -i http://localhost:8000/example/anything \
  -H 'apikey: secret-api-key-123'                        # 200 OK
HTTP/1.1 401 Unauthorized
{ "message": "No API key found in request" }

You just added authentication to an API without changing the API. That is the core Kong workflow: create entities, attach plugins, send traffic.

Tip

Prefer a single config file over many curl calls? Run Kong in DB-less mode and describe everything in one YAML file. See Declarative (DB-less) Configuration.


3. Installation

Kong supports Linux and macOS. Choose the distribution that fits how you run software.

3.1 Installation methods

Docker

docker pull kong/kong:3.10

Run it pointing at PostgreSQL (traditional mode) or with KONG_DATABASE=off for DB-less. Official images and tags are on Docker Hub.

Kubernetes

Use the official Helm chart and/or the Kubernetes Ingress Controller:

helm repo add kong https://charts.konghq.com
helm repo update
helm install kong/kong --generate-name

Package (deb/rpm)

Download the .deb or .rpm for your distribution from the official install page and install with your package manager, e.g.:

sudo apt install ./kong-3.10.0.amd64.deb     # Debian/Ubuntu
sudo yum install kong-3.10.0.el8.amd64.rpm   # RHEL/CentOS

From source (LuaRocks)

Kong is published as a LuaRocks rock and depends on a specific OpenResty build. Building from source is for contributors and advanced users; see DEVELOPER.md in the repository. Most users should use a packaged distribution.

3.2 Requirements

Requirement Detail
Operating system Linux or macOS
Runtime OpenResty (bundled in the official packages); requires NGINX 1.27.1.2
Database (traditional mode) PostgreSQL 9.5 or newer
Database (DB-less mode) None — configuration is held in memory (LMDB)
Memory A few hundred MB minimum; scales with cache sizes and config size

Important

You only need PostgreSQL if you run in traditional or hybrid control-plane mode. In DB-less mode Kong needs no database at all — it loads a declarative config file into an in-memory LMDB store.

3.3 First start (traditional mode)

If you run Kong against PostgreSQL, bootstrap the database schema once, then start:

# 1. Bootstrap the database (run once, creates Kong's schema)
kong migrations bootstrap -c /etc/kong/kong.conf

# 2. Start Kong
kong start -c /etc/kong/kong.conf
Kong started

Confirm it is healthy:

kong health
nginx.......running

Kong is healthy at /usr/local/kong

3.4 The configuration file

Kong reads a single configuration file (default /etc/kong/kong.conf) when you run kong start or kong prepare. A fully commented template ships as kong.conf.default; copy it to get started:

cp /etc/kong/kong.conf.default /etc/kong/kong.conf

Every setting can also be supplied as an environment variable by uppercasing it and prefixing KONG_. For example:

export KONG_LOG_LEVEL=notice          # same as `log_level = notice`
export KONG_DATABASE=off              # same as `database = off`
export KONG_PROXY_LISTEN="0.0.0.0:8000"

Note

The full list of every configuration property, its default, and its meaning is in the Admin Guide → Configuration Reference. As a user you usually only set a handful (database connection, listen addresses, log level).

Validate a config file at any time without starting Kong:

kong check /etc/kong/kong.conf
configuration at /etc/kong/kong.conf is valid

4. Core Concepts

Everything you configure in Kong is an entity — a piece of data stored in the database (traditional mode) or loaded from a YAML file (DB-less mode). You create, read, update, and delete entities through the Admin API (a RESTful API on port 8001), through the decK CLI, or through a declarative config file.

These are the entities you will use most often.

4.1 The request-handling entities

   Consumer  ─────┐
                  │ (credentials, plugin scoping)
                  ▼
   Client ─► Route ─► Service ─► Upstream ─► Target(s) ─► your backend
                  │        │          (load balancing across targets)
                  └─ Plugins run along this path ─┘
Entity What it represents
Service An abstraction of one of your upstream APIs/microservices (where to send traffic).
Route Rules for matching client requests (paths, hosts, methods, headers, SNIs…) and mapping them to a Service.
Consumer A client/application that uses your APIs. Credentials and per-consumer policy attach here.
Plugin A unit of functionality (auth, rate limiting, logging…) that runs on requests, scoped globally or to a Service/Route/Consumer.
Upstream A virtual hostname representing a pool of backend targets, with load balancing and health checks.
Target A single backend host:port (with a weight) that belongs to an Upstream.
Certificate / SNI TLS certificates and the SNI hostnames they serve.
Consumer Group A way to group consumers for shared policy (used by some plugins).
Vault A reference to a secrets backend so sensitive values aren't stored in plain config.
Key / Key Set Cryptographic keys (e.g. for JWT verification) managed as entities.

4.2 Services

A Service tells Kong where to send traffic. You can define it field-by-field or with the convenient url shorthand.

# Using the url shorthand (sets protocol, host, port, path at once)
curl -X POST http://localhost:8001/services \
  --data name=billing \
  --data url='https://billing.internal:8443/api'

# Equivalent, field by field
curl -X POST http://localhost:8001/services \
  --data name=billing \
  --data protocol=https \
  --data host=billing.internal \
  --data port=8443 \
  --data path=/api

Key Service fields (with defaults):

Field Default Description
name — Human-friendly unique name.
protocol http One of http, https, grpc, grpcs, tcp, tls, udp.
host — Upstream host or the name of an Upstream entity.
port 80 Upstream port.
path — Path prefix prepended to the upstream request.
retries 5 Retries on proxy failure (0–32767).
connect_timeout 60000 Connection timeout (ms).
write_timeout 60000 Write timeout (ms).
read_timeout 60000 Read timeout (ms).
tls_verify — Verify the upstream's TLS certificate.
client_certificate — Client cert for mTLS to the upstream.
enabled true Whether the Service is active.

Note

Set host to the name of an Upstream entity to get load balancing and health checks instead of pointing at a single host. See Load balancing.

4.3 Routes

A Route decides which requests reach a Service. A Route can match on any combination of paths, hosts, methods, headers, SNIs, and (for stream traffic) sources/destinations.

curl -X POST http://localhost:8001/services/billing/routes \
  --data name=billing-route \
  --data 'paths[]=/billing' \
  --data 'methods[]=GET' \
  --data 'methods[]=POST' \
  --data 'hosts[]=api.example.com'

Key Route fields (with defaults):

Field Default Description
protocols ["http","https"] Protocols this Route accepts.
paths — Path patterns to match (plain prefixes or regexes).
hosts — Host header values to match.
methods — HTTP methods to match.
headers — Header values to match (cannot include host).
snis — SNIs to match (TLS/HTTPS/gRPCs/tls_passthrough only).
strip_path true Strip the matched path prefix before proxying upstream.
preserve_host false Forward the client's Host header upstream.
https_redirect_status_code 426 Status returned when only the protocol fails to match.
request_buffering true Buffer the request body.
response_buffering true Buffer the response body.
regex_priority 0 Tie-breaker when multiple regex routes match.
path_handling v0 How service path + route path + request path combine.

Tip

strip_path is one of the most common sources of confusion. With strip_path=true (the default) and a route path of /billing, a request to /billing/invoices is sent upstream as /invoices. Set it to false to forward the full path.

4.4 Consumers and credentials

A Consumer is anyone (a user, app, or service) calling your APIs. Authentication plugins attach credentials to consumers.

# Create a consumer
curl -X POST http://localhost:8001/consumers --data username=mobile-app

# Give them a key-auth credential
curl -X POST http://localhost:8001/consumers/mobile-app/key-auth --data key=abc123

# Give them basic-auth credentials
curl -X POST http://localhost:8001/consumers/mobile-app/basic-auth \
  --data username=mobile-app --data password=s3cr3t

Once authenticated, Kong adds identity headers to the upstream request (e.g. X-Consumer-ID, X-Consumer-Username), so your backend knows who is calling.

4.5 Tags

Most entities accept a tags array. Tags let you filter and organize entities, which is invaluable in large deployments:

curl -X POST http://localhost:8001/services \
  --data name=payments --data url=http://payments:8080 \
  --data 'tags[]=team-fintech' --data 'tags[]=pci'

# List everything tagged team-fintech
curl http://localhost:8001/services?tags=team-fintech

5. Everyday Usage

This section walks through the tasks you will perform most often.

5.1 Proxying an API

The minimum to proxy an API is a Service plus a Route:

curl -X POST http://localhost:8001/services \
  --data name=orders --data url=http://orders.internal:8080
curl -X POST http://localhost:8001/services/orders/routes \
  --data name=orders --data 'paths[]=/orders'

Traffic to http://localhost:8000/orders/... now reaches http://orders.internal:8080/....

5.2 Routing rules

Routes are matched in priority order based on how specific they are. You can combine matchers:

# Host + path
curl -X POST http://localhost:8001/services/orders/routes \
  --data 'hosts[]=shop.example.com' --data 'paths[]=/v2/orders'

# Regex path (prefix with ~ to force regex evaluation)
curl -X POST http://localhost:8001/services/orders/routes \
  --data 'paths[]=~/orders/\d+'

# Header match
curl -X POST http://localhost:8001/services/orders/routes \
  --data 'headers.x-api-version=2'

Note

Kong 3.x defaults to the traditional_compatible router, which uses a fast expression engine under the hood while keeping the classic paths/hosts/methods interface. You can opt into writing raw router expressions by setting router_flavor = expressions in kong.conf (see the Admin Guide).

5.3 Adding authentication

Attach an auth plugin to a Route (or Service, or globally), then create Consumers with credentials. Available auth plugins: key-auth, basic-auth, jwt, oauth2, hmac-auth, ldap-auth, session.

# JWT authentication on a service
curl -X POST http://localhost:8001/services/orders/plugins --data name=jwt

# Create a consumer and a JWT credential
curl -X POST http://localhost:8001/consumers --data username=partner
curl -X POST http://localhost:8001/consumers/partner/jwt \
  --data algorithm=HS256 --data secret=my-signing-secret

A common pattern is multiple auth methods with an anonymous fallback: set the anonymous config field on each auth plugin to a fallback consumer, then use the acl plugin to authorize.

5.4 Load balancing with Upstreams and Targets

To balance across several backend instances, create an Upstream and add Targets, then point a Service's host at the Upstream name:

# 1. Create the upstream (a virtual hostname)
curl -X POST http://localhost:8001/upstreams --data name=orders-pool

# 2. Add backend targets with weights
curl -X POST http://localhost:8001/upstreams/orders-pool/targets \
  --data target=10.0.0.11:8080 --data weight=100
curl -X POST http://localhost:8001/upstreams/orders-pool/targets \
  --data target=10.0.0.12:8080 --data weight=100

# 3. Point a service at the upstream by name
curl -X POST http://localhost:8001/services \
  --data name=orders --data host=orders-pool --data port=8080

Upstreams support several load-balancing algorithms (round-robin, consistent hashing, least-connections, latency) and active/passive health checks that automatically remove unhealthy targets from rotation. Health-check configuration lives on the Upstream entity; see the Admin Guide for the full field reference.

5.5 Rate limiting

# Allow 100 requests per minute, counted per consumer
curl -X POST http://localhost:8001/services/orders/plugins \
  --data name=rate-limiting \
  --data config.minute=100 \
  --data config.policy=local

The rate-limiting plugin counts against one of: consumer, credential, ip, service, header, or path (the limit_by field). For multi-node deployments use config.policy=redis (shared) or config.policy=cluster; local counts per node only.

5.6 Transforming requests and responses

# Add a header to every upstream request
curl -X POST http://localhost:8001/services/orders/plugins \
  --data name=request-transformer \
  --data 'config.add.headers=x-source:kong'

# Remove a header from responses
curl -X POST http://localhost:8001/services/orders/plugins \
  --data name=response-transformer \
  --data 'config.remove.headers=server'

5.7 Inspecting what you've configured

curl http://localhost:8001/services            # list all services
curl http://localhost:8001/routes              # list all routes
curl http://localhost:8001/consumers           # list all consumers
curl http://localhost:8001/plugins             # list all enabled plugins
curl http://localhost:8001/orders/anything     # NOT this — that's the proxy port (8000)

Caution

The Admin API (:8001) and the proxy (:8000) are different ports. The Admin API can create, modify, and delete everything in your gateway — never expose it to the public internet. See the Admin Guide → Security for how to lock it down.


6. Working with Plugins

Plugins are how Kong does almost everything beyond plain proxying. A plugin can be attached at four scopes, from broadest to narrowest:

Scope How to attach Applies to
Global POST /plugins Every request through the node.
Service POST /services/{id}/plugins All routes of that Service.
Route POST /routes/{id}/plugins One Route.
Consumer POST /consumers/{id}/plugins One Consumer (where supported).

When multiple instances of the same plugin could apply, the most specific scope wins (Route+Consumer > Route > Service > Global).

6.1 Enabling and configuring a plugin

Every plugin has a name and a config object whose fields are defined by that plugin's schema. Configure fields with the config.<field> convention:

curl -X POST http://localhost:8001/routes/orders/plugins \
  --data name=rate-limiting \
  --data config.minute=60 \
  --data config.limit_by=ip \
  --data config.policy=local

6.2 Listing, updating, and deleting plugins

# Find the plugin instance id
curl http://localhost:8001/routes/orders/plugins

# Update its config
curl -X PATCH http://localhost:8001/plugins/{plugin-id} \
  --data config.minute=120

# Disable temporarily without deleting
curl -X PATCH http://localhost:8001/plugins/{plugin-id} --data enabled=false

# Delete it entirely
curl -X DELETE http://localhost:8001/plugins/{plugin-id}

6.3 Which plugins are loaded

By default Kong loads the bundled set (the 44 plugins shipped with the gateway). You control which plugins a node will load with the plugins setting in kong.conf:

# Load only the bundled plugins plus a custom one
export KONG_PLUGINS="bundled,my-custom-plugin"

# Load nothing but two specific plugins (faster startup, less memory)
export KONG_PLUGINS="key-auth,rate-limiting"

# Disable all plugins
export KONG_PLUGINS=off

Important

Loading a plugin only makes it available — it does not enable it on any traffic. You still attach instances via the Admin API. Also: Kong will refuse to start if a plugin that has configured instances in the database is removed from the plugins list. Remove all instances before dropping a plugin.


7. The Plugin Catalog

Kong 3.10.0 bundles 44 plugins. They are grouped below by purpose. To use any of them, attach an instance with POST .../plugins --data name=<plugin> and set its config.* fields.

7.1 Authentication

Plugin Purpose
key-auth API key authentication (key in header, query, or body).
basic-auth HTTP Basic authentication (username/password).
jwt Verify JSON Web Tokens signed by consumer credentials.
oauth2 OAuth 2.0 flows (authorization code, client credentials, password, implicit).
hmac-auth HMAC signature authentication over request components.
ldap-auth Authenticate against an LDAP directory.
session Maintain authenticated sessions with a session cookie.

7.2 Security & access control

Plugin Purpose
acl Allow/deny consumers by group membership.
cors Cross-Origin Resource Sharing headers and preflight handling.
ip-restriction Allow/deny by client IP or CIDR range.
bot-detection Block requests from known bots by User-Agent.
request-size-limiting Reject requests whose body exceeds a size limit.
request-termination Short-circuit requests with a fixed response (maintenance mode, blocking).
standard-webhooks Verify inbound webhooks per the Standard Webhooks spec.

7.3 Traffic control

Plugin Purpose
rate-limiting Limit request rate per second/minute/hour/day/month/year.
response-ratelimiting Rate limit based on custom counters returned by the upstream.
proxy-cache Cache upstream responses in memory to reduce backend load.
redirect Issue HTTP redirects for matched requests.

7.4 Transformations

Plugin Purpose
request-transformer Add/remove/replace/rename request headers, query args, and body fields.
response-transformer Modify response headers and body.
correlation-id Inject a unique correlation/request ID header.
grpc-gateway Transcode JSON/REST requests to gRPC upstreams.
grpc-web Allow gRPC-Web browser clients to reach gRPC services.

7.5 Logging

Plugin Purpose
file-log Append request/response logs to a file.
http-log POST logs to an HTTP(S) endpoint.
tcp-log Stream logs over TCP.
udp-log Stream logs over UDP.
syslog Send logs to syslog.
loggly Send logs to Loggly.

7.6 Analytics & observability

Plugin Purpose
prometheus Expose metrics in Prometheus format (with an optional Grafana dashboard).
datadog Send metrics to a Datadog agent (StatsD).
statsd Send metrics to a StatsD server.
opentelemetry Export traces (and more) via OTLP to an OpenTelemetry collector.
zipkin Distributed tracing with Zipkin/B3 propagation.

7.7 Serverless & cloud functions

Plugin Purpose
aws-lambda Invoke an AWS Lambda function as the upstream.
azure-functions Invoke an Azure Function as the upstream.
pre-function Run custom Lua before other plugins (sandboxed).
post-function Run custom Lua after other plugins (sandboxed).

7.8 AI Gateway

Kong's AI Gateway plugins turn Kong into a universal, governed front door for LLM and MCP traffic.

Plugin Purpose
ai-proxy Universal LLM API — route to OpenAI, Anthropic, Azure, Bedrock, Gemini, Mistral, Cohere, Hugging Face, Ollama, and more behind one interface.
ai-prompt-decorator Inject system/assistant prompts (prepend/append messages).
ai-prompt-template Enforce reusable, parameterized prompt templates.
ai-prompt-guard Allow/deny prompts by pattern (prompt firewall).
ai-request-transformer Use an LLM to transform the incoming request.
ai-response-transformer Use an LLM to transform the upstream response.

7.9 TLS automation

Plugin Purpose
acme Automatic Let's Encrypt (ACME) certificate issuance and renewal.

Tip

Every plugin's exact configuration fields, defaults, and validation rules are defined by its schema.lua. The Admin Guide lists each plugin's config reference; you can also introspect any plugin's schema live at GET /schemas/plugins/{plugin-name}.


8. Declarative (DB-less) Configuration

In DB-less mode Kong needs no database. You describe your entire configuration in a single YAML (or JSON) file and load it into Kong's in-memory store. This is ideal for GitOps, immutable deployments, and Kubernetes.

8.1 Enable DB-less mode

Set the database to off and point at a declarative config file:

export KONG_DATABASE=off
export KONG_DECLARATIVE_CONFIG=/etc/kong/kong.yml
kong start

8.2 Generate a starter file

kong config init kong.yml

This writes an example kong.yml you can edit. A minimal declarative config looks like:

_format_version: "3.0"

services:
  - name: orders
    url: http://orders.internal:8080
    routes:
      - name: orders-route
        paths:
          - /orders
    plugins:
      - name: rate-limiting
        config:
          minute: 100
          policy: local

consumers:
  - username: mobile-app
    keyauth_credentials:
      - key: abc123

plugins:
  - name: key-auth
    route: orders-route

8.3 Validate and load

# Check syntax without loading
kong config parse kong.yml

# Load it into a running DB-less node via the Admin API
curl -X POST http://localhost:8001/config \
  -F config=@kong.yml

Note

In DB-less mode the Admin API is read-only for entities — you cannot POST /services to create entities one at a time. The only write endpoint is POST /config, which replaces the whole configuration atomically.

8.4 Converting between modes

If you started in traditional (database) mode and want a declarative snapshot:

kong config db_export kong.yml      # database  -> file
kong config db_import kong.yml      # file -> database (traditional mode only)

Tip

The decK CLI is the recommended tool for managing declarative configuration in either mode: it diffs your file against the running config and applies only the changes, supports validation, and integrates with CI.


9. The kong CLI Reference

The kong command manages the gateway node (starting, stopping, migrations, config). It is the tool you run on the host where Kong is installed. Add --v for verbose output or --vv for debug output to any command.

9.1 Command summary

Command Purpose
kong start Start Kong (and NGINX) in the configured prefix.
kong stop Stop a running node (SIGTERM).
kong quit Gracefully stop — finish in-flight requests first (SIGQUIT).
kong reload Reload config without dropping connections (SIGHUP).
kong restart stop then start.
kong health Check whether the node's services are running.
kong check <conf> Validate a configuration file.
kong prepare Prepare the prefix so you can start NGINX directly.
kong migrations Manage database schema migrations.
kong config Work with declarative config files.
kong vault Vault/secret utilities.
kong hybrid Hybrid-mode utilities (generate certificates).
kong drain Make /status/ready return 503 (for graceful removal from a load balancer).
kong version Print the Kong version (-a for all dependencies).
kong roar Print the Kong mascot. 🦍

9.2 kong start

Usage: kong start [OPTIONS]

Options:
 -c,--conf            Configuration file.
 -p,--prefix          Override prefix directory.
 --nginx-conf         Custom Nginx configuration template.
 --run-migrations     Run migrations before starting.
 --db-timeout         Timeout (seconds) for all database operations.
 --lock-timeout       Timeout (seconds) waiting on the leader node's migrations.
 --nginx-conf-flags   Flags controlling how Nginx templates are rendered.
kong start -c /etc/kong/kong.conf --run-migrations

9.3 kong migrations

Usage: kong migrations COMMAND [OPTIONS]

Commands:
  bootstrap   Bootstrap the database and run all migrations.
  up          Run any new migrations.
  finish      Finish pending migrations after 'up'.
  list        List executed migrations.
  reset       Reset the database (ERASES ALL DATA).
  status      Dump migration status as JSON.

Options:
 -y,--yes            Assume "yes" to prompts (non-interactive).
 -q,--quiet          Suppress all output.
 -f,--force          Run migrations even if reported as already executed.
 --db-timeout        Timeout (seconds) for database operations.
 --lock-timeout      (default 60) Timeout waiting on the leader node.
 -c,--conf           Configuration file.
 -p,--prefix         Override prefix directory.

The zero-downtime upgrade flow is migrations up (apply new, backward-compatible migrations while the old version runs) → deploy new nodes → migrations finish (apply the rest):

kong migrations up   -c /etc/kong/kong.conf
kong migrations finish -c /etc/kong/kong.conf

Caution

kong migrations reset erases all data in Kong's database and drops its schemas. It prompts for confirmation; with --yes it runs without prompting. Never run it against a production database you want to keep.

9.4 kong config

Usage: kong config COMMAND [OPTIONS]

Commands:
  init [<file>]        Generate an example config file (default ./kong.yml).
  db_import <file>     Import a declarative config file into the database.
  db_export [<file>]   Export the database into a declarative config file.
  parse <file>         Parse/validate a declarative file without loading it.

Options:
 -c,--conf            Configuration file.
 -p,--prefix          Override prefix directory.

9.5 kong quit (graceful shutdown)

Usage: kong quit [OPTIONS]

Options:
 -p,--prefix      Prefix Kong is running at.
 -t,--timeout     (default 10) Timeout before forced shutdown.
 -w,--wait        (default 0) Wait time before initiating the shutdown.

kong quit sends SIGQUIT so all in-flight requests finish before the node stops. If the timeout is reached, the node is forcefully stopped (SIGTERM). Combine kong drain (stop accepting new readiness traffic) with kong quit for zero-downtime rollouts.

9.6 kong vault get

Resolve a secret reference from a configured vault. The open-source distribution ships the environment-variable vault (env):

TEST=hello kong vault get env/test
hello

9.7 kong hybrid gen_cert

Generate the mTLS certificate/key pair used for control-plane ↔ data-plane communication in hybrid mode:

kong hybrid gen_cert            # writes ./cluster.crt and ./cluster.key
kong hybrid gen_cert -d 365     # valid for 365 days (default 1095 / 3 years)

9.8 kong version

kong version -a
Kong: 3.10.0
ngx_lua: ...
nginx: ...
Lua: LuaJIT ...

10. Common Workflows

10.1 Add authentication + rate limiting to an existing API

# Assume service `orders` and route `orders-route` already exist
curl -X POST http://localhost:8001/routes/orders-route/plugins --data name=key-auth
curl -X POST http://localhost:8001/routes/orders-route/plugins \
  --data name=rate-limiting --data config.minute=100 --data config.policy=local

curl -X POST http://localhost:8001/consumers --data username=client-a
curl -X POST http://localhost:8001/consumers/client-a/key-auth --data key=KEY-A

10.2 Canary / weighted rollout with Upstreams

Shift traffic gradually by adjusting target weights:

curl -X POST http://localhost:8001/upstreams/orders-pool/targets \
  --data target=10.0.0.20:8080 --data weight=10     # new version: 10%
# old version stays at weight=90 → ~10% canary

10.3 Maintenance mode

curl -X POST http://localhost:8001/routes/orders-route/plugins \
  --data name=request-termination \
  --data config.status_code=503 \
  --data 'config.message=Service under maintenance'

10.4 Automatic HTTPS with Let's Encrypt

curl -X POST http://localhost:8001/plugins \
  --data name=acme \
  --data 'config.account_email=[email removed]' \
  --data 'config.domains[]=api.example.com' \
  --data config.tos_accepted=true

10.5 Zero-downtime config change

# Edit /etc/kong/kong.conf, then:
kong reload -c /etc/kong/kong.conf

reload sends SIGHUP: NGINX spawns new workers with the new config and retires old workers after their requests complete — no dropped connections.

10.6 Route an LLM through the AI Gateway

curl -X POST http://localhost:8001/services \
  --data name=ai --data url='http://localhost:32000'
curl -X POST http://localhost:8001/services/ai/routes \
  --data name=openai --data 'paths[]=/chat'
curl -X POST http://localhost:8001/routes/openai/plugins \
  --data name=ai-proxy \
  --data 'config.route_type=llm/v1/chat' \
  --data 'config.model.provider=openai' \
  --data 'config.model.name=gpt-4o' \
  --data 'config.auth.header_name=Authorization' \
  --data 'config.auth.header_value=Bearer $OPENAI_KEY'

Clients now POST OpenAI-style chat requests to http://localhost:8000/chat and Kong handles provider routing, auth, and (with the other AI plugins) prompt governance.


11. Advanced Usage

11.1 Custom Lua with serverless functions

The pre-function and post-function plugins let you run arbitrary Lua at specific request phases. Code runs in a sandbox by default (untrusted_lua = sandbox in kong.conf):

curl -X POST http://localhost:8001/routes/orders-route/plugins \
  --data name=pre-function \
  --data 'config.access[1]=kong.log.notice("hello from Lua")'

Warning

The sandbox restricts access to the OS and global environment, but LuaJIT is not a hardened secure runtime. Treat the Admin API as privileged and never expose it publicly even with sandboxing on. Set untrusted_lua = off to disable custom Lua entirely.

11.2 Secrets management with Vaults

Instead of putting secrets directly in plugin config, reference them from a vault using {vault://...} syntax. The open-source distribution includes the environment-variable vault:

# Store the secret in an env var, reference it in config
export MY_REDIS_PASSWORD=supersecret
curl -X POST http://localhost:8001/services/orders/plugins \
  --data name=rate-limiting \
  --data config.policy=redis \
  --data 'config.redis.password={vault://env/my-redis-password}'

11.3 The Status API

A read-only API (default 127.0.0.1:8007) for monitoring tools. It never exposes sensitive data and is safe to wire into load-balancer health checks:

curl http://localhost:8007/status            # node metrics + db reachability
curl http://localhost:8007/status/ready      # 200 when ready to serve, 503 otherwise
curl http://localhost:8007/status/dns        # new DNS client stats (if enabled)

kong drain flips /status/ready to 503 so a load balancer stops sending new traffic before you kong quit.

11.4 Custom plugins

You can write your own plugins in:

  • Lua — native; a plugin is a directory under kong/plugins/<name>/ with handler.lua and schema.lua. Make it loadable with KONG_PLUGINS=bundled,<name> and KONG_LUA_PACKAGE_PATH.
  • Go, JavaScript, Python — via external "pluginservers" (process-based plugins using the corresponding PDK: go-pdk, kong-pdk for JS/Python).
  • WebAssembly — enable wasm = on and drop .wasm filter modules into wasm_filters_path.

Plugin authoring (the PDK, phases, and the "add a plugin in N steps" recipe) is covered in the Admin Guide.


12. How Kong Works (Concepts in Depth)

You don't need this section to use Kong, but it helps explain why things behave the way they do.

12.1 The request lifecycle

Kong processes each proxied request through NGINX/OpenResty phases. Plugins hook into these phases:

rewrite ─► access ─► (balancer) ─► header_filter ─► body_filter ─► log
   │          │            │             │               │           │
   │          │            │             │               │           └─ logging plugins
   │          │            │             │               └─ response body transforms
   │          │            │             └─ response header transforms
   │          │            └─ pick an upstream target, retry on failure
   │          └─ auth, rate limiting, request transforms run here (most plugins)
   └─ earliest hook; runs before routing

12.2 Deployment topologies

Kong supports three ways to run, set by role and database in kong.conf:

Topology database role Description
Traditional postgres traditional Each node talks to a shared PostgreSQL; config via Admin API.
DB-less off traditional No database; config from a declarative file loaded into memory.
Hybrid postgres (CP) / off (DP) control_plane / data_plane A control plane holds config in a database and pushes it over mTLS to stateless data planes that proxy traffic.

Hybrid mode is the recommended production topology at scale: data planes keep serving traffic even if the control plane or database is temporarily down. The deep dive lives in the Admin Guide.

12.3 Caching and propagation

Kong caches entities in shared memory to avoid hitting the database on every request. When you change config through the Admin API, other nodes pick it up on their next poll (db_update_frequency, default 5s). This is why a change made on one node may take a few seconds to take effect cluster-wide. DB-less and hybrid data planes instead receive a full config push.

12.4 The router

Kong matches requests to Routes using a router engine. Kong 3.x defaults to traditional_compatible, which compiles your paths/hosts/methods into a fast expression engine while preserving the classic interface. The expressions flavor lets you write router expressions directly; traditional is the legacy pre-3.0 engine (deprecated).


13. Troubleshooting

Troubleshooting entries use the format Error → Cause → Solution → Prevention.

Error: No API key found in request

  • Cause: The key-auth plugin is enabled on the route but the request did not include a valid key in the configured location.
  • Solution: Send the key in the header named by config.key_names (default apikey): -H 'apikey: <key>'. Verify a matching credential exists for a consumer.
  • Prevention: Document the expected header name for your API consumers; the default is apikey.

Error: no Route matched with those values

  • Cause: No Route's matchers (path/host/method/protocol) matched the incoming request.
  • Solution: Check the request path, Host header, and method against your Routes (GET /routes). Remember strip_path and that paths starting with ~ are regexes.
  • Prevention: Add a catch-all route for diagnostics, or use the Kong-Debug: 1 request header with allow_debug_header = on to see which Service/Route matched.

Error: an invalid response was received from the upstream server / 502 Bad Gateway

  • Cause: Kong reached the upstream but the connection failed or the upstream returned an invalid response.
  • Solution: Verify the Service host/port/protocol and that the backend is reachable from the Kong node. Check connect_timeout/read_timeout.
  • Prevention: Use Upstreams with health checks so unhealthy targets are removed automatically.

Error: Kong is already running in <prefix>

  • Cause: kong start found a live NGINX PID in the prefix.
  • Solution: kong stop (or kong restart) first; if the process is truly gone, remove stale sockets/PID from the prefix.
  • Prevention: Use kong restart rather than start when redeploying on the same host.

Error: Kong is not running at <prefix>

  • Cause: kong health/kong stop could not find a running node at the prefix.
  • Solution: Confirm the prefix with -p, and that the node was started with the same prefix/config.
  • Prevention: Always pass a consistent -c/-p (or KONG_PREFIX) across commands.

Error: Database needs bootstrapping or is older than Kong 1.0

  • Cause: The PostgreSQL database has not been initialized with Kong's schema.
  • Solution: Run kong migrations bootstrap -c <conf>.
  • Prevention: Bootstrap as part of your provisioning before the first kong start.

Error: cannot run 'kong config db_import' ... database=off

  • Cause: db_import/db_export only work in traditional (database) mode.
  • Solution: In DB-less mode, reload your declarative configuration with POST /config instead.
  • Prevention: Know which mode the node runs in (database setting).

Error: JWT plugin returns HTTP 500

  • Cause: A misconfigured JWT credential (algorithm/secret mismatch) or unexpected token claim can surface as a 500 (reported in issue #14827 for some token shapes).
  • Solution: Verify the consumer's JWT credential algorithm and secret/rsa_public_key match how the token is signed; enable --vv logging to see the parser error.
  • Prevention: Test tokens with a known-good credential before rolling out; pin the algorithm.

Error: high latency / CPU after adding many regex routes

  • Cause: The PCRE regex cache is too small for the number of regex paths (relevant when router_flavor = traditional).
  • Solution: Increase nginx_http_lua_regex_cache_max_entries (recommended: at least regex_paths * 2), or switch to traditional_compatible/expressions which don't use PCRE.
  • Prevention: Prefer the default traditional_compatible router; avoid traditional unless required.

Tip

For deeper timing analysis, enable request debugging (request_debug = on) and send the X-Kong-Request-Debug: * header. Kong returns per-component timing in X-Kong-Request-Debug-Output. Protect this with X-Kong-Request-Debug-Token.


14. FAQ

Q: Do I need a database to run Kong? No. DB-less mode (database = off) keeps configuration in memory from a declarative file. You only need PostgreSQL for traditional mode or a hybrid control plane.

Q: What's the difference between the proxy port (8000) and the Admin API port (8001)? Port 8000/8443 is where your API clients send traffic. Port 8001/8444 is the management API where you configure Kong. Never expose the Admin API publicly.

Q: How do I expose Kong over HTTPS? HTTPS is enabled by default on 8443. Kong auto-generates a self-signed certificate on first start; supply real certificates with ssl_cert/ssl_cert_key (or per-SNI via Certificate entities), or automate with the acme plugin.

Q: Can a plugin run on every request automatically? Yes — attach it globally with POST /plugins. Scope it to a Service, Route, or Consumer to narrow it.

Q: How many plugins does Kong ship with? 44 in the open-source 3.10.0 distribution. You can also write your own in Lua, Go, JavaScript, Python, or WebAssembly.

Q: What protocols can Kong proxy? http, https, grpc, grpcs, tcp, tls, tls_passthrough, and udp. Stream (TCP/TLS/UDP) routing uses stream_listen.

Q: How do I apply config changes without downtime? kong reload (SIGHUP) reloads kong.conf without dropping connections. Entity changes via the Admin API or POST /config take effect without any restart.

Q: How do I do a zero-downtime version upgrade? Use the expand/contract migration flow: kong migrations up (backward-compatible changes), roll new nodes, then kong migrations finish. Drain old nodes with kong drain + kong quit.

Q: Where do per-consumer identity headers come from? After an auth plugin authenticates a request, Kong injects headers like X-Consumer-ID, X-Consumer-Username, and X-Consumer-Custom-ID to the upstream so your backend knows the caller.

Q: How do I rate-limit across multiple Kong nodes? Use config.policy=redis (shared counters in Redis) or config.policy=cluster. The default local counts per node only.

Q: Is there a UI? Kong Manager (port 8002) provides a web UI. Advanced Manager features depend on Kong Enterprise/Konnect; the open-source build focuses on the Admin API and decK.

Q: How do I limit which plugins load to speed up startup? Set plugins (or KONG_PLUGINS) to just the plugins you use, e.g. key-auth,rate-limiting. This reduces memory and startup time.

Q: My change took a few seconds to take effect — is that normal? Yes, in traditional mode other nodes poll for changes every db_update_frequency seconds (default 5). DB-less/hybrid data planes get pushed updates.

Q: How do I see Kong's version and dependencies? kong version -a.

Q: Can Kong call serverless functions directly? Yes — the aws-lambda and azure-functions plugins invoke cloud functions as the upstream, so a Route can be backed by a function instead of a server.

Q: What is the AI Gateway? A set of plugins (ai-proxy and friends) that give you one unified API across many LLM providers plus prompt governance, security, and observability for AI/MCP traffic.


15. Reference Tables

15.1 Default ports

Port Service Config setting
8000 Proxy (HTTP) proxy_listen
8443 Proxy (HTTPS) proxy_listen
8001 Admin API (HTTP) admin_listen
8444 Admin API (HTTPS) admin_listen
8002 Kong Manager (HTTP) admin_gui_listen
8445 Kong Manager (HTTPS) admin_gui_listen
8007 Status API status_listen
8005 Cluster (hybrid CP) cluster_listen

15.2 Supported protocols

http, https, grpc, grpcs, tcp, tls, tls_passthrough, udp

15.3 Core entities

workspaces, consumers, certificates, services, routes, snis, upstreams, targets, plugins, tags, ca_certificates, clustering_data_planes, parameters, vaults, key_sets, keys, filter_chains

15.4 Kong-injected headers

Header Meaning
X-Kong-Request-Id Unique ID for the request.
X-Kong-Proxy-Latency Time Kong spent processing before proxying (ms).
X-Kong-Upstream-Latency Time the upstream took to send response headers (ms).
X-Kong-Response-Latency Time Kong took to produce a response (ms).
X-Consumer-ID / X-Consumer-Username Identity of the authenticated consumer.
X-RateLimit-Remaining Remaining requests in the current rate-limit window.
Via / Server kong/3.10.0 server tokens.

15.5 Rate-limit windows

second, minute, hour, day, month, year

15.6 Useful Admin API endpoints

Method & Path Purpose
GET / Node information and version.
GET /status Health and database reachability.
POST /services Create a Service.
POST /services/{id}/routes Create a Route under a Service.
POST /consumers Create a Consumer.
POST /{services,routes,consumers}/{id}/plugins Enable a plugin at a scope.
POST /plugins Enable a plugin globally.
POST /config Replace whole config (DB-less).
GET /schemas/plugins/{name} Inspect a plugin's config schema.
GET /upstreams/{id}/health Health status of an upstream's targets.