Ingress NGINX Migration
The Ingress NGINX Migration is a tool that analyzes Kubernetes NGINX Ingress resources to help with migration planning to Traefik.
Features
The Ingress NGINX Migration tool creates and serves an interactive HTML report,
and to do so it:
- Analyzes all Ingress resources in a Kubernetes cluster or specific namespaces
- Identifies Ingress NGINX Controller annotations and their compatibility with Traefik
- Supports both in-cluster deployment and external kubeconfig access
- Generates timestamped migration HTML report showing:
- Total number of Ingress resources
- How many can be migrated automatically
- Which Ingress resources need manual attention
- Unsupported annotations and their frequency
- Provides flexible ingress filtering by controller class, ingress class name, and namespace
Supported NGINX Annotations
The Ingress NGINX Migration checks for compatibility with common Ingress NGINX Controller annotations including:
- Authentication (
auth-type, auth-secret, auth-realm, auth-url, auth-signin, etc.)
- Client TLS authentication (
auth-tls-secret, auth-tls-verify-client)
- SSL/TLS (
force-ssl-redirect, ssl-redirect, ssl-passthrough)
- Path rewriting (
use-regex, rewrite-target, app-root)
- Redirects (
permanent-redirect, temporal-redirect, from-to-www-redirect, etc.)
- Session affinity (
affinity, session-cookie-*)
- Backend configuration (
service-upstream, backend-protocol, proxy-ssl-*)
- Proxy timeout (
proxy-connect-timeout)
- CORS (
enable-cors, cors-allow-*)
- IP allowlisting (
whitelist-source-range, allowlist-source-range)
- Custom headers (
custom-headers, upstream-vhost)
- Buffering (
proxy-request-buffering, proxy-body-size, proxy-buffering, etc.)
- ModSecurity (
enable-modsecurity, enable-owasp-core-rules, modsecurity-transaction-id, modsecurity-snippet) — Traefik Hub only
For a complete list of supported annotations and their Traefik equivalents, see the Ingress NGINX Annotations table in the Traefik documentation.
Installation
Quick Install (Recommended)
Install the latest version using the install script:
curl -sSL https://raw.githubusercontent.com/traefik/ingress-nginx-migration/main/scripts/install.sh | bash
Install a specific version:
curl -sSL https://raw.githubusercontent.com/traefik/ingress-nginx-migration/main/scripts/install.sh | TAG=v0.0.1 bash
Install without sudo (installs to ~/bin):
curl -sSL https://raw.githubusercontent.com/traefik/ingress-nginx-migration/main/scripts/install.sh | bash -s -- --no-sudo
Manual Download
Download the appropriate binary for your platform from the releases page.
Via Web Interface:
- Open the migration report in your browser
- Click the "Share Report" button that appears in the report interface (If unsupported ingresses or annotations are detected)
- Optionally view the exact data to be sent before confirming
Note: The "Share Report" button only appears when there are compatibility issues to report.
If your analysis shows all ingresses are compatible, no sharing option will be displayed.
Usage
NAME:
ingress-nginx-migration - Analyze NGINX Ingresses to build a migration report to Traefik
USAGE:
ingress-nginx-migration [global options] [command [command options]]
COMMANDS:
version Shows the current version
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--addr string Defines the address to listen on for serving the migration report. (default: ":8080") [$ADDR]
--kubeconfig string Defines the kubeconfig file to use to connect to the Kubernetes cluster. [$KUBECONFIG]
--namespaces string [ --namespaces string ] Defines the namespaces to analyze. When empty, all namespaces are analyzed. [$NAMESPACES]
--ingress-class string Defines the name of the ingress class this controller satisfies. [$INGRESS_CLASS]
--controller-class string Defines the Ingress Controller class to analyze. When empty, 'k8s.io/ingress-nginx' is used. [$CONTROLLER_CLASS]
--watch-ingress-without-class Defines if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. [$WATCH_INGRESS_WITHOUT_CLASS]
--ingress-class-by-name Defines if Ingress Controller should watch for Ingress Class by Name together with Controller Class. [$INGRESS_CLASS_BY_NAME]
--format string Output the report once in this format ('json' or 'markdown') and exit, instead of serving the HTML report. When empty, the HTML report is served. [$FORMAT]
--output-file string Write the one-shot report to this file instead of stdout. Requires --format. Overwrites an existing file. [$OUTPUT_FILE]
--summary Omit the per-Ingress detail from the report. Only valid with --format markdown. [$SUMMARY]
--help, -h Show help
[!TIP]
Quick Start: Run the tool with your kubeconfig:
ingress-nginx-migration --kubeconfig ~/.kube/config
Or using environment variables:
KUBECONFIG=~/.kube/config ingress-nginx-migration
Output Formats
The --format flag allows you to output the generated report to stdout or a file, in either Markdown or JSON format.
This can be useful for use in automations and keeping track of an ongoing migration.
# Machine-readable JSON to stdout:
ingress-nginx-migration --kubeconfig ~/.kube/config --format json
# Full human-readable Markdown:
ingress-nginx-migration --kubeconfig ~/.kube/config --format markdown
# Compact Markdown status (counts only, no per-Ingress detail):
ingress-nginx-migration --kubeconfig ~/.kube/config --format markdown --summary
By default, these flags will write to stdout (default logs will be redirected to stderr). They can be combined with the --output-file flag in order to write to a file instead.
Notes:
-
--format json emits the full report, including the names/namespaces of
Ingresses that need manual migration. It also exposes a hash field as a digest
of the report content excluding the timestamp allowing you to detect
changes between runs without relying on the generationDate.
-
--summary is Markdown-only; combining it with --format json will result in an error.
-
In one-shot mode logs go to stderr, so stdout carries only the report.
-
The tool only exits non-zero only on real errors (no cluster
access, bad flags, write failure), never because Ingresses are incompatible.
You can Gate a CI job on full compatibility by inspecting the JSON, e.g. with jq:
ingress-nginx-migration --format json | jq -e '.unsupportedIngressCount == 0'
Required Permissions
The Ingress NGINX Migration requires specific read-only permissions to analyze your cluster's Ingress resources.
The tool is principally designed to be run externally by SREs and system administrators who have access to a Kubernetes cluster via kubeconfig.
But the same requirement would apply for an In-Cluster pod approach.
Your kubeconfig user or service account must have the following permissions:
| API Group | Resources | Verbs | Scope |
|---|
networking.k8s.io/v1 | ingressclasses | list, get, watch | Cluster-wide |
networking.k8s.io/v1 | ingresses | list, get, watch | Namespace-scoped* |
[!NOTE]
Namespace Scope:
The tool supports the --namespaces flag.
If specific namespaces are provided, permissions are only required for those namespaces.
If no namespaces are specified, the tool will attempt to analyze all namespaces, and requiring permission across all namespaces for Ingresses.
Why These Permissions?
The tool uses Kubernetes client-go informers to efficiently cache and monitor Ingress and IngressClass resources.
Informers require list, get, and watch permissions to:
- list: Retrieve all existing resources for initial analysis
- get: Fetch individual resources when needed
- watch: Receive updates when resources change (for report refresh functionality)
All operations are read-only - the tool never modifies any cluster resources.
Send Report Feature
The Ingress NGINX Migration tool includes an optional feature to share anonymized usage statistics with Traefik Labs.
This helps the Traefik team understand real-world NGINX Ingress usage patterns and prioritize compatibility improvements for the migration process.
The data is transmitted securely over HTTPS to Traefik Labs.
Privacy and Data Protection
Important: No sensitive data is transmitted.
The tool only sends aggregated statistics and counts - never actual ingress configurations, resource names, namespaces, or any other identifying information from your cluster.
Data Transmitted
When you choose to share your report, the following anonymized data is sent:
{
"generationDate": "2024-12-03T14:30:25.123Z",
"version": "v1.0.0",
"ingressCount": 42,
"compatibleIngressCount": 38,
"vanillaIngressCount": 15,
"supportedIngressCount": 23,
"unsupportedIngressCount": 4,
"unsupportedIngressAnnotations": {
"nginx.ingress.kubernetes.io/custom-annotation": 2,
"nginx.ingress.kubernetes.io/experimental-feature": 2
}
}
How to Use
Via Web Interface:
- Open the migration report in your browser
- Click the "Share Report" button in the report interface
- Optionally view the exact data to be sent before confirming
Utility endpoints exposed by the Ingress NGINX Migration tool
| Method | Path | Description |
|---|
GET | / | Serve the HTML migration report |
PUT | /send | Send usage data to Traefik Labs |
PUT | /update | Update the migration report |
E2E Tests
The e2e tests spin up a k3s cluster using testcontainers and deploy both Traefik and NGINX Ingress controllers to verify annotation compatibility.
Running
The TRAEFIK_IMAGE environment variable is required. It must point to a Traefik image available in your local Docker daemon that includes the kubernetesIngressNginx provider:
TRAEFIK_IMAGE=traefik/traefik:v100.0.0 make e2e-test
Reusing the k3s Cluster
Creating the k3s cluster from scratch takes ~60-90s. Set E2E_REUSE_CLUSTER=true to keep the cluster running between test runs:
# First run creates the cluster (~90s):
E2E_REUSE_CLUSTER=true TRAEFIK_IMAGE=traefik/traefik:v100.0.0 make e2e-test
# Subsequent runs reuse it (~20s):
E2E_REUSE_CLUSTER=true TRAEFIK_IMAGE=traefik/traefik:v100.0.0 make e2e-test
To force a fresh cluster (e.g. after changing TRAEFIK_IMAGE):
docker rm -f e2e-k3s-cluster
[!NOTE]
The Traefik image is not reloaded when reusing the cluster. If you change TRAEFIK_IMAGE, remove the container first.