Last week, I decided to modernize the routing configuration in my k3s homelab. Until then, I had been using Traefik’s custom resource, IngressRoute, to manage traffic routing. While it worked well, I wanted to align my setup with the Kubernetes Gateway API, a more standardized and extensible approach. This post details why I made the switch, the challenges I faced, and how I configured Traefik to use HTTPRoute instead of IngressRoute.

Why Switch from IngressRoute to HTTPRoute?

I have been using Traefik’s IngressRoute in my homelab for a while, and it worked just fine. But after stumbling on a YouTube video about the Gateway API, I got curious. Why? Because it’s clearly becoming the standard way to handle routing in Kubernetes. Instead of relying on Traefik’s custom resources, I figured it made sense to use something backed by the community and likely to stick around.

Learning it means I can reuse the same concepts and configurations in any Kubernetes environment, whether it’s another homelab, a cloud provider, or even a work setup.

Enabling Gateway API Support in Traefik

Since I deploy Traefik with Flux CD, I updated my Helm values.yaml to enable the Kubernetes Gateway API provider—but intentionally left the built-in Gateway creation disabled. Here’s what I changed:

gateway:
  enabled: false

providers:
  kubernetesGateway:
    enabled: true

This way, Traefik is ready to work with the Gateway API, but I can manually define my own Gateway and HTTPRoute resources later. The goal is to learn how the Gateway API works by writing the YAML myself, rather than letting Helm automate everything.

Here is an example:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: traefik-gateway
  namespace: traefik
spec:
  gatewayClassName: traefik
  listeners:
    - name: example-gateway
      port: 8443
      protocol: HTTPS
      hostname: "*.example.com"
      allowedRoutes:
        namespaces:
          from: All
      tls:
        certificateRefs:
        - kind: Secret
          name: example-wildcard-prod
        - kind: Secret
          name: staging-example-wildcard-prod

In a homelab, using from: All is convenient because it allows routes from any namespace. However, in a production environment, you should explicitly list the authorized namespaces to enforce stricter security controls.

Migrate from IngressRoute to HTTPRoute

Let’s compare the configuration for the whoami example application. The HTTPRoute is actually very similar to the IngressRoute I was using before, but with a few key differences.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: whoami
  namespace: traefik
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`whoami.example.com`)
      kind: Rule
      services:
        - name: whoami
          namespace: whoami
          port: 80
  tls:
    secretName: example-wildcard-prod

With HTTPRoute, the TLS configuration is managed at the Gateway level, so there’s no need to repeat it here. The configuration is cleaner and more focused on routing rules:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: whoami
  namespace: whoami
spec:
  parentRefs:
    - name: traefik-gateway
      namespace: traefik
      sectionName: example-gateway # References the listener defined in the Gateway
  hostnames:
    - "whoami.example.com" # Like Traefik, it defaults to "PathPrefix: /"
  rules:
    - backendRefs:
      - name: whoami
        namespace: whoami
        port: 80

Conclusion

Migrating from Traefik’s IngressRoute to the Kubernetes Gateway API’s HTTPRoute was a breeze. Since my homelab routes were simple—no complex rules or advanced matching—the transition was straightforward. The Gateway API’s modular design made the configuration cleaner and more maintainable, while keeping all the flexibility I needed. If you’re using basic routing, it’s definitely worth giving HTTPRoute a try!