You may think: „Wait, buddy! Aren’t you a bit late?“ The answer to this questions is „Yes and No“. Well, more „No“ but let’s sort this out.
TL;DR: SUSE Rancher and RKE2 support Ingress-nginx until 2028, so there’s plenty of time to get to know the possible ways to migrate to Traefik, Cilium or Calico.
The Situation
According to https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/ the open source implementation Ingress-nginx was retired by 2026-03-31. This means that development is discontinued and no security fixes are available anymore after that date.
However: SUSE is delivering their own, hardened Ingress-nginx image together with RKE2. According to the announcement from early 2026 Ingress-nginx will be supported for 2 years in version v1.35. The announcement from Kubecon EU 2026 introduced an even longer support for RKE2 v1.36 until 2028. It is limited to security fixes. Earlier versions will be delivered with a supported Ingress-nginx version for their normal life-cycle. E.g. RKE2 v1.34 EOL (End of Life) is 2027-02-27, all older versions have shorter EOL. See the complete RKE2 lifecycle! This means that SUSE customers don’t need to rush and can carefully plan the migration to fit the needs of their enterprise.
The designated successor of Ingress-nginx at SUSE is Traefik. Its support in RKE2 is available starting with the August 2024 releases: v1.28.12+rke2r1, v1.29.7+rke2r1, v1.30.3+rke2r1. But there are also other migration targets available as RKE2 addons, like Cilium or Calico. Traefik support in Rancher is available in v2.14 (which was released end of March 2026).
This article only covers the official RKE2 addons Ingress-nginx, Traefik, Cilium and Calico. RKE2 is derived from k3s, the described methods and techniques also apply to k3s.
A few words about RKE2 addons
- RKE2 is delivered with a number of addons, including Ingress controllers and CNIs, which are tightly integrated in the RKE2 deployment process. See the addon documentation for details!
- Addons are basically Helm charts which are deployed as static manifests. They make use of the
HelmChart– andHelmChartConfig-API - Specific RKE2 versions contain one specific version of an addon. E.g. v1.34.6+rke2r3 comes with Ingress-nginx v1.14.5-hardened1 and Traefik v3.6.10. An update of an Ingress controller usually involves updating RKE2.
- The default Helm chart values for the addons are derived from their respective upstream projects, but are modified in various ways. Check here for the SUSE modifications! Make sure to check the actual values.yaml files, the markdown documents are sometimes not up-to-date!
- Some remarkable differences between e.g. the upstream Ingress-nginx chart and the RKE2 chart are, that
HostPortsare used and the controller is deployed asDaemonSetrather than aDeployment.
Available Ingress Controller Choices
Ingress NGINX
Ingress NGINX is/was the de facto standard Ingress controller in Kubernetes environments. RKE2 deploys a bundled version automatically if there is no explicit configuration. So you may be running Ingress NGINX without actually knowing.
Ingress NGINX implements the Ingress API and supports a lot of additional features with annotations, e.g. nginx.ingress.kubernetes.io/ssl-redirect or nginx.ingress.kubernetes.io/app-root. These annotations are not standardized, every Ingress controller is using its own way of implementing additional features.
The open source Ingress NGINX project was retired on 2026/03/25 live at Kubecon EU 2026.
Ingress NGINX onlys support the Ingress API.
Traefik
Traefik is the designated Ingress-nginx successor by SUSE. It will be the default Ingress controller in RKE2 starting with v1.36. It can replace most installations of Ingress-nginx because it has a dedicated Ingress-nginx compatibility mode, which is off by default. The compatibility mode is available since v3.5.4 and considered stable since v3.6.2.
Traefik v3.6.2 is bundled with these RKE2 versions:
- RKE2 v1.32 >= v1.32.11+rke2r1
- RKE2 v1.33 >= v1.33.7+rke2r1
- RKE2 v1.34 >= v1.34.3+rke2r1
- RKE2 v1.35 >= v1.35.0+rke2r1
Not all of the Ingress-nginx annotations are supported. The list of supported annotations can be checked here. Make sure to consult the documentation for the Traefik version bundled with the RKE2 version you’re planning to use! Usually there is a version gap of a few minor versions.
Traefik Labs created a migration tool which analyzes the existing Ingress-nginx configuration and assists with the migration.
All RKE2 clusters running Ingress NGINX can be migrated to Traefik. The used CNI (either Canal, Calico, Cilium) does not matter. Traefik supports the Ingress-API and also the Gateway-API. Gateway-API is disabled by default.
Traefik Migration Tool
The check tool and its documentation are available here. It will analyze the guest cluster and start a local HTTP service on port 8080 on the node where the tool is executed. Use curl or a browser to access the report:

