This guide walks you through running a multi-zone Kong Mesh deployment in Universal mode using Docker containers. We’ll start a PostgreSQL database to back the control planes, deploy a global control plane, register a zone control plane against it, and run a zone ingress so cross-zone traffic can flow.
Deploy a multi-zone global Kong Mesh control plane in Universal mode with Docker
Run PostgreSQL, a global control plane, and a zone control plane as Docker containers, then attach a zone ingress so the zone can join the global mesh and exchange traffic with other zones.
Install Kong Mesh
-
Run the following command to install the Kong Mesh binaries:
curl -L https://developer.konghq.com/mesh/installer.sh | VERSION=2.13.6 sh -Copied! -
Add the binaries to your system’s path:
export PATH="$(pwd)/kong-mesh-2.13.6/bin:$PATH"Copied! -
Run the following command to confirm that Kong Mesh is installed correctly:
kumactl version 2>/dev/nullCopied!You should see the following output:
Client: Kong Mesh 2.13.6
Create a temporary directory
Set up a temporary directory to store the zone token and the ZoneIngress resource file. Ensure the path doesn’t end with a trailing /.
If you are using Colima, make sure to adjust the path in the steps of this guide. Colima only allows shared paths from the
HOMEdirectory or/tmp/colima/. Instead of/tmp/kong-mesh-multi-zone, you can use/tmp/colima/kong-mesh-multi-zone.
Create the directory if it doesn’t exist:
export KONG_MESH_MULTI_ZONE_TMP="/tmp/kong-mesh-multi-zone"
mkdir -p "$KONG_MESH_MULTI_ZONE_TMP"Create a Docker network
Set up a separate Docker network for the containers. In this example we’ll use IP addresses in the 172.18.78.0/24 range:
docker network create \
--subnet 172.18.0.0/16 \
--ip-range 172.18.78.0/24 \
--gateway 172.18.78.254 \
kong-mesh-multi-zoneStart PostgreSQL
The global and zone control planes both need a database to persist state in Universal mode. Run a single PostgreSQL container and create one database per control plane.
-
Run PostgreSQL:
docker run \ --detach \ --name kong-mesh-multi-zone-postgres \ --hostname postgres \ --network kong-mesh-multi-zone \ --ip 172.18.78.10 \ --env POSTGRES_USER=kong \ --env POSTGRES_PASSWORD=pass123 \ postgres:16Copied! -
Create a database for each control plane:
until docker exec kong-mesh-multi-zone-postgres pg_isready -U kong -d postgres >/dev/null 2>&1; do sleep 1 done docker exec --interactive kong-mesh-multi-zone-postgres \ psql -U kong -d postgres <<'SQL' CREATE DATABASE global; CREATE DATABASE zone1; SQLCopied!
Start the global control plane
The global control plane accepts connections from zone control planes, distributes policies, and keeps an inventory of all data plane proxies across zones.
-
Run the database migrations for the
globaldatabase:docker run --rm \ --network kong-mesh-multi-zone \ --env KUMA_STORE_TYPE=postgres \ --env KUMA_STORE_POSTGRES_HOST=postgres \ --env KUMA_STORE_POSTGRES_PORT=5432 \ --env KUMA_STORE_POSTGRES_USER=kong \ --env KUMA_STORE_POSTGRES_PASSWORD=pass123 \ --env KUMA_STORE_POSTGRES_DB_NAME=global \ kong/kuma-cp:2.13.6 migrate upCopied! -
Run the global control plane:
docker run \ --detach \ --name kong-mesh-multi-zone-global-control-plane \ --hostname global-control-plane \ --network kong-mesh-multi-zone \ --ip 172.18.78.1 \ --publish 5681:5681 \ --publish 5685:5685 \ --env KUMA_MODE=global \ --env KUMA_ENVIRONMENT=universal \ --env KUMA_STORE_TYPE=postgres \ --env KUMA_STORE_POSTGRES_HOST=postgres \ --env KUMA_STORE_POSTGRES_PORT=5432 \ --env KUMA_STORE_POSTGRES_USER=kong \ --env KUMA_STORE_POSTGRES_PASSWORD=pass123 \ --env KUMA_STORE_POSTGRES_DB_NAME=global \ kong/kuma-cp:2.13.6 runCopied!The global control plane exposes:
- Port
5681: the HTTP API and GUI. - Port
5685: the Kong Mesh Discovery Service (KDS) endpoint that zone control planes connect to.
- Port
Configure kumactl
To manage the deployment with kumactl, connect it to the global control plane.
-
Run the following command to get the admin token from the global control plane:
export KONG_MESH_MULTI_ZONE_ADMIN_TOKEN="$( docker exec kong-mesh-multi-zone-global-control-plane \ wget --quiet --output-document - \ http://127.0.0.1:5681/global-secrets/admin-user-token \ | jq --raw-output .data \ | base64 --decode )"Copied! -
Use the retrieved token to link kumactl to the global control plane:
kumactl config control-planes add \ --name kong-mesh-multi-zone-global \ --address http://127.0.0.1:5681 \ --auth-type tokens \ --auth-conf "token=$KONG_MESH_MULTI_ZONE_ADMIN_TOKEN" \ --skip-verifyCopied! -
Run the following command to verify the connection. No zones are registered yet:
kumactl get zonesCopied!You should see an empty list.
Configure the default mesh
Apply an mTLS-enabled Mesh to the global control plane. mTLS is required for cross-zone communication because Kong Mesh uses the Server Name Indication field of the TLS handshake to pass routing information between zones. The mesh syncs down to every zone once registered:
echo 'type: Mesh
name: default
meshServices:
mode: Exclusive
mtls:
enabledBackend: ca-1
backends:
- name: ca-1
type: builtin' | kumactl apply -f -Start the zone control plane
Each zone runs its own control plane. The zone control plane connects to the global control plane over KDS and serves XDS configuration to local data plane proxies.
-
Run the database migrations for the
zone1database:docker run --rm \ --network kong-mesh-multi-zone \ --env KUMA_STORE_TYPE=postgres \ --env KUMA_STORE_POSTGRES_HOST=postgres \ --env KUMA_STORE_POSTGRES_PORT=5432 \ --env KUMA_STORE_POSTGRES_USER=kong \ --env KUMA_STORE_POSTGRES_PASSWORD=pass123 \ --env KUMA_STORE_POSTGRES_DB_NAME=zone1 \ kong/kuma-cp:2.13.6 migrate upCopied! -
Run the zone control plane:
docker run \ --detach \ --name kong-mesh-multi-zone-zone1-control-plane \ --hostname zone1-control-plane \ --network kong-mesh-multi-zone \ --ip 172.18.78.2 \ --publish 25681:5681 \ --publish 25678:5678 \ --env KUMA_MODE=zone \ --env KUMA_MULTIZONE_ZONE_NAME=zone1 \ --env KUMA_ENVIRONMENT=universal \ --env KUMA_STORE_TYPE=postgres \ --env KUMA_STORE_POSTGRES_HOST=postgres \ --env KUMA_STORE_POSTGRES_PORT=5432 \ --env KUMA_STORE_POSTGRES_USER=kong \ --env KUMA_STORE_POSTGRES_PASSWORD=pass123 \ --env KUMA_STORE_POSTGRES_DB_NAME=zone1 \ --env KUMA_MULTIZONE_ZONE_GLOBAL_ADDRESS=grpcs://global-control-plane:5685 \ --env KUMA_MULTIZONE_ZONE_KDS_TLS_SKIP_VERIFY=true \ kong/kuma-cp:2.13.6 runCopied!The zone control plane exposes:
- Port
25681: the local HTTP API and GUI. - Port
25678: the local XDS endpoint that data plane proxies inside the zone connect to.
KUMA_MULTIZONE_ZONE_KDS_TLS_SKIP_VERIFY=trueis required because the global control plane’s certificate is self-signed in this demo. For production, use a certificate signed by a trusted CA. See Secure access across services for more information. - Port
-
Confirm the zone registered against the global control plane:
kumactl get zonesCopied!You should see
zone1listed asOnline.
Start the zone ingress
A zone ingress is the entry point for cross-zone traffic. Without it, other zones have no way to reach services in zone1.
-
Generate a zone token scoped to ingress and egress for
zone1:kumactl generate zone-token \ --zone=zone1 \ --valid-for 720h \ --scope ingress \ --scope egress \ > "$KONG_MESH_MULTI_ZONE_TMP/token-zone1"Copied! -
Create a
ZoneIngressresource definition:echo 'type: ZoneIngress name: zone1-ingress networking: address: 172.18.78.3 port: 10000 advertisedAddress: zone1-ingress advertisedPort: 10000' > "$KONG_MESH_MULTI_ZONE_TMP/zone1-ingress.yaml"Copied!networking.addressis the addresskuma-dpbinds to inside the ingress container. It must be a real address. In this example, we’ll use the static IP we’ll assign to the ingress container in the next step:172.18.78.3.advertisedAddressandadvertisedPortare the values other zones use to reach this ingress. On this single-host Docker network, the container hostnamezone1-ingressresolves via Docker’s embedded DNS and works for both directions. -
Run the zone ingress data plane proxy:
docker run \ --detach \ --name kong-mesh-multi-zone-zone1-ingress \ --hostname zone1-ingress \ --network kong-mesh-multi-zone \ --ip 172.18.78.3 \ --publish 10000:10000 \ --volume "$KONG_MESH_MULTI_ZONE_TMP:/demo" \ kong/kuma-dp:2.13.6 run \ --proxy-type=ingress \ --cp-address=https://zone1-control-plane:5678 \ --dataplane-token-file=/demo/token-zone1 \ --dataplane-file=/demo/zone1-ingress.yamlCopied! -
Confirm the zone ingress registered with the zone control plane:
kumactl get zone-ingressesCopied!You should see
zone1-ingresslisted.
Verify the deployment
-
Confirm the zone is healthy and online:
kumactl inspect zonesCopied!You should see
zone1reportingOnlinewith its ingress listed. -
Open the global control plane UI at http://127.0.0.1:5681/gui and check that:
-
zone1displays in the Zones view. -
zone1-ingressdisplays in the zone’s Ingresses tab. - The
defaultmesh has mTLS enabled.
-
Cleanup
Remove the Kong Mesh containers, network, and temporary directory
docker rm --force \
kong-mesh-multi-zone-zone1-ingress \
kong-mesh-multi-zone-zone1-control-plane \
kong-mesh-multi-zone-global-control-plane \
kong-mesh-multi-zone-postgres
docker network rm kong-mesh-multi-zone
rm -rf "${KONG_MESH_MULTI_ZONE_TMP:-/tmp/kong-mesh-multi-zone}"Remove the kumactl control plane
kumactl config control-planes remove --name kong-mesh-multi-zone-global