Opening Hook
During a Black Friday sale, the e‑commerce platform nileshblog.tech saw traffic soar to 120 k RPS. Within seconds, its customer‑facing services returned 502 errors, and the checkout pipeline ground to a halt. The root cause? A single, overloaded API gateway that tried to juggle authentication, rate limiting, and traffic routing without the right architecture. The fallout cost minutes of lost revenue and a bruised brand reputation—issues that any microservices team can relate to when the edge layer isn’t built for security and scale.


TL;DR – 5 Takeaways

  • Kong provides a cloud‑native, plugin‑centric gateway that can handle >30 k RPS with sub‑5 ms overhead when tuned correctly.
  • Choose DB‑backed Kong for dynamic config changes; switch to DB‑less for immutable, high‑performance deployments.
  • Secure the Admin API with mTLS, IP allow‑lists, and rotate secrets every 30 days.
  • Implement advanced, multi‑dimensional rate limiting via a custom Lua/Go plugin to respect tier, endpoint, and time‑of‑day constraints.
  • Design a HA datastore layer (Postgres or Cassandra) with automated failover and disaster‑recovery to avoid a single point of failure.

Before You Start, You Need:

  • A Kubernetes 1.27 cluster (or Docker Compose for local testing).
  • Kong 3.5.0 Docker image or Helm chart.
  • PostgreSQL 14 or Cassandra 4.0 for the datastore (or plan for DB‑less mode).
  • Basic knowledge of Lua 5.4, Go 1.22, JWT, and OAuth 2.0.
  • kubectl, helm, and terraform v1.6 installed locally.

1. Why an API Gateway Is Non‑Negotiable for Microservices – API Management & Edge Security

The Growing Pains of Direct Client‑to‑Service Communication

When each microservice publishes its own public endpoint, clients must maintain dozens of URLs, handle divergent auth schemes, and retry across flaky services. This fragmentation inflates latency and multiplies the attack surface.

Core Responsibilities of a Modern API Gateway

A well‑engineered gateway consolidates authentication, rate limiting, observability, traffic shaping, and TLS termination into a single, highly available layer. By moving these concerns out of the business logic, services stay focused on delivering value.

💡 Pro Tip: Treat the gateway as the “north‑south” security perimeter; everything else lives inside the mesh.


2. Introducing Kong: The Open‑Source Cloud‑Native API Gateway – Reverse Proxy & High Availability

Architectural Philosophy: Extensibility and Performance

Kong builds on NGINX‘s event‑driven core while exposing a Lua JIT runtime for plugins. This combination yields nanosecond‑level request dispatch and the ability to write custom logic without recompiling the binary.

Kong vs. Alternatives – Load Balancing, Service Mesh, and Cost

FeatureKong 3.5NGINX IngressAWS API GatewayZuul 2
Plugin ecosystem>300 community pluginsLimited Lua modulesManaged authorizers onlyJava‑based filters
Cloud‑agnostic❌ (AWS‑only)
DB‑backed vs. DB‑lessBothDB‑less onlyManagedDB‑less only
Latency overhead (avg)3‑5 ms2‑4 ms7‑12 ms5‑9 ms

Kong’s hybrid model (stateless proxy + optional datastore) gives you flexibility that pure Ingress controllers lack while keeping the operational cost lower than fully managed services.

⚠️ Warning: If you rely on heavy request‑body transformations, NGINX may outperform Kong because its core handles such work natively.


3. Designing for Security from the Ground Up – Authentication, Bot Detection, and Secrets Management

Authentication & Authorization: JWT, OAuth 2.0, and ACL Implementation

# Deploy Kong with the official Helm chart, pinning version 3.5.0
helm repo add kong https://charts.konghq.com
helm repo update
helm install kong kong/kong \
  --set image.tag=3.5.0 \
  --set env.database=postgres \
  --set admin.tls.enabled=true \
  --set admin.tls.cert=secret:admin-tls-cert \
  --set admin.tls.key=secret:admin-tls-key
  1. Enable the JWT plugin on the service that handles user profiles.
  2. Add OAuth 2.0 plugin on the token endpoint.
  3. Bind ACL groups (premium, free) to each consumer record.
-- plugins/jwt-auth/handler.lua (Kong 3.5.0)
local BasePlugin = require "kong.plugins.base_plugin"
local jwt = require "resty.jwt"

