Skip to main content

Command Palette

Search for a command to run...

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

Updated
4 min read
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 ClusterIssuer

  • Wire 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:

  1. Generate a private key

  2. Create a CertificateRequest

  3. Create an ACME Order

  4. Create an ACME Challenge

  5. Create a temporary HTTPRoute to serve the HTTP-01 challenge

  6. Wait for Let’s Encrypt validation

  7. Fetch the certificate

  8. Store it in the target Kubernetes secret

  9. Clean up Order, Challenge, and temporary HTTPRoute

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 CertificateRequest

  • completed 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.