This tool automates the migration of Kubernetes Ingress resources from NGINX Ingress Controller to Kong Ingress Controller (KIC). It parses existing NGINX annotations and translates them into Kong-equivalent configurations, generating Ingress, KongPlugin, and KongUpstreamPolicy resources.
- Automated Translation: Converts NGINX annotations to Kong Plugins and CRDs.
- Modern Kong Support: targeting
KongUpstreamPolicyfor upstream configurations (replacing deprecatedKongIngress). - Full Coverage Testing: Includes an End-to-End (E2E) test suite validating the migration against a live cluster.
- Safe Defaults: Generates standard configuration while warning about incompatible or manual-intervention items.
The migration tool includes a web-based dashboard that connects to your cluster and lets you browse, analyse, and migrate Ingress resources interactively.
Download the binary from the releases page, then start the dashboard:
./migrator uiOptional flags:
-port <int>— port to listen on (default:8080)-namespace <string>— restrict to a single namespace (default: all namespaces)-kubeconfig <string>— path to kubeconfig (falls back to$KUBECONFIGor~/.kube/config)
Example:
./migrator ui -port 9090 -namespace kongOpen http://localhost:8080 in your browser. The dashboard reads Ingress resources from your currently active cluster context and colour-codes each one:
- Green — already migrated (non-NGINX ingress)
- Yellow — ready to migrate, may have followup notes
- Red — has unmigrated annotations that require manual intervention
From the dashboard you can:
- Migrate Selected / Migrate Single — applies the migration in-cluster: sets
ingressClassName: kong, createsKongPluginandKongUpstreamPolicyCRDs - Copy to Namespace — copies one or more ingresses (with their Kong resources) to a target namespace
- Download as Kong Ingress YAML / Gateway API YAML — export the migrated manifests without applying them
- Refresh — reload the ingress list from the cluster
You can run the dashboard as a Docker container outside the cluster by mounting your kubeconfig:
docker run --rm \
-p 8080:8080 \
-e KUBECONFIG=/kubeconfig \
-v "${KUBECONFIG:-$HOME/.kube/config}:/kubeconfig:ro" \
kong/nginx-kong-migrator:latest uiThen open http://localhost:8080. The container reads the mounted kubeconfig to connect to whichever cluster context is currently active.
To target a specific context, set it before running:
kubectl config use-context <your-context>
docker run --rm \
-p 8080:8080 \
-e KUBECONFIG=/kubeconfig \
-v "${KUBECONFIG:-$HOME/.kube/config}:/kubeconfig:ro" \
kong/nginx-kong-migrator:latest uiThe deploy/kubernetes.yaml manifest creates a dedicated kong-migrator namespace and installs all required resources (ServiceAccount, ClusterRole, ClusterRoleBinding, Deployment, Service):
kubectl apply -f deploy/kubernetes.yamlWait for the pod to become ready:
kubectl -n kong-migrator rollout status deployment/kong-migratorPort-forward the service to your local machine:
kubectl -n kong-migrator port-forward svc/kong-migrator 8080:8080Then open http://localhost:8080 in your browser. The dashboard reads Ingress resources across all namespaces using the pod's service account — no kubeconfig or extra credentials required.
To restrict the dashboard to a single namespace, edit the Deployment and add --namespace <your-namespace> to the args list.
kubectl delete -f deploy/kubernetes.yamlThis removes all resources created by the manifest, including the kong-migrator namespace.
The tool supports 25+ NGINX Ingress annotations across multiple categories:
| NGINX Annotation | Kong Output | Notes |
|---|---|---|
rewrite-target |
Kong Ingress: konghq.com/rewriteGateway API: HTTPRoute.filters.urlRewrite |
Static rewrites supported |
backend-protocol |
konghq.com/protocol |
HTTP, HTTPS, GRPC |
ssl-redirect |
konghq.com/https-redirect-status-code (301) |
Force HTTPS |
force-ssl-redirect |
konghq.com/https-redirect-status-code (301) |
Force HTTPS |
app-root |
konghq.com/rewrite |
Redirect / to specified path |
| NGINX Annotation | Kong Output | Notes |
|---|---|---|
limit-rps |
rate-limiting Plugin |
Requests per second |
limit-rpm |
rate-limiting Plugin |
Requests per minute |
limit-connections |
Warning + Manual Config | Requires Enterprise rate-limiting-advanced |
| NGINX Annotation | Kong Output | Notes |
|---|---|---|
auth-url |
external-auth Plugin |
External authentication service |
auth-signin |
openid-connect Plugin |
Enterprise - OIDC with placeholders |
enable-cors |
Kong Ingress: cors PluginGateway API: ResponseHeaderModifier |
CORS headers |
cors-allow-origin |
cors Plugin |
Allowed origins |
cors-allow-methods |
cors Plugin |
Allowed HTTP methods |
cors-allow-headers |
cors Plugin |
Allowed headers |
cors-allow-credentials |
cors Plugin |
Allow credentials |
cors-max-age |
cors Plugin |
Preflight cache duration |
whitelist-source-range |
ip-restriction Plugin |
IP allowlist |
| NGINX Annotation | Kong Output | Notes |
|---|---|---|
proxy-body-size |
request-size-limiting Plugin |
Max request body size |
configuration-snippet (headers) |
request-transformer & response-transformer Plugins |
Header injection only |
server-snippet |
Warning | Not supported |
| NGINX Annotation | Kong Output | Notes |
|---|---|---|
proxy-connect-timeout |
konghq.com/connect-timeout |
Connect timeout in ms |
proxy-read-timeout |
konghq.com/read-timeout |
Read timeout in ms |
proxy-send-timeout |
konghq.com/write-timeout |
Write timeout in ms |
| NGINX Annotation | Kong Output | Notes |
|---|---|---|
affinity |
KongUpstreamPolicy |
Cookie-based session affinity |
session-cookie-name |
KongUpstreamPolicy |
Custom cookie name for affinity |
session-cookie-max-age |
KongUpstreamPolicy |
Cookie TTL |
canary |
Kong Ingress: canary PluginGateway API: Weighted backendRefs |
Canary deployments |
canary-weight |
Kong Ingress: canary PluginGateway API: Weighted backendRefs |
Traffic splitting percentage |
upstream-keepalive-connections |
Warning | Global Kong setting, not per-upstream |
| NGINX Annotation | Kong Output | Notes |
|---|---|---|
proxy-cache |
proxy-cache Plugin |
Enterprise - Response caching |
proxy-cache-valid |
proxy-cache Plugin |
Cache TTL configuration |
| NGINX Annotation | Kong Output | Notes |
|---|---|---|
mirror-target |
Warning + Manual Config | Requires custom plugin (not bundled) |
The following annotations are detected but not automatically migrated:
auth-method- No direct equivalentauth-response-headers- No direct equivalentbase-auth-secret- Use Kong's basic-auth plugin manuallypreserve-trailing-slash- Kong default behaviorconfiguration-snippet(non-header) - Complex snippets not supportedmodsecurity-*- Third-party WAF integration required
go build -o migrator main.go./migrator --input input.yaml --output output.yaml./migrator --input input.yaml --output httproute.yaml --output-format gateway-api./migrator --input input.yaml --output-format both
# Generates: migrated-kong-ingress.yaml & migrated-gateway-api.yamlFlags:
--input: Path to input YAML containing NGINX Ingress resources--output: Path to output YAML (default:migrated-output.yaml)--output-format: Output format -kong-ingress(default),gateway-api, orboth--ingress-class: (Optional) Set theingressClassNamein the generated Ingress
Kong Ingress:
kubectl apply -f migrated-kong-ingress.yamlGateway API:
# Install Gateway API CRDs (one-time)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
# Deploy HTTPRoute and plugins
kubectl apply -f migrated-gateway-api.yamlNote: Some features like affinity generate KongUpstreamPolicy resources which require you to MANUALLY annotate your Service. The tool will log these actions.
The tool supports migrating to Kubernetes Gateway API (HTTPRoute) format, offering:
Native Gateway API Features:
- URL Rewrites:
rewrite-target→HTTPRoute.filters.urlRewrite - Canary Routing:
canary-weight→ weightedbackendRefs - CORS Headers:
enable-cors→ResponseHeaderModifierfilter
Kong Plugin Integration (via ExtensionRef):
- Rate limiting, authentication, caching, and other advanced features
- Plugins attached to HTTPRoute using
ExtensionReffilters - Full compatibility with Kong-specific features
Benefits:
- Standards-based (works across implementations)
- Portable configurations
- Future-proof with Kubernetes Gateway API
Input (NGINX Ingress):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
rules:
- host: api.example.com
http:
paths:
- path: /v1
backend:
service:
name: api-service
port:
number: 80Output (Gateway API):
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api
spec:
hostnames:
- api.example.com
parentRefs:
- name: kong
rules:
- matches:
- path:
type: PathPrefix
value: /v1
filters:
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: Access-Control-Allow-Origin
value: "*"
backendRefs:
- name: api-service
port: 80
weight: 80
- name: api-service-canary
port: 80
weight: 20The project includes a robust E2E test script e2e-test.sh that works with Orbstack, Minikube, or Kind.
- Docker / Orbstack
- Helm
- Go 1.22+
kubectlconfigured for your cluster
./e2e-test.shEnterprise Testing:
To enable Kong Enterprise features (like openid-connect) in the test:
- Place your
license.jsonfile in the root of this project. - Run
./e2e-test.sh. The script will automatically detect the license and deploy Kong Gateway Enterprise.
This script will:
- Install NGINX and Kong Controllers via Helm.
- Deploy a full-coverage backend application (
ealen/echo-server). - Run the migration tool against a complex "kitchen sink" ingress.
- Apply the result and wait for Kong to program routes.
- Verify Traffic: Sends real HTTP requests to validate Connectivity, CORS, Headers, Rate Limiting, and Session Affinity.
The tool is written in Go and structured as follows:
main.go: Entry point, parses flags and input files.pkg/parser: Reads Kubernetes YAML manifests.pkg/mappers: Contains logic for each annotation category (e.g.,ratelimit.go,auth.go).pkg/generator: specific structs and logic for generating Kong CRDs (KongPlugin,KongUpstreamPolicy).
- Regex Rewrites: Complex NGINX regex captures in
rewrite-targetare not fully automated and may require manualrequest-transformerconfiguration. - Snippets: Arbitrary NGINX configuration snippets are not supported, except for specific header directives which are parsed into transformers.