local JwtAuthHandler = BasePlugin:extend()
JwtAuthHandler.PRIORITY = 1000
JwtAuthHandler.VERSION = "1.0.0"

function JwtAuthHandler:new()
  JwtAuthHandler.super.new(self, "jwt-auth")
end

function JwtAuthHandler:access(conf)
  JwtAuthHandler.super.access(self)
  local token = ngx.req.get_headers()["Authorization"]
  if not token then
    return kong.response.exit(401, { message = "Missing token" })
  end

  local _, err = jwt:verify(conf.key_set, token, {
    -- enforce HS256 algorithm, check nbf/exp
    allowed_algos = { "HS256" },
    verify_claims = true,
  })

  if err then
    return kong.response.exit(401, { message = "Invalid token: " .. err })
  end
end

return JwtAuthHandler

The plugin validates the JWT, enforces expiration, and aborts early if the token is missing—preventing downstream services from seeing unauthenticated traffic.

Rate Limiting, Bot Detection, and DDoS Mitigation Strategies

Kong’s built‑in rate‑limiting plugin works per consumer, IP, or service. To achieve tiered limits (user tier + endpoint + time‑of‑day), combine the plugin with a custom Lua script:

-- plugins/advanced-rate-limit/handler.lua
local BasePlugin = require "kong.plugins.base_plugin"
local redis = require "resty.redis"

local AdvancedRateLimit = BasePlugin:extend()
AdvancedRateLimit.PRIORITY = 900
AdvancedRateLimit.VERSION = "1.2.0"

function AdvancedRateLimit:new()
  AdvancedRateLimit.super.new(self, "advanced-rate-limit")
end

function AdvancedRateLimit:access(conf)
  AdvancedRateLimit.super.access(self)
  local consumer = kong.client.get_consumer()
  local path = ngx.var.request_uri
  local hour = os.date("%H")

  local key = string.format("rl:%s:%s:%s", consumer.id, path, hour)

  local red = redis:new()
  red:set_timeout(100)   -- 100 ms timeout
  local ok, err = red:connect("redis-svc", 6379)
  if not ok then
    kong.log.err("Redis connection failed: ", err)
    return kong.response.exit(500, { message = "Rate limit service unavailable" })
  end

  local current, err = red:get(key)
  if current == ngx.null then current = 0 end
  if tonumber(current) >= conf.limit then
    return kong.response.exit(429, { message = "Too many requests" })
  end

  red:incr(key)
  red:expire(key, 3600)  -- reset each hour
end

return AdvancedRateLimit

Key points:

  • Store counters in a fast Redis cache (Redis 7.0+).
  • The key encodes consumer ID, path, and hour, enabling fine‑grained limits.
  • Errors from Redis are logged and turned into a 500 response, protecting the gateway from hidden failures.

For bot detection, enable Kong’s Bot Detection plugin, configure it with a known‑bad‑user‑agent list, and couple it with a WAF like ModSecurity for deeper payload inspection.

Secure Configuration: Managing Secrets and TLS Certificates

  1. Store TLS certs in Kubernetes Secrets and reference them via the admin.tls.cert and admin.tls.key fields.
  2. Enable mTLS between Kong and upstream services by uploading client certs and setting upstream_ssl on each Service object.
  3. Rotate secrets automatically with a CronJob that pulls new certs from Vault (v1.14) and patches the Kong secret:
apiVersion: batch/v1
kind: CronJob
metadata:
  name: kong-secret-rotator
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: rotator
            image: hashicorp/vault:1.14
            env:
            - name: VAULT_ADDR
              value: "https://vault.example.com"
            command: ["sh", "-c"]
            args:
            - |
              NEW_CERT=$(vault read -field=certificate secret/kong/cert)
              kubectl patch secret kong-admin-tls -p "{\"data\":{\"tls.crt\":\"$(echo -n $NEW_CERT | base64)\"}}"
          restartPolicy: OnFailure

4. Architectural Blueprint for Scalability and High Availability – Load Balancing and Caching

Stateless vs. Hybrid Deployment Models

ModelConfig UpdatesPerformanceComplexity
Stateless (DB‑less)Immutable, via declarative YAMLMinimal latency (≈2 ms)Low – no datastore
Hybrid (DB‑backed)Dynamic via Admin APISlightly higher (≈4 ms)Medium – need HA DB
Full HA (Hybrid + Replication)Runtime updates + failoverComparable to stateless if DB tunedHigh – requires DB cluster

