Automating TLS for Envoy Gateway Using cert-manager and ACME HTTP-01 (with Let's Encrypt)

With the recent annoncements regarding the Gateway APIs and the end of the Ingress Nginx maintenance, some aspects (historically managed by the Ingress Nginx) need to be re-adapted, we’ll shre one of those today: the TLS certificates management through cert-manager, using an Envoy Gateway API.
Cert-manager now supports ACME HTTP-01 challenges via Gateway API, allowing us to issue and renew Let’s Encrypt certificates without Ingress resources dependance.
In this guide, we will:
Install cert-manager with Gateway API support
Configure an ACME
ClusterIssuerWire cert-manager to Envoy Gateway
Issue a Let’s Encrypt certificate
Inspect the certificate lifecycle (
CertificateRequest,Order,Challenge)Quick bonus: Add HTTP → HTTPS redirection using an
HTTPRoute
Context: what runs in which namespace ?
In order to have a better understanding of our main resources, here’s a small schema to visualize what runs in which namespace:

Installing cert-manager with Gateway API Support
First, we install cert-manager with its CRDs and enable Gateway API integration with the correct config flag:
helm repo add jetstack https://charts.jetstack.io
helm repo update
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.crds.yaml
kubectl create namespace cert-manager
helm upgrade --install cert-manager oci://quay.io/jetstack/charts/cert-manager \
--namespace cert-manager \
--set config.apiVersion="controller.config.cert-manager.io/v1alpha1" \
--set config.kind="ControllerConfiguration" \
--set config.enableGatewayAPI=true
This enables cert-manager to generate HTTPRoute objects to serve ACME HTTP-01 challenges directly through Envoy Gateway.
Configuring the ACME ClusterIssuer (Let’s Encrypt)
To issue certificates, cert-manager requires an issuer. So we need to create a production Let’s Encrypt ClusterIssuer using the Gateway API HTTP solver. We’ve provided some additional informations, as the mail adress and the server used to contct Let’s Encrypt. Additionally, we need to specify which gateway (with the namespace) will be used with the received certificate.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-http01
spec:
acme:
email: your@mail.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-http01
solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- name: gateway1
namespace: test
This instructs cert-manager to dynamically create an HTTPRoute during ACME validation.
Preparing Envoy Gateway to Terminate TLS
Next, as Envoy Gateway needs a TLS listener, we’ll patch the gateway with the TLS listener (if it wasn’t already done before).
spec:
gatewayClassName: gatewayclass1
listeners:
- allowedRoutes:
namespaces:
from: Same # Only routes in "test" namespace can attach
hostname: myhostname.com
name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: eg-https
The Gateway will terminate TLS using the Kubernetes secret eg-https.
Observing Certificate Issuance
When cert-manager begins issuing a certificate, it performs several steps:
Generate a private key
Create a
CertificateRequestCreate an ACME
OrderCreate an ACME
ChallengeCreate a temporary
HTTPRouteto serve the HTTP-01 challengeWait for Let’s Encrypt validation
Fetch the certificate
Store it in the target Kubernetes secret
Clean up
Order,Challenge, and temporaryHTTPRoute
When all those steps are done, we’ll receive the certificate.
Viewing the issued certificate
To get the certificate’s details, we just need to use
kubectl describe certificate eg-https -n test
Example output:
Name: eg-https
Namespace: test
...
Status:
Conditions:
Message: Certificate is up to date and has not expired
Reason: Ready
Status: True
Not After: 2026-02-26T13:44:40Z
Revision: 1
Events:
Normal Issuing Issuing certificate as Secret does not exist
Normal Generated Stored new private key in temporary Secret
Normal Requested Created new CertificateRequest resource "eg-https-1"
Normal Issuing The certificate has been successfully issued
This output proves that cert-manager successfully:
created a key
created a
CertificateRequestcompleted ACME validation
issued the certificate
Inspecting CertificateRequests
kubectl get certificaterequests -n test
Expected output:
NAME APPROVED DENIED READY ISSUER AGE
eg-https-1 True True letsencrypt-http01 2d18h
If we need detailed inspection to show the full ACME validation journey:
kubectl describe certificaterequest eg-https-1 -n test
Additional feature: Enforcing HTTP → HTTPS Redirection
In our example, we use only HTTPS but if a redirection is necessary, Gateway API allows explicit redirect policies using filters.
To force HTTPS globally, we can add a route with the correct filter (request redirect) and refere the gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirect-to-https
namespace: test
spec:
parentRefs:
- name: gateway1
hostnames:
- "yourdomain.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301 # moved permanently
All HTTP requests now receive a 301 Permanent Redirect to their HTTPS equivalent.
Conclusion
This workflow demonstrates how Envoy Gateway + Gateway API + cert-manager can provide letsencrypt certs, in the same way as an it does with Ingress Nginx. Some aspects changed comparing to the “old” way:
No annotations needed
Explicit API resources (
Certificate,ClusterIssuer,HTTPRoute)
As Gateway API continues to be adopted, this integration is becoming a new standard for Kubernetes ingress security.





