# 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
```