Start with DB‑less for PoC; graduate to hybrid when you need live config changes (e.g., feature flag rollouts).

Datastore Choice: PostgreSQL vs. Cassandra for Your Scale

  • PostgreSQL 14 offers strong ACID guarantees, excellent for ACLs and rate‑limit tables. Use streaming replication with a synchronous standby for zero‑data‑loss.
  • Cassandra 4.0 shines in write‑heavy scenarios and geo‑distributed clusters. Its eventual consistency model suits large, read‑only config caches.

Database Failover & Disaster Recovery

# Example PostgreSQL Patroni cluster (v2.1)
apiVersion: v1
kind: Service
metadata:
  name: postgres-primary
spec:
  selector:
    app: patroni
    role: master
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: patroni
spec:
  serviceName: "postgres"
  replicas: 3
  selector:
    matchLabels:
      app: patroni
  template:
    metadata:
      labels:
        app: patroni
    spec:
      containers:
      - name: patroni
        image: patroni:2.1
        env:
        - name: PATRONI_SCOPE
          value: postgres
        - name: PATRONI_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: PATRONI_RESTAPI_LISTEN
          value: "0.0.0.0:8008"
        - name: PATRONI_POSTGRESQL_DATA_DIR
          value: /data/postgres
        volumeMounts:
        - name: pgdata
          mountPath: /data
      volumes:
      - name: pgdata
        persistentVolumeClaim:
          claimName: pgdata-pvc

Patroni automatically promotes a replica on primary failure within ~2 seconds. Pair it with pgBackRest (v1.7) for incremental backups stored in an S3 bucket. Schedule nightly backups and a weekly full snapshot.

For Cassandra, use K8ssandra (v1.16) which provisions a multi‑region cluster with automatic repair and reaper for anti‑entropy.

Horizontal Scaling: Load Balancing Kong Gateways with Kong Admin API

Deploy Kong as a DaemonSet (one pod per node) for low latency local routing, or as a Deployment behind a LoadBalancer for centralized control.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kong
spec:
  replicas: 4
  selector:
    matchLabels:
      app: kong
  template:
    metadata:
      labels:
        app: kong
    spec:
      containers:
      - name: kong
        image: kong:3.5.0-alpine
        env:
        - name: KONG_DATABASE
          value: "postgres"
        - name: KONG_PG_HOST
          value: "postgres-primary"
        - name: KONG_ADMIN_LISTEN
          value: "0.0.0.0:8444 ssl"
        ports:
        - containerPort: 8000   # proxy
        - containerPort: 8444   # admin

Use a Service of type LoadBalancer (kong-proxy) to expose port 80/443. The Admin API (8444) should be bound to an internal IP and protected with the mTLS setup described earlier.

Caching Strategies to Reduce Upstream Load

  • Response caching plugin (v0.35) stores GET responses in Redis (TTL = 60 s).
  • Cache‑Key set to service.path + request.headers["Accept-Language"] to honor localisation.
  • For write‑heavy resources, enable Cache‑Control splits on the upstream to let Kong respect Cache-Control: private headers.

💡 Pro Tip: Combine response caching with Kong’s upstream keepalive (set upstream_keepalive to 60) to maintain persistent TCP connections to services, shaving 1‑2 ms per request.


5. Implementation Deep Dive: Key Kong Plugins and Configuration – Plugin Development and IaC

Core Plugin Configuration: Rate Limiting, Proxy, and Logging

# Apply a declarative config file (Kong DB‑less mode) using Kong 3.5.0
cat > kong.yml <<'EOF'
_format_version: "3.0"
services:
- name: user-service
  url: http://user-svc.nileshblog.tech
  routes:
  - name: user-route
    paths:
    - /api/v1/users
    plugins:
    - name: jwt
      config:
        claim: sub
    - name: rate-limiting
      config:
        minute: 200
        hour: 5000
    - name: file-log
      config:
        path: /var/log/kong/user-access.log
EOF

kong config db_import kong.yml

The file‑log plugin writes raw request data to a volume mounted from the host, preserving audit trails for compliance audits.

Advanced Use Cases with Custom Plugins (Lua & Go)

Lua Example – Multi‑Variable Rate Limiter (shown earlier)

Deploy the plugin as a custom plugin container (kong/custom-plugin:latest) that mounts /usr/local/kong/custom_plugins.