The migration tool only informs about unsupported annotations. One still needs to check for supported annotations! If there are Ingress NGINX annotations but the tool did not complain, the Ingress NGINX compatibility mode needs to be used. All supported annotations in Ingress NGINX mode are listed in the Traefik documentation. Make sure to use the version of the Traefik docs that matches your RKE2 version! Ingresses without any Ingress-NGINX annotations can be migrated without using the compatibility mode.
Cilium
Cilium comes with its own Ingress controller. There are prerequisites: „kube-proxy replacement“ or „node-port“ features needs to be enabled. For additional functionality, also L7 features (Envoy) need to be enabled. See all prerequisites here. It is possible to migrate Cilium from kube-proxy to the kube-proxy replacement mode with downtime. Cilium does not have an Ingress-nginx compatibility mode. This means that the migration probably needs additional changes in the Ingress objects. Some features are only available in the Enterprise version of Cilium.
There is an Isovalent blog article about possible choices for migration. A description of a Cilium Host Port Ingress/Gateway-API setup similar to the Host port configuration of Ingress-nginx is available here. These are just examples which cannot be used directly in RKE2 because they assume that the RKE2 Cilium addon is disabled.
Cilium supports the Ingress-API and also the Gateway-API. Both need to be enabled on demand.
Calico
Calico also has its own Ingress controller (Calico Ingress Gateway) derived from open source Envoy Gateway. It can be enabled by configuring the Calico operator. Only basic functionality is available with the open source version. There is an Enterprise version with extended features.
Calico Ingress Gateway only supports Gateway-API and is disabled by default.
Ingress or Gateway API?
The decision to migrate to another Ingress or to Gateway API depends on the necessary efforts and the complexity of the existing Ingress annotations.
| Ingress API | Gateway API | |
| No Ingress-nginx annotations are used | Traefik, Cilium | |
| Some Ingress-nginx annotations are used and are supported by Traefik | Traefik with Ingress-nginx compatibility mode | |
| A lot of Ingress-nginx annotations are used which are not supported by Traefik | Traefik, Cilium, Calico |
Gateway API is designed for a wide feature set and to move away from the „annotation hell“. It is GA for some time, however it is still in development and some features and APIs may change. Traefik and Cilium can provide both Ingress API and Gateway API at the same time, which opens the path to a seamless migration. The tool ingress2gateway may help converting from various Ingresses to Gateway API, but is in an early development stage.
Ingress is feature complete and will not change anymore in the future. So far, there have been no announcements about the deprecation of Ingress API. There is no clear winner at the moment. This means the people should use whatever fits their use case.
Possible Migration Paths
Big Bang Migration
A Big Bang migration involves a short downtime of the Ingress resources. It should only be used if there are no or no unknown Ingress-nginx annotations used anywhere. It is possible to remove the RKE2 Ingress-nginx controller and install another Ingress controller in the same deployment step. RKE2 takes care about the correct sequence of removal and installation steps. The existing Loadbalancer service can be reused if it’s not managed in the controller Helm deployment. It just needs to be modified with the updated selectors for the new Ingress controller.
Seamless Migration
This type of migration involves the installation of a second Ingress controller while the old Ingress-nginx controller is still running. In addition, a second LoadBalancer service needs to be created. Ingress objects are duplicated on demand. After all new Ingresses have been successfully tested, either the old load balancer should be directed to the new Ingress controller or the productive DNS entries have to be moved to the new load balancer.
General Approach for a Migration
- Assess your Ingress NGINX helm chart values for changes from the default. Sometimes there are global options configured, e.g. wildcard TLS certificates, buffer sizes etc. Use
kubectl get HelmChartConfig -n kube-system rke2-ingress-nginx -o yaml - Assess all configured Ingress objects. Use the Rancher UI or
kubectl get ingress -A -o yaml - Assess all used Ingress NGINX annotations. Some can be ignored.
- For Traefik check for unsupported annotations
- Find alternative configurations or workarounds for unsupported annotations
- For Traefik choose the Ingress Controller mode (either with or without Ingress NGINX compatibility)
- Choose the migration model (Big Bang or Seamless migration)
- Test the migration in a test environment
- Deploy the new Ingress controller
- Adjust Ingresses, Load balancers, DNS etc.
- Remove the old Ingress controller
The following examples may or may not work in your environment. There are too many possible combinations of external load balancers or ways to configure your Ingress objects to cover them all here. Take the examples as a base and adjust them to your environment!
Migration to Traefik
Standalone or imported RKE2 clusters
Standalone clusters are usually managed outside of a Rancher Managment environment, e.g. with Ansible or Terraform. Even if they are imported to a Rancher Manager, they behave differently compared to a Rancher Managed Cluster.
Big Bang Migration
To switch from Ingress-nginx to Traefik /etc/rancher/rke2/config.yaml needs to contain:
disable: - rke2-ingress-nginx ingress-controller: traefik
Changes to the default Traefik configuration are to be done in a HelmChartConfig which needs to be placed into /var/lib/rancher/rke2/server/manifests/rke2-traefik-config.yaml on the control-plane nodes. Depending on the used IngressClassName, the Traefik configuration should either include the IngressClassName nginx or has to watch for Ingresses without a class (see below). If your cluster doesn’t use any Ingress-nginx annotations, the compatibility mode can be disabled completely.
---
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: rke2-traefik
namespace: kube-system
spec:
valuesContent: |-
global:
checkNewVersion: false # these 2 should always be present!
sendAnonymousUsage: false
additionalArguments:
- "--log.level=DEBUG" # optional higher debug level
providers:
kubernetesGateway: # optional enable Gateway API
enabled: true
kubernetesIngressNginx: # optional enable IngressNginx compatibility
enabled: true
watchIngressWithoutClass: true # watches empty Ingresses
ingressRoute: # optional, don't configure in prod!
dashboard:
enabled: true
matchRule: Host(`dashboard.local`)
services:
- name: api@internal
kind: TraefikService
entryPoints:
- web # dashboard runs on port 80 without TLS!
Change all config.yaml files on all nodes! Restarting the first control plane node (systemctl restart rke2-server) will remove the Helm chart rke2-ingress-nginx and install the Traefik Helm charts on the whole cluster. Subsequent RKE2 restarts (either server or agent) of all other nodes are still necessary.
The default RKE2 addon deployments of Ingress-nginx and Traefik don’t deploy a LoadBalancer service. For easier migration it is recommended to NOT deploy the LoadBalancer service together with the controller via Helm, but manage it separately. This way it is possible to switch the controller without deleting the loadbalancer to avoid e.g. the assignment of a new IP address by the cloud provider. Example loadbalancer manifest:
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: rke2-traefik
app.kubernetes.io/instance: rke2-traefik-kube-system
name: rke2-traefik
namespace: kube-system
spec:
type: LoadBalancer
ports:
- name: http
port: 80
protocol: TCP
targetPort: web
- name: https
port: 443
protocol: TCP
targetPort: websecure
selector:
app.kubernetes.io/instance: rke2-traefik-kube-system
app.kubernetes.io/name: rke2-traefik
See the RKE2 ingress documentation for more details. Especially take a look into the Traefik Ingress-nginx docs if you rely on certain Ingress-nginx annotations!
Seamless Migration
This time /etc/rancher/rke2/config.yaml needs to be modified to contain both Ingress addons:
ingress-controller: - traefik - ingress-nginx
Both addons use host ports in their default Helm values, therefore the new one needs to be adjusted to use different ports. This needs to be done in the static manifest /var/lib/rancher/rke2/server/manifests/rke2-traefik-config.yaml:
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: rke2-traefik
namespace: kube-system
spec:
valuesContent: |-
global:
checkNewVersion: false # these 2 should always be present!
sendAnonymousUsage: false
ports:
web:
hostPort: 8000
websecure:
hostPort: 8443
additionalArguments:
- "--log.level=DEBUG" # optional higher debug level
providers:
kubernetesIngressNginx: # optional enable IngressNginx compatibility
enabled: true
ingressClass: "rke2-ingress-nginx-migration"
controllerClass: "rke2.cattle.io/ingress-nginx-migration"
Make sure the IngressClassName of all of your Ingress objects is set to „nginx“, otherwise your application will not be reachable afterwards! RKE2 will configure Traefik as the default Ingress if it is installed alongside Ingress-nginx. Run systemctl restart rke2-server to apply the new configuration.
Now there are 2 Ingress controllers running, but Traefik doesn’t have a LoadBalancer yet. Apply the above loadbalancer service manifest rke2-traefik to get one!
The next step is to duplicate all Ingress objects to use the IngressClass rke2-ingress-nginx-migration. This way the applications can be accessed over 2 different ways and 2 different Ingress controllers. The setup can be tested and modified until the new Ingress configuration and annotations are working as expected. The final step is to switch over the external loadbalancer to the new Ingress LoadBalancer service and remove the old Ingresses.
This scenario is also described in the SUSE knowledge base articles 000022184 and 000022285, a SCC account might be required for access.
Rancher managed RKE2 clusters
Rancher can manage the complete lifecycle of a guest cluster using cloud and/or node providers. This can be done via the Rancher UI or by maintaining cluster.provisioning.cattle.io/v1 manifests, either by kubectl or terraform. The actual cluster deployment looks very similar to a standalone deployment, there are config YAML files, static manifests etc.
As of the day of writing, only Rancher v2.14.0 is able to create cluster configurations which show the Traefik feature in the UI. It was announced that this feature will be backported to Rancher v2.13.5. Older Rancher versions are likely not receiving this functionality. It is still possible to create Traefik configurations with these Rancher versions, but it is necessary to do this in the cluster provisioning manifest directly via Edit YAML.
Attention: Editing the cluster object directly can cause serious damage to the cluster. Be careful!

