Applications Azure

Kong Gateway 3 on Ubuntu 24.04 on Azure User Guide

| Product: Kong Gateway 3.9.1 OSS on Ubuntu 24.04 LTS on Azure

Overview

Kong Gateway is the leading open-source API gateway and microservices ingress — a Lua/OpenResty data plane on top of nginx that handles routing, authentication, rate limiting, transformations, observability, and dozens of other concerns through its plugin ecosystem. The cloudimg image installs Kong Gateway 3.9.1 OSS Community Edition pinned via apt-mark hold, backed by PostgreSQL 16 on loopback, with the Admin API bound strictly to 127.0.0.1:8001 (Kong OSS has no Admin API authentication, so loopback is the security boundary). A first-boot oneshot wires up an example service, route, key-auth plugin, and consumer so the gateway is exercising real upstream traffic the moment the VM finishes booting. The decK CLI is bundled at /usr/local/bin/deck for declarative configuration management.

What is included:

  • Kong Gateway 3.9.1 OSS Community Edition (Apache 2.0) from the official Cloudsmith repository gateway-39/noble, pinned via apt-mark hold kong=3.9.1
  • PostgreSQL 16 from Ubuntu noble main, with a per-VM kong role and kong database, listening on 127.0.0.1:5432 only
  • Admin API on 127.0.0.1:8001 — loopback only, never exposed
  • Proxy plane on 0.0.0.0:8000 (HTTP) and 0.0.0.0:8443 (HTTPS)
  • decK CLI 1.59.1 at /usr/local/bin/deck for declarative config management
  • kong-firstboot.service oneshot — runs migrations bootstrap, generates the per-VM database password and consumer API key, writes /etc/kong/kong-firstboot.env, touches /var/lib/cloudimg/kong-firstboot.done
  • kong-postboot.service oneshot — polls Admin API ready, creates the example service, route, key-auth plugin and consumer via the Admin API, touches /var/lib/cloudimg/kong-postboot.done, then self-disables
  • Per-VM credentials log at /stage/scripts/kong-credentials.log (root:root 0600) carrying KONG_DB_PASSWORD, KONG_CONSUMER_API_KEY, KONG_PROXY_URL
  • 24/7 cloudimg support

There is no web UI — Kong OSS administration is API-only via the Admin API, decK CLI, or any of the Kong-compatible tooling.

Prerequisites

Active Azure subscription, SSH key, VNet + subnet. Standard_B2s (4 GB RAM) is comfortable for a single-node Kong + PostgreSQL on the same VM. NSG inbound: allow 22/tcp from your management CIDR, and 8000/tcp plus 8443/tcp from any client CIDR that needs to reach the proxy. Never expose 8001/tcp — that is the Admin API and it has no authentication in Kong OSS.

Step 1-3: Deploy + SSH (standard pattern)

ssh azureuser@<vm-ip>

Step 4: Service Status + Versions

sudo systemctl is-active kong.service postgresql.service kong-firstboot.service kong-postboot.service
/usr/local/bin/kong version
/usr/local/bin/deck version

All four services should report active. Kong reports 3.9.1 and decK reports v1.59.1.

All four services active and Kong 3.9.1 + decK 1.59.1 reported

Step 5: Read Per-VM Credentials

sudo cat /stage/scripts/kong-credentials.log