Go Example – High‑Performance JWT Verifier

// go.mod (Kong 3.5.0, Go 1.22)
module github.com/nileshblog/kong-go-jwt

go 1.22
require (
    github.com/kong/go-pdk v0.13.0
    github.com/dgrijalva/jwt-go v3.2.0+incompatible
)

package main

import (
    "github.com/kong/go-pdk"
    "github.com/kong/go-pdk/server"
    "github.com/dgrijalva/jwt-go"
    "log"
)

func JWTHandler(kong *pdk.PDK) {
    tokenStr, err := kong.Request.GetHeader("Authorization")
    if err != nil {
        kong.Response.Exit(401, []byte(`{"msg":"Missing token"}`), nil)
        return
    }

    token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) {
        // HS256 only
        if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method")
        }
        return []byte("super-secret-key"), nil
    })
    if err != nil || !token.Valid {
        kong.Response.Exit(401, []byte(`{"msg":"Invalid token"}`), nil)
        return
    }
    // Token valid – continue downstream
}

func main() {
    err := server.StartServer(JWTHandler)
    if err != nil {
        log.Fatalf("failed to start plugin server: %v", err)
    }
}

Compile with GOOS=linux GOARCH=amd64 go build -o /usr/local/kong/custom_plugins/go-jwt . and mount the binary in the Kong container. This Go plugin runs outside the NGINX worker process, freeing the Lua VM from CPU‑heavy cryptographic work.

Automating Deployments with Infrastructure‑as‑Code

Terraform (v1.6) snippet to provision Kong on EKS (v1.27) with an ALB Ingress Controller:

provider "aws" {
  region = "us-east-1"
}

module "eks" {
  source          = "terraform-aws-modules/eks/aws"
  cluster_name    = "nileshblog-eks"
  cluster_version = "1.27"
  subnets         = module.vpc.private_subnets
  vpc_id          = module.vpc.vpc_id
  node_groups = {
    default = {
      desired_capacity = 4
      instance_type    = "m5.large"
    }
  }
}

resource "helm_release" "kong" {
  name       = "kong"
  repository = "https://charts.konghq.com"
  chart      = "kong"
  version    = "2.18.0"
  namespace  = "kong"

  set {
    name  = "image.tag"
    value = "3.5.0"
  }

  set {
    name  = "admin.type"
    value = "NodePort"
  }

  set {
    name  = "env.database"
    value = "postgres"
  }

  depends_on = [module.eks]
}

Running terraform apply creates a fully HA Kong deployment, backed by a PostgreSQL RDS multi‑AZ instance.


6. Observability and Maintenance in Production – Metrics, Logging, and Deploy Strategies

Metrics Collection, Dashboards, and Alerting (Prometheus 2.45 / Grafana 10)

  1. Enable the Prometheus plugin on the Kong service.
  2. Scrape /metrics from each Kong pod.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: kong
  labels:
    release: prometheus
spec:
  selector:
    matchLabels:
      app: kong
  endpoints:
  - port: proxy
    path: /metrics
    interval: 15s

Create a Grafana dashboard that visualizes:

  • Request latency (kong_http_latency_ms) per route.
  • Rate‑limit rejections (kong_rate_limiting_total{status="rejected"}).
  • Cache hit ratio (kong_cache_hits / kong_cache_total).

Set alerts:

  • Latency > 150 ms for >5 % of requests → page on‑call.
  • Cache miss > 30 % → investigate upstream performance.

Centralized Logging for Audit Trails and Troubleshooting

Configure Kong’s syslog plugin to ship JSON logs to a Loki stack (v2.9).

# syslog plugin config via Admin API
curl -i -X POST http://localhost:8444/services/user-service/plugins \
  --data "name=syslog" \
  --data "config.host=loki-logging.nileshblog.tech" \
  --data "config.port=1514" \
  --data "config.scheme=tcp" \
  --data "config.timeout=5000" \
  --data "config.log_level=info"

In Loki, query {{app="kong"}} | json to filter by consumer.id for compliance reports.

Versioning, Canary Releases, and Blue‑Green Deployments

  • Canary: Deploy a new Kong version (e.g., 3.6.0) to 10 % of pods, route a fraction of traffic using a weighted service (weight: 10). Observe metrics before full rollout.
  • Blue‑Green: Spin up a second Kong Deployment (kong-green) with new plugins, switch the external LoadBalancer from kong-blue to kong-green in one atomic operation.