The relevant parts of the cluster manifest for a big bang migration look like:
apiVersion: provisioning.cattle.io/v1
kind: Cluster
spec:
rkeConfig:
machineGlobalConfig:
disable:
- rke2-ingress-nginx
ingress-controller: traefik
while the seamless migration looks like this:
apiVersion: provisioning.cattle.io/v1
kind: Cluster
spec:
rkeConfig:
machineGlobalConfig:
ingress-controller:
- traefik
- ingress-nginx
The remaining configuration procedure is the same as in the standalone examples, the HelmChartConfigs have to be placed into the same cluster provisioning manifest. The rke2-traefik addon has no own configuration name and needs to go to additionalManifest (all other manifests work also if placed here):
apiVersion: provisioning.cattle.io/v1
kind: Cluster
spec:
rkeConfig:
chartValues:
rke2-ingress-nginx:
...
rke2-cilium:
...
rke2-calico:
...
additionalManifest: |-
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: rke2-traefik
namespace: kube-system
spec:
valuesContent: |-
global:
checkNewVersion: false
...
providers:
kubernetesGateway:
enabled: true
LoadBalancer and Ingress objects can be managed „as usual“ from the Rancher UI, via kubectl, terraform, ArgoCD, Fleet, your favorite IaaS…
SUSE has released knowledge base article 000022470 for the seamless migration of a managed guest cluster via the Rancher UI.
Migration to Cilium Ingress/Gateway API
It is important to note that RKE2 does not support the change from one CNI to another, see KB article 000020904. If your cluster does not run Cilium, don’t progress from here. Consider creating a new cluster with Cilium instead and migrate the workload.
Prerequisites
To be able to use either the Ingress controller or the Gateway API features, it is necessary to enable the kube-proxy replacement mode in Cilium. This change will cause a downtime to your cluster! If kube-proxy replacement mode is already active, the prerequisite paragraphs can be skipped.
Prerequisites for Standalone or imported RKE2 clusters
- kube-proxy needs to be disabled on every cluster node in the RKE2 configuration
/etc/rancher/rke2/config.yaml:
cni: cilium disable-kube-proxy: true
- The replacement mode needs to be enabled in the Cilium
HelmChartConfigat/var/lib/rancher/rke2/server/manifests/rke2-cilium-config.yaml:
---
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: rke2-cilium
namespace: kube-system
spec:
valuesContent: |-
kubeProxyReplacement: true
k8sServiceHost: localhost
k8sServicePort: 6443
- Now all RKE2 services on all nodes need to be restarted, on control plane nodes:
systemctl restart rke2-server, on worker nodes:systemctl restart rke2-agent. This should terminate all kube-proxy instances on all nodes. Double-check that this really happened. When it didn’t, the static manifests need to be removed manually, on the control plane:rm /var/lib/rancher/rke2/server/manifests/kube-proxy.yaml, on workers:rm /var/lib/rancher/rke2/agent/pod-manifests/kube-proxy.yaml. Following the migration procedure from Cilium, the remaining kube-proxy iptable rules need to be removed:
iptables-save | grep -v KUBE | iptables-restore
Network issues can still remain for existing pods, the general recommendation is to reboot all cluster nodes.
Prerequisites for Rancher managed RKE2 clusters
The general procedure is very similar to the one for standalone clusters. Again the cluster provisioning YAML needs to be changed (similar to the Traefik changes) because the Rancher UI does not support disabling kube-proxy. Use Edit YAML in the cluster manager:
apiVersion: provisioning.cattle.io/v1
kind: Cluster
spec:
rkeConfig:
machineGlobalConfig:
cni: cilium
disable-kube-proxy: true
chartValues:
rke2-cilium:
kubeProxyReplacement: true
k8sServiceHost: localhost
k8sServicePort: 6443
After the cluster deployment, the migration needs to be finished:
- On agent nodes (nodes with the worker role only) the kube-proxy pods are not automatically removed by Rancher. This needs to be done manually with
systemctl restart rke2-agentand/or emoving the static manifest withrm /var/lib/rancher/rke2/agent/pod-manifests/kube-proxy.yaml. Check also control plane nodes for left-over kube-proxy pods! - Remove the remaining kube-proxy iptables rules from all the nodes (either by
iptables-save | grep -v KUBE | iptables-restoreor rebooting the node)
Standalone or imported RKE2 clusters
Big Bang Migration
This type of migration makes only sense if you don’t use any Ingress-nginx annotations at all. Depending on the configured IngressClass you may have to modify the used IngressClassName of the existing Ingresses to „cilium“. Modify /etc/rancher/rke2/config.yaml to disable Ingress-nginx:
disable: - rke2-ingress-nginx
In the HelmChartConfig (or in the Addon config in the Rancher UI) the Ingress Controller needs to be enabled:
---
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: rke2-cilium
namespace: kube-system
spec:
valuesContent: |-
kubeProxyReplacement: true
k8sServiceHost: localhost
k8sServicePort: 6443
l7Proxy: true
ingressController:
enabled: true
default: false # optional set to true
service:
type: LoadBalancer # optional ClusterIP and bring your own!
loadbalancerMode: shared # check docs for dedicated mode
gatewayAPI:
enabled: true # optional enable gateway api
After systemctl restart rke2-server all Ingresses should be working as before. If they are not, the default IngressClass may no be set. Add IngressClassName: cilium to all Ingresses to fix this. Then restart all RKE2 services on all cluster nodes!
Most of the Cilium configuration changes require the redeployment of the Cilium pods:
kubectl -n kube-system rollout restart deployment/cilium-operator kubectl -n kube-system rollout restart ds/cilium
If Gateway API was enabled, the actual work starts now. A gateway needs to be created, as well as TLSRoutes and HTTPRoutes. Check ingress2gateway for the first steps. This is beyond the scope of this article.
Seamless Migration
Only the Cilium HelmChartConfig needs to be adjusted to add the IngressController options. It’s the same as with the big bang example. By default Cilium Ingress is not using host ports, so it can be installed in parallel with Ingress-nginx. The deployment also creates a LoadBalancer service with the name „cilium-ingress“. To prevent the creation, set service.type: ClusterIP in the HelmchartConfig and manage the Loadbalancer service independently.
After all existing Ingresses have been migrated to Cilium, either by moving the Loadbalancer services or changing the DNS of the newly created LoadBalancer, the old Ingress-nginx needs to be removed from /etc/rancher/rke2/config.yaml followed by a systemctl restart rke2-server to finish the migration:
disable: - rke2-ingress-nginx
Rancher managed RKE2 clusters
The general comments for Cilium are the same as in the Traefik paragraph, including the lack of some support in the Rancher UI. Make sure to change things with Edit YAML in the cluster manager.
Big Bang Migration
The Big Bang migration does not contain surprises:
apiVersion: provisioning.cattle.io/v1
kind: Cluster
spec:
rkeConfig:
machineGlobalConfig:
cni: cilium
disable-kube-proxy: true
disable:
- rke2-ingress-nginx
chartValues:
rke2-cilium:
kubeProxyReplacement: true
k8sServiceHost: localhost
k8sServicePort: 6443
l7Proxy: true
ingressController:
enabled: true
default: false # optional set to true
loadbalancerMode: shared # check docs for dedicated mode
gatewayAPI:
enabled: true # optional enable gateway api
The seamless migration creates the needed LoadBalancer services by default:
apiVersion: provisioning.cattle.io/v1
kind: Cluster
spec:
rkeConfig:
machineGlobalConfig:
cni: cilium
disable-kube-proxy: true
chartValues:
rke2-cilium:
kubeProxyReplacement: true
k8sServiceHost: localhost
k8sServicePort: 6443
l7Proxy=true
ingressController:
enabled: true
default: false # optional set to true
service:
type: LoadBalancer # optional ClusterIP and bring your own
loadbalancerMode: shared # check docs for dedicated mode
gatewayAPI:
enabled: true # optional enable gateway api
Redeploy the Cilium pods to activate the changes:
kubectl -n kube-system rollout restart deployment/cilium-operator kubectl -n kube-system rollout restart ds/cilium
To finalize the seamless migration, add the options to disable rke2-ingress-nginx, adjust load balancers and/or DNS.
Migration to Calico Ingress Gateway
Here it is also important to note that RKE2 does not support the change from one CNI to another, see KB article 000020904. If your cluster does not run Calico, don’t progress from here, consider creating a new cluster.
Even though Canal is a variant of Calico, it lacks the Calico operator which is a requirement to configure Calico Ingress Gateway. It may (or may not) be possible to deploy the Calico operator on top of a Canal setup. This was not tested.
Calico Ingress Gateway only supports Gateway API. A big bang migration is possible but doesn’t make a lot of sense. Since the actual deployment is not related to RKE2 or Rancher, there are far less RKE2 related options and the Tigera documentation can be followed. At the end of a migration the rke2-ingress-nginx addon needs to be disabled. It’s identical for all Ingress Controllers. See the examples above.
At first, the Gateway API CRDs need to be applied. Make sure to apply the correct version that matches your deployed Calico version!
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.5/manifests/operator-crds.yaml kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.5/manifests/tigera-operator.yaml kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.5/manifests/custom-resources.yaml
Following that, the Gateway API feature needs to be enabled via Calico operator:
kubectl apply -f - <<EOF apiVersion: operator.tigera.io/v1 kind: GatewayAPI metadata: name: default EOF
Finally, a gateway needs to be created. This can be done manually, or by using the existing Ingress configuration and converting one via ingress2gateway. There are a few usage examples in the Kubernetes blog, an example deployment tutorial can found here.
Summary
Ingress-NGINX has been retired finally and a migration to a different controller is necessary. RKE2 is offering a lot of choices and the extended support for Ingress-NGINX until 2028 relieves the pressure to act immediately. Traefik is the SUSE-designated follower, but there are other ways to reach the goal. I tried to describe various migrations to make it easy to try and evaluate the best method for your environment.



