If you run K3s in production, you may use Traefik Ingress for web traffic. It works well for HTTP, but the Gateway API offers clearer routing rules, better TLS handling, and more stable TCP and UDP routes. This guide shows how to migrate Kubernetes Ingress to Gateway API v1.6 on K3s with Cilium.
Migrate from Kubernetes Ingress to Gateway API on a K3s Cluster
Table of Contents
- Why You Should Migrate Kubernetes Ingress to Gateway API
- Architecture for This Migration
- Prerequisites
- Step 1: Save Your Current Ingress Setup
- Step 2: Install Gateway API CRDs
- Step 3: Enable Gateway API in Cilium
- Step 4: Create the HTTP and HTTPS Gateway
- Step 5: Create HTTP Redirect and HTTPS Routes
- Step 6: Create a TCP Gateway
- Step 7: Create the TCPRoute
- Step 8: Create a UDP Gateway
- Step 9: Create the UDPRoute
- Step 10: Test the Gateway Before Changing DNS
- Step 11: Remove the Old Ingress
- Conclusion

Why You Should Migrate Kubernetes Ingress to Gateway API
The classic Ingress resource has served Kubernetes well, but it was not built for multi-protocol or multi-team clusters. Controllers often need their own annotations for TLS redirects, headers, and path rules. That makes it harder to move between Traefik, NGINX, and cloud controllers.
Gateway API separates the work into clear resources:
-
GatewayClass selects the controller that runs a Gateway.
-
Gateway opens ports and sets protocols and TLS.
-
HTTPRoute, TCPRoute, UDPRoute, TLSRoute, and GRPCRoute define routing rules.
-
ReferenceGrant controls safe cross-namespace access to Services and Secrets.
In Gateway API v1.6, TCPRoute and UDPRoute became stable v1 resources. The old v1alpha2 versions are deprecated. If you have old test manifests, convert them to v1 during this migration.
Architecture for This Migration
This guide starts with a standard K3s Ingress setup and ends with Gateway API resources that you can test next to the old Ingress.
Before: Traefik Ingress routes demo.example.com to a Kubernetes Service. It uses a TLS Secret, an HTTP-to-HTTPS redirect annotation, and a header-related Traefik middleware annotation.
After: Cilium runs the Gateway API controller. An HTTP Gateway and HTTPRoute handle web traffic, HTTPS, redirects, and a response header. A separate TCP Gateway exposes a TCP Service, and a separate UDP Gateway exposes a UDP Service.
Cilium is used because it supports Gateway API v1.6.1, including HTTPRoute, TCPRoute, and UDPRoute. K3s includes Traefik, but the bundled Traefik Gateway API support may not provide the stable v1.6 TCP and UDP route support used in this guide.
If you're hosting this yourself instead of using a managed cloud cluster, a high-performance dedicated server gives you steady CPU, network, and system-level access that tools like Cilium need to run Gateway API properly.
Prerequisites
Before you migrate Kubernetes Ingress to Gateway API, confirm the following:
-
A K3s cluster that already runs Cilium, or a new K3s cluster where Cilium will be the CNI.
-
kubectl,helm,curl, andtaron your admin machine. -
A working app Service named
demo-webin thedefaultnamespace, listening on port80. -
A TLS Secret named
demo-web-tlsin thedefaultnamespace. It must include a valid certificate fordemo.example.com. -
A LoadBalancer solution. For self-hosted K3s, use Cilium LB IPAM, MetalLB, or another supported option. The Gateway needs an external address before public DNS can point to it.
-
A TCP Service named
demo-postgreson port5432, if you want to use the TCPRoute example. -
A UDP Service named
demo-corednson port5353, if you want to use the UDPRoute example.
Before you migrate Kubernetes Ingress to Gateway API, check your current app, Service, endpoints, and TLS Secret:
Step 1: Save Your Current Ingress Setup
Before you change anything, save a copy of your current setup so you can compare it later. This backup step is what makes it safe to migrate Kubernetes Ingress to Gateway API without breaking live traffic:
Here is a typical old Ingress file. Create it for reference:
Add:
Do not delete the old Ingress yet. The Gateway API resources will run beside it while you test the migration.
Step 2: Install Gateway API CRDs
Gateway API uses new Kubernetes resource types such as Gateway, HTTPRoute, TCPRoute, and UDPRoute. Kubernetes cannot use these resources until their CRDs are installed.
Create the install script:
Paste this content:
Make the script executable and run it:
Verify the installation:
You should see at least gatewayclasses, gateways, httproutes, tcproutes, udproutes, and referencegrants.
Step 3: Enable Gateway API in Cilium
Cilium is the Gateway API controller in this guide. It reads Gateway API objects and creates the Envoy proxy setup that routes traffic.
This step is only for a cluster that already uses Cilium. Do not run it as a fast way to change a live Flannel-based K3s cluster into a Cilium cluster. Replacing a CNI changes all Pod networking and needs a proper migration plan.
Add the Cilium Helm repository if you do not already have it:
Check the current Cilium installation:
Enable Gateway API in an existing Cilium installation:
Restart Cilium so it reloads the Gateway API feature:
Install the Cilium CLI if needed:
Check Cilium health and confirm that Cilium created the GatewayClass:
The GatewayClass named cilium tells Kubernetes that Cilium owns Gateways that use gatewayClassName: cilium.
If the GatewayClass is missing, check Cilium operator logs:
For Cilium basics, policies, and traffic visibility after the migration, see our Cilium Hubble Kubernetes network policy guide.
Step 4: Create the HTTP and HTTPS Gateway
A Gateway opens ports and defines how traffic enters the cluster. In this step, it listens on port 80 for HTTP and port 443 for HTTPS.
Create the file:
Add:
Apply it:
Check its status:
The HTTP listener receives normal traffic on port 80. The HTTPS listener receives encrypted traffic on port 443 and uses the demo-web-tls Secret to terminate TLS.
Wait until the Gateway shows Accepted=True and Programmed=True. It also needs an external ADDRESS before you can point public DNS to it. If no address appears, configure Cilium LB IPAM, MetalLB, or another LoadBalancer option first.
Step 5: Create HTTP Redirect and HTTPS Routes
An HTTPRoute holds the routing rules. This step creates two routes: one redirects HTTP to HTTPS, and the other sends HTTPS requests to the existing demo-web Service.
Create the file:
Add:
Apply the file:
Check both routes:
The first route replaces the Traefik HTTP-to-HTTPS redirect annotation. The second route sends https://demo.example.com/ traffic to demo-web on port 80.
The X-App-Version: v2 header is just a test. It helps prove that Gateway API filters are working. Do not manually add X-Forwarded-Proto unless your application truly needs a custom value; Cilium Envoy normally handles forwarding headers.
Step 6: Create a TCP Gateway
Ingress cannot route raw TCP services. Gateway API v1.6 adds stable TCPRoute support for PostgreSQL, Redis, MQTT, custom TCP applications, and similar services.
Use a separate Gateway for TCP. This keeps Layer 4 traffic separate from HTTP and HTTPS traffic and avoids controller-specific limits.
Security warning: Do not expose a database to the public internet unless it is required. Prefer private networking, VPN access, strict firewall rules, TLS, and strong database authentication.
Create the TCP Gateway file:
Add:
Apply:
Check with:
This Gateway opens TCP port 5432. It accepts only TCPRoute objects from the same namespace.
Step 7: Create the TCPRoute
The TCPRoute forwards connections from the TCP Gateway to a Kubernetes Service. This example routes PostgreSQL-style traffic to a Service named demo-postgres.
Create the file:
Add:
Apply:
Verify the route:
The parentRefs block attaches the route to the postgres listener on demo-tcp-gateway. The backendRefs block points to the Kubernetes Service that receives the connections.
The demo-postgres Service must already exist and expose TCP port 5432. Change its name and port to match your real Service.
Step 8: Create a UDP Gateway
UDPRoute is useful for UDP services such as DNS, game servers, VoIP, metrics agents, or IoT applications. Gateway API v1.6 makes UDPRoute stable.
Create the UDP Gateway file:
Add:
Apply it:
Verify with:
This Gateway accepts UDP traffic on port 5353. Change this port to the port used by your own UDP service.
Step 9: Create the UDPRoute
The UDPRoute forwards UDP packets from the UDP Gateway to a Kubernetes Service.
Create the file:
Add:
Apply and verify:
This sends UDP traffic from port 5353 to the demo-coredns Service. Replace demo-coredns and port 5353 with the name and port of your own UDP Service.
Never expose the default CoreDNS Service used by your cluster to the public internet.
Step 10: Test the Gateway Before Changing DNS
Test the new Gateway before changing public DNS. This lets you leave the old Traefik Ingress active while you check the new path.
Get the HTTP Gateway address:
Test the HTTP redirect without changing DNS:
You should receive a 301 response and a Location: https://demo.example.com/ header.
Test HTTPS, the certificate, the backend app, and the response header:
Check for:
-
A successful TLS connection. Remove
-konce the certificate is trusted. -
A
200,301, or another expected app response. -
The
X-App-Version: v2response header.
Check all Gateway API resources once more:
Only after the tests pass should you update the public DNS A or AAAA record for demo.example.com to the address of demo-web-gateway.
Step 11: Remove the Old Ingress
Do not remove the old Ingress immediately after you change DNS. First, monitor application logs, access logs, error rates, TLS errors, and normal user traffic.
When the new Gateway API route is stable, delete the old Ingress:
Check whether other workloads still need Traefik:
Only remove Traefik from K3s if no other application depends on it.
Conclusion
You can move from Kubernetes Ingress to Gateway API step by step, without changing everything at once. Install the CRDs, enable a supported controller, and create Gateway API objects next to the old Ingress. Test with curl --resolve, switch DNS, and remove the old Ingress only after validation.
Gateway API v1.6 also gives you stable TCPRoute and UDPRoute support for services that classic Ingress cannot handle. You can run production K3s and Gateway API workloads on a PerLod dedicated server with predictable networking.
We hope you enjoy this guide.
For more details about routing TCP traffic with Gateway API, read the official Gateway API TCP routing docs.