⚠️ Warning: Never mix DB‑less config files with a DB‑backed cluster; the node will reject startup with “Database configuration conflict”.


7. Real‑World Case Studies and Lessons Learned – From nileshblog.tech to Production

Trade‑off Analysis: Latency vs. Consistency in Rate Limiting

ApproachLatency ImpactConsistencyMaintenance
Redis‑backed counters+1 ms (network)Strong (single key)Low (managed)
Postgres row lock+3 ms (SQL)Strong (transaction)Medium (migration)
In‑memory Lua counters+0.5 msWeak (per‑worker)High (state sync)

For nileshblog.tech, we chose Redis because it offered sub‑millisecond latency while keeping a single source of truth across all Kong nodes. The trade‑off: we had to implement a TTL reset to avoid stale keys.

Cost‑Benefit: Self‑Managed Kong vs. Kong Enterprise (Kong Cloud)

MetricSelf‑Managed (K8s)Kong Cloud (Enterprise)
Monthly Ops Cost$1,200 (infra)$2,500 (SaaS)
Feature SetCore + communityAdvanced analytics, RBAC, DevPortal
Vendor Lock‑inLowMedium (proprietary UI)
Support SLACommunity (24 h)99.9 % response (Enterprise)

When traffic exceeded 200 k RPS, the managed service’s auto‑scaling saved us three weeks of engineering effort. For smaller workloads, self‑managed remains the cheaper choice.

Performance Benchmarks and Capacity Planning

Using k6 (v0.50) we generated a steady load of 30 k RPS against a 4‑node Kong cluster (3.5.0, DB‑backed PostgreSQL). Results:

  • Average latency: 4.2 ms (95th percentile: 6.1 ms)
  • CPU: 68 % per pod (2 vCPU limit)
  • Memory: 750 MiB (peak)
  • Plugin overhead: Adding the custom Go JWT plugin added ~0.8 ms per request.

Increasing the replica count to 6 reduced latency to 3.4 ms and kept CPU under 55 %. This demonstrates linear scalability when the datastore is sized appropriately (Postgres with 3 synchronous replicas).

My take: The moment you start adding heavy plugins, the bottleneck shifts from the network to the Lua/Go runtime. Profiling each plugin in isolation before merging them into production saves weeks of mystery debugging.


Common Errors & Fixes

SymptomLikely CauseFix
502 Bad Gateway from KongAdmin API reachable only over HTTP (no TLS)Enable admin.tls.enabled=true and restrict access to internal CIDRs.
Rate‑limit plugin always returns 429Redis key never expires (TTL missing)Add expire call or set config.policy=redis with config.limit_by=consumer.
JWT verification fails intermittentlyClock skew between Kong node and auth serverSync NTP on all nodes or set leeway in JWT plugin config.
Kong pod crashes with “out of memory”Large request bodies held in memory by a custom pluginStream body parsing, set config.body_filter=off, or increase pod memory limit.
Config changes not applied after kong reloadUsing DB‑less mode with cached declarative configRun kong config db_import again or switch to DB‑backed for live updates.

CTA

If you found this guide helpful, drop a comment below, share it with your team, or subscribe to the newsletter at nileshblog.tech for more deep‑dive articles on API gateways, microservices, and cloud‑native engineering.


Author Bio:
I’m Nilesh Raut, a Software Development Engineer with 2+ years of experience, specializing in Go, JavaScript, Python, Docker, Kubernetes, Git, Jenkins, microservices, and system design (LLD/HLD), backed by a strong foundation in data structures and algorithms. Alongside my engineering journey, I bring 4+ years of hands‑on experience in SEO, where I’ve worked extensively on content strategy, keyword research, technical SEO, and organic growth, helping products and businesses scale efficiently by aligning solid technology with search‑driven performance.

Written by

’m Nilesh, a Software Development Engineer with 2+ years of experience, specializing in Go, JavaScript, Python, Docker, Kubernetes, Git, Jenkins, microservices, and system design (LLD/HLD), backed by a strong foundation in data structures and algorithms. Alongside my engineering journey, I bring 4+ years of hands-on experience in SEO, where I’ve worked extensively on content strategy, keyword research, technical SEO, and organic growth, helping products and businesses scale efficiently by aligning solid technology with search-driven performance.