Kubernetes Ingress controllers don’t inherently speak HTTP/3; they’re designed for HTTP/1.1 and HTTP/2.

Let’s see this in action. We’ll use the nginx-ingress controller, a common choice, and configure it to serve an application over HTTP/3.

First, ensure you have a Kubernetes cluster running and kubectl configured. We’ll deploy a simple echoserver application.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: echoserver
spec:
  replicas: 1
  selector:
    matchLabels:
      app: echoserver
  template:
    metadata:
      labels:
        app: echoserver
    spec:
      containers:
      - name: echoserver
        image: registry.k8s.io/echoserver:v1.10
        ports:
        - containerPort: 8080
apiVersion: v1
kind: Service
metadata:
  name: echoserver-svc
spec:
  selector:
    app: echoserver
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

Now, the core of it: the Ingress resource. For HTTP/3, we need a controller that explicitly supports it. nginx-ingress (the one from kubernetes/ingress-nginx) does. This requires a specific configuration.

First, we need to enable QUIC (the transport protocol for HTTP/3) on the Ingress controller itself. This is typically done via a ConfigMap.

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-configuration
  namespace: ingress-nginx # or wherever your controller is installed
data:
  use-quic: "true"
  quic-listen-port: "8443" # This is the UDP port QUIC will listen on
  ssl-protocols: "TLSv1.2 TLSv1.3" # Ensure TLS 1.3 is enabled for QUIC

Apply this ConfigMap:

kubectl apply -f nginx-config.yaml

Next, we define the Ingress resource. For HTTP/3, you must use TLS. The Ingress controller will handle the TLS termination and then proxy to your backend service.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: echoserver-ingress
  annotations:
    # This annotation tells nginx-ingress to use the QUIC listener
    nginx.ingress.kubernetes.io/quic-listen-ports: "8443"
spec:
  ingressClassName: nginx # Make sure this matches your ingress controller's class
  tls:
  - hosts:
    - echoserver.example.com # Replace with your actual domain
    secretName: echoserver-tls # A Kubernetes Secret containing your TLS cert and key
  rules:
  - host: echoserver.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: echoserver-svc
            port:
              number: 80

You’ll need to create the echoserver-tls Secret containing your TLS certificate and private key.

kubectl create secret tls echoserver-tls \
  --cert=/path/to/your/tls.crt \
  --key=/path/to/your/tls.key \
  --namespace default # or the namespace of your Ingress

The nginx-ingress controller, when use-quic: "true" is set in its ConfigMap, will start listening on UDP port 8443 (or whatever quic-listen-port is set to) in addition to its standard TCP ports. When a client attempts to connect via HTTP/3 (which is UDP-based), the controller will negotiate the connection. The nginx.ingress.kubernetes.io/quic-listen-ports annotation ensures that the Ingress resource is configured to accept QUIC connections on that specified port.

The controller then terminates the TLS and forwards the request (over HTTP/1.1 or HTTP/2, as QUIC doesn’t carry the HTTP version itself, but rather the HTTP/3 framing) to your echoserver-svc.

To test this, you’ll need a client that supports HTTP/3. Most modern browsers (Chrome, Firefox) do. You can also use curl with the --http3 flag.

# Ensure you have DNS set up for echoserver.example.com to point to your Ingress controller's IP
curl --http3 -v https://echoserver.example.com/

You should see output indicating a successful connection, and potentially messages about the HTTP/3 protocol being used.

The most surprising thing about HTTP/3 is that it’s not an upgrade to HTTP/2 in the same way HTTP/2 was to HTTP/1.1; it’s a complete reimagining of the transport layer, moving from TCP to UDP. This is why it’s often called "HTTP over QUIC."

The mental model here is that your Ingress controller is now a dual-protocol endpoint. It handles both traditional TCP-based HTTP/1.1 and HTTP/2, and UDP-based HTTP/3. The configuration maps the HTTP/3 connection attempts to the appropriate TLS termination and backend routing. The ingressClassName ensures the correct controller picks up the Ingress resource. The tls section is mandatory because QUIC, by design, requires encryption.

What most people don’t realize is that nginx-ingress (and many other HTTP/3 capable ingress controllers) doesn’t actually speak HTTP/3 end-to-end to the backend. It terminates the QUIC/HTTP/3 connection at the ingress controller and then proxies the request to the backend service using HTTP/1.1 or HTTP/2 over TCP. The benefit of HTTP/3 is realized in the connection establishment and transport efficiency between the client and the ingress controller, especially over lossy networks.

The next hurdle you’ll likely face is understanding how to properly configure DNS and firewall rules to allow UDP traffic on the QUIC port to reach your Ingress controller pods.

Want structured learning?

Take the full Http3 course →