# Testing the ListenerSet feature from the master branch ahead of the v1.20.0 release We will be using envoygateway's [gateway-1.5-rc.1 branch](https://github.com/envoyproxy/gateway/pull/8161) as it is the branch that works with ListernSet, and Hemant's [cert-manager branch](https://github.com/cert-manager/cert-manager/pull/8518). We will be using the certificate-shim feature + an HTTP-01 ClusterIssuer. ## Requirements 1. You will need a Kubernetes cluster that can create LoadBalancer Services, for example GKE or EKS. Or you can use Kind with [Chisel](https://hackmd.io/@maelvls/kind-chisel) if you are into this kind of thing (I am). 2. You will need to own a DNS zone. You can learn more in Richard's tutorial: https://cert-manager.io/docs/tutorials/getting-started-aws-letsencrypt/ First, install cert-manager and envoygateway. These are versions that I built with [the correct patches](https://github.com/envoyproxy/gateway/pull/8161): ```bash helm upgrade --install eg oci://ghcr.io/maelvls/gateway-helm --version v1.8.0-dev.0 \ -n envoy-gateway-system \ --create-namespace helm upgrade --install cert-manager oci://ghcr.io/maelvls/cert-manager --version v1.20.0-dev.0 \ -n cert-manager --create-namespace \ --set config.apiVersion="controller.config.cert-manager.io/v1alpha1" \ --set config.kind="ControllerConfiguration" \ --set config.enableGatewayAPI=true \ --set config.enableGatewayAPIListenerSet=true \ --set config.featureGates.ListenerSets=true \ --set crds.enabled=true \ --set crds.keep=false ``` Then, create the resources: ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: eg spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: eg namespace: envoy-gateway-system spec: gatewayClassName: eg listeners: - name: http hostname: dummy protocol: HTTP port: 80 allowedListeners: namespaces: from: All --- kind: ClusterIssuer apiVersion: cert-manager.io/v1 metadata: name: letsencrypt spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: youremail@example.com privateKeySecretRef: name: letsencrypt-acc-key solvers: - http01: gatewayHTTPRoute: {} --- kind: Namespace apiVersion: v1 metadata: name: dev --- kind: ListenerSet apiVersion: gateway.networking.k8s.io/v1 metadata: name: backend namespace: dev annotations: cert-manager.io/cluster-issuer: letsencrypt spec: parentRef: name: eg namespace: envoy-gateway-system listeners: - name: https hostname: example.mael-valais-gcp.jetstacker.net port: 443 protocol: HTTPS tls: mode: Terminate certificateRefs: - name: backend-tls - name: http hostname: example.mael-valais-gcp.jetstacker.net port: 80 protocol: HTTP --- apiVersion: v1 kind: ServiceAccount metadata: name: backend namespace: dev --- apiVersion: apps/v1 kind: Deployment metadata: name: backend namespace: dev spec: replicas: 1 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: containers: - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e imagePullPolicy: IfNotPresent name: backend ports: - name: http containerPort: 3000 env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace --- apiVersion: v1 kind: Service metadata: name: backend namespace: dev spec: ports: - name: http port: 3000 targetPort: 3000 selector: app: backend --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: backend namespace: dev spec: parentRefs: - kind: ListenerSet name: backend namespace: dev hostnames: - example.mael-valais-gcp.jetstacker.net rules: - backendRefs: - kind: Service name: backend port: 3000 weight: 1 matches: - path: type: PathPrefix value: / ``` Once that's done, make sure to update the DNS record of your domain (`example.mael-valais-gcp.jetstacker.net` in the example above) to point to the IP of the LoadBalancer created for the `eg` Gateway. For example, on GCP: ```bash IP=$(kubectl -n envoy-gateway-system get service \ -l gateway.envoyproxy.io/owning-gateway-name=eg \ -o jsonpath='{.items[*].status.loadBalancer.ingress[0].ip}' ) gcloud dns record-sets update --project $PROJECT --zone $DNS_ZONE \ --type=A --ttl=300 example.mael-valais-gcp.jetstacker.net --rrdatas=$IP ``` You should now be able to access `http://example.mael-valais-gcp.jetstacker.net`! ```bash $ kubectl cert-manager status certificate -n dev backend-tls Name: backend-tls Namespace: dev Created at: 2026-02-19T19:17:39+01:00 Conditions: Ready: True, Reason: Ready, Message: Certificate is up to date and has not expired DNS Names: - example.mael-valais-gcp.jetstacker.net Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Issuing 8m32s cert-manager-certificates-trigger Issuing certificate as Secret does not exist Normal Generated 8m31s cert-manager-certificates-key-manager Stored new private key in temporary Secret resource "backend-tls-zrzrw" Normal Requested 8m31s cert-manager-certificates-request-manager Created new CertificateRequest resource "backend-tls-1" Normal Generated 109s cert-manager-certificates-key-manager Stored new private key in temporary Secret resource "backend-tls-ttkc8" Normal Requested 109s cert-manager-certificates-request-manager Created new CertificateRequest resource "backend-tls-2" Normal Issuing 108s (x2 over 2m23s) cert-manager-certificates-issuing The certificate has been successfully issued Issuer: Name: letsencrypt Kind: ClusterIssuer Conditions: Ready: True, Reason: ACMEAccountRegistered, Message: The ACME account was registered with the ACME server Events: <none> Secret: Name: backend-tls Issuer Country: US Issuer Organisation: Let's Encrypt Issuer Common Name: R12 Key Usage: Digital Signature, Key Encipherment Extended Key Usages: Server Authentication Public Key Algorithm: RSA Signature Algorithm: SHA256-RSA Subject Key ID: d16c95dce59f32662f586684353189a35a926dfe Authority Key ID: 00b529f22d8e6f31e89b4cad783efadce90cd1d2 Serial Number: 050de0689f26b56bca367735245c829d525e Events: <none> Not Before: 2026-02-19T18:25:52+01:00 Not After: 2026-05-20T19:25:51+02:00 Renewal Time: 2026-04-20T19:25:51+02:00 No CertificateRequest found for this Certificate ``` ## Building your own images and Helm charts For envoygateway: ```bash git clone https://github.com/envoyproxy/gateway cd gateway gh pr checkout https://github.com/envoyproxy/gateway/pull/8161 make push-multiarch BINS="envoy-gateway" PLATFORMS="linux_amd64 linux_arm64" REGISTRY=ghcr.io/maelvls TAG=v1.8.0-dev.0 make helm-push IMAGE_PULL_POLICY=Always OCI_REGISTRY=oci://ghcr.io/maelvls REGISTRY=ghcr.io/maelvls RELEASE_VERSION=v1.8.0-dev.0 TAG=v1.8.0-dev.0 ``` For cert-manager: ```bash git clone https://github.com/cert-manager/cert-manager cd cert-manager gh pr checkout https://github.com/cert-manager/cert-manager/pull/8518 make ko-images-push KO_PLATFORM=linux/amd64,linux/arm64 KO_REGISTRY=ghcr.io/maelvls VERSION=v1.20.0-dev.0 yq -i '.imageRegistry = "ghcr.io" | .imageNamespace = "maelvls" | (..| select(key=="pullPolicy")) = "Always"' deploy/charts/cert-manager/values.yaml make helm-chart _bin/cert-manager.tgz VERSION=v1.20.0-dev.0 helm push _bin/cert-manager.tgz oci://ghcr.io/maelvls ``` ``` VERSION=v0.0.0-pr$(gh pr view --json number --template '{{.number}}') make ko-images-push KO_PLATFORM=linux/amd64,linux/arm64 KO_REGISTRY=ghcr.io/maelvls/cert-manager VERSION=$VERSION yq -i '(..| select(key=="pullPolicy")) = "Always"' deploy/charts/cert-manager/values.yaml perl -i -pe 's|quay.io/jetstack|ghcr.io/maelvls/cert-manager|g' deploy/charts/cert-manager/values.yaml rm -rf _bin/helm make helm-chart _bin/cert-manager.tgz VERSION=$VERSION helm push _bin/cert-manager.tgz oci://ghcr.io/maelvls/cert-manager ```