Pick up KONG_CONSUMER_API_KEY (the demo consumer's key-auth credential — used in Step 8), KONG_PROXY_URL, and KONG_DB_PASSWORD (the per-VM PostgreSQL password Kong uses to talk to its database). The file is root:root 0600.

Step 6: Admin API Health

curl -sf http://127.0.0.1:8001/status | jq

Returns the gateway's connection counters, lua_shared_dicts memory utilisation, and database.reachable: true (PostgreSQL roundtrip OK). The Admin API listens on 127.0.0.1:8001 only — it is bound to loopback because Kong OSS Admin API has no authentication and exposing it would let any client reconfigure the gateway. If you need remote administration, tunnel through SSH (ssh -L 8001:127.0.0.1:8001 azureuser@<vm-ip>) or front the Admin API with a reverse proxy that does its own auth.

Kong Admin API /status returns 200 with database connectivity confirmed

Step 7: List Services and Routes

curl -sf http://127.0.0.1:8001/services | jq '.data[].name'
curl -sf http://127.0.0.1:8001/routes | jq '.data[].paths'

You will see cloudimg-firstboot-service and ["/example"] — the demo wired up by the postboot oneshot. The service points to https://httpbin.org/anything and the route is protected by a key-auth plugin.

Kong Admin API services + routes endpoints showing the example wired up at firstboot

Step 8: Test the Proxy with key-auth

Pull the consumer's API key out of the credentials log, then call the proxy with and without it:

KEY=$(sudo grep '^KONG_CONSUMER_API_KEY=' /stage/scripts/kong-credentials.log | cut -d= -f2)
curl -sf -H "apikey: $KEY" http://127.0.0.1:8000/example | jq '.url'
curl -sI http://127.0.0.1:8000/example | head -1

The first call returns the upstream URL string from httpbin.org (proves Kong proxied the request). The second call has no apikey header and returns HTTP/1.1 401 Unauthorized — proves the key-auth plugin is enforcing the credential.

Kong proxy /example with valid apikey returns 200 from upstream; missing key returns HTTP 401

Step 9: Manage Routes via Admin API

Add a new service and route imperatively. The example below adds a route on /github that proxies to https://api.github.com:

curl -s -X POST http://127.0.0.1:8001/services \
  -d 'name=github-api' \
  -d 'url=https://api.github.com'
curl -s -X POST http://127.0.0.1:8001/services/github-api/routes \
  -d 'name=github-route' \
  -d 'paths[]=/github'
curl -sf http://127.0.0.1:8001/routes | jq '.data[].paths'

To remove a route or service, use DELETE on the same endpoints:

curl -s -X DELETE http://127.0.0.1:8001/routes/github-route
curl -s -X DELETE http://127.0.0.1:8001/services/github-api

Step 10: Manage Routes via decK (declarative)

decK lets you treat the gateway's configuration as a YAML file you can put under version control. Dump the current config, edit it, then sync it back:

sudo /usr/local/bin/deck gateway dump --kong-addr http://127.0.0.1:8001 -o /tmp/kong.yaml
sudo /usr/local/bin/deck gateway diff --kong-addr http://127.0.0.1:8001 /tmp/kong.yaml

dump writes a snapshot of every service, route, plugin, consumer and credential. diff shows the delta between the YAML file and the live gateway — should report no changes immediately after a fresh dump. To apply the YAML back, use deck gateway sync. In production, version-control the YAML in git and run deck gateway sync from CI.

Step 11: Add Plugins

Kong's plugin ecosystem covers rate limiting, authentication (jwt, oauth2, basic-auth, ldap-auth), transformations, observability (prometheus, opentelemetry, zipkin), traffic control, and more. Attach the rate-limiting plugin to the example service:

curl -s -X POST http://127.0.0.1:8001/services/cloudimg-firstboot-service/plugins \
  -d 'name=rate-limiting' \
  -d 'config.minute=10'

Browse the full plugin catalogue at https://docs.konghq.com/hub/. Plugins can be attached at the global, service, route, or consumer scope.

Step 12: Tune Worker Processes

Kong inherits nginx's worker model. Edit /etc/kong/kong.conf to set nginx_worker_processes:

sudo sed -i 's/^#nginx_worker_processes.*/nginx_worker_processes = auto/' /etc/kong/kong.conf
sudo systemctl restart kong.service

Defaults on Standard_B2s:

  • admin_listen: 127.0.0.1:8001 (loopback only — security boundary)
  • proxy_listen: 0.0.0.0:8000, 0.0.0.0:8443 ssl
  • database: postgres
  • pg_host: 127.0.0.1
  • nginx_worker_processes: auto (one worker per vCPU)

For higher throughput on bigger VMs, auto is usually correct — Kong's data plane is heavily I/O-bound and benefits from one worker per CPU core.

Step 13: Open the Proxy to Public Traffic

The NSG attached to this VM at deploy time already allows 8000/tcp and 8443/tcp from your chosen client CIDR. To restrict further (e.g. only allow your CDN's egress ranges), edit the NSG inbound rules in the Azure portal or via:

az network nsg rule update -g <rg> --nsg-name <nsg> -n allow-kong-8000 \
  --source-address-prefixes <your-cidr>

Inside the VM, Ubuntu's default firewall (ufw) is disabled — Kong listens directly. If you enable ufw, add sudo ufw allow 8000/tcp and sudo ufw allow 8443/tcp.

Step 14: Install Let's Encrypt SSL

Kong has an official ACME plugin that handles Let's Encrypt issuance and renewal entirely inside the gateway:

curl -s -X POST http://127.0.0.1:8001/plugins \
  -d 'name=acme' \
  -d 'config.account_email=you@example.com' \
  -d 'config.tos_accepted=true' \
  -d 'config.domains[]=apps.example.com'

Point your domain DNS A record at this VM, then curl https://apps.example.com:8443/example — Kong will request a certificate from Let's Encrypt on the first hit and serve it from then on. Renewal is automatic via Kong's internal scheduler.

Alternatively, terminate SSL at nginx (or Caddy) in front of Kong and proxy to 127.0.0.1:8000. This is the older pattern and is useful if you already have a fronting reverse proxy; the ACME plugin is the simpler choice for net-new deployments.

Step 15: Backups

Kong's only persistent state is its PostgreSQL database. Take a consistent backup with the gateway running:

PASS=$(sudo grep '^KONG_DB_PASSWORD=' /stage/scripts/kong-credentials.log | cut -d= -f2-)
PGPASSWORD="$PASS" pg_dump -h 127.0.0.1 -U kong kong | gzip > /var/backups/kong-db-$(date +%F).sql.gz
sudo cp /etc/kong/kong.conf /var/backups/kong.conf-$(date +%F)

If you are using decK to manage Kong declaratively (Step 10), also commit your YAML to git — the YAML plus a fresh PostgreSQL pg_dump together are a complete, replayable backup. Periodically copy /var/backups to Azure Blob Storage (az storage blob upload-batch) for off-VM retention.

Step 16: Logs and Troubleshooting

sudo journalctl -u kong.service --no-pager -n 80
sudo journalctl -u kong-firstboot.service --no-pager -n 30
sudo journalctl -u kong-postboot.service --no-pager -n 30
sudo tail -50 /usr/local/kong/logs/access.log
sudo tail -50 /usr/local/kong/logs/error.log

kong reload is a graceful nginx reload — apply config changes without dropping in-flight connections:

sudo /usr/local/bin/kong reload

For a deeper inspection, the Admin API exposes everything: curl http://127.0.0.1:8001/status, /services, /routes, /plugins, /consumers, /upstreams, /targets, plus Prometheus-style metrics if you enable the prometheus plugin.

Security

  • Admin API bound to 127.0.0.1:8001 only — Kong OSS has no Admin API auth, so loopback is the boundary. Never bind the Admin API to a routable interface.
  • PostgreSQL listens on 127.0.0.1:5432 only — Kong is the only consumer.
  • Per-VM KONG_DB_PASSWORD (32 hex chars) generated at first boot.
  • Per-VM KONG_CONSUMER_API_KEY for the demo consumer — rotate or replace once you wire up your own consumers.
  • NSG inbound opens 22/tcp, 8000/tcp, 8443/tcp only — never 8001/tcp.
  • Use the decK CLI's gateway diff workflow to catch unintended config drift before applying it.
  • Use the key-auth, jwt, oauth2, or basic-auth plugins on every public route. Kong does not authenticate by default.

Support

cloudimg provides 24/7/365 expert technical support. Guaranteed response within 24 hours, one hour average for critical issues. Contact support@cloudimg.co.uk.