When you expose services through an ASM ingress gateway over HTTPS, the gateway needs a TLS certificate that browsers trust. Instead of manually requesting and renewing certificates, you can automate the entire lifecycle with the Automatic Certificate Management Environment (ACME) protocol.
The following steps walk through using cert-manager with Let's Encrypt to issue a browser-trusted HTTPS certificate for a Service Mesh (ASM) ingress gateway. The same approach works with any ACME-compatible CA, such as Sectigo.
How it works
When you create an ACME Issuer resource in cert-manager, cert-manager generates a private key for secure communication with the ACME CA server. To issue a certificate, the CA must first verify that you own the domain. It does this through challenges.
cert-manager supports two challenge types:
| Challenge type | Verification method | How cert-manager handles it |
|---|---|---|
| HTTP-01 | The CA sends an HTTP request to http://<your-domain>/.well-known/acme-challenge/<token>. | cert-manager creates a temporary Ingress resource to route the request to a solver pod, which responds with the expected key. |
| DNS-01 | The CA looks up a DNS TXT record for your domain. | With the right permissions, cert-manager creates this record automatically through your DNS provider. |
The following steps use HTTP-01 challenges with the Ingress API. ASM also supports the Gateway API for solving challenges.
Make sure your CA supports the ACME protocol before you begin. Any ACME-compatible CA works with this approach.
For the full ACME protocol specification, see RFC 8555.
Prerequisites
A Container Service for Kubernetes (ACK) cluster is added to an ASM instance of V1.16 or later. For more information, see Add a cluster to an ASM instance and Update an ASM instance.
An ingress gateway is deployed with ports 80 and 443 exposed. For more information, see Create an ingress gateway.
An HTTPBin application is deployed in the ACK cluster added to the ASM instance. For more information, see Deploy the HTTPBin application.
cert-manager is installed. For more information, see Install cert-manager in your cluster.
Ingress API access is enabled for the ASM ingress gateway. For more information, see Enable Ingress on the ASM gateway.
Step 1: Prepare a public domain
Point a public domain to the IP address of your ASM ingress gateway. The Let's Encrypt CA needs to reach your domain over the public Internet to complete HTTP-01 challenges.
For DNS configuration instructions, see the documentation of your DNS provider. If you use Alibaba Cloud DNS, see Add DNS records. For more information about Let's Encrypt, see Getting Started.
Step 2: Create an Issuer resource
An Issuer tells cert-manager how to request certificates from a CA. In this step, create an Issuer that connects to the Let's Encrypt production server.
Let's Encrypt enforces strict rate limits on its production server. When testing this workflow for the first time, use the staging server URL https://acme-staging-v02.api.letsencrypt.org/directory instead. Staging certificates are not browser-trusted, but the workflow is identical. Switch to the production URL after you confirm everything works.
Use the kubeconfig file of the ACK cluster on the data plane and create the following Issuer resource:
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-prod-issuer
namespace: istio-system
spec:
acme:
# Email for certificate-related notifications (optional but recommended)
email: '<your-email@example.com>'
# ACME account private key stored in this Secret
privateKeySecretRef:
name: letsencrypt-prod
# Let's Encrypt production server
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
# Use HTTP-01 challenges routed through the Istio Ingress class
- http01:
ingress:
ingressClassName: istioThis Issuer uses an HTTP-01 solver with ingressClassName: istio. When a challenge is triggered, cert-manager creates a temporary Ingress resource with this class, which routes the challenge request through the ASM ingress gateway to a cert-manager solver pod.
cert-manager supports two solver APIs: Ingress API and Gateway API. ASM supports both. This example uses the Ingress API.
Verify that the Issuer is ready:
kubectl -n istio-system get issuer letsencrypt-prod-issuerExpected output:
NAME READY AGE
letsencrypt-prod-issuer True 8m3sIf READY shows False, see Troubleshooting.
Step 3: Issue a certificate
Create a Certificate resource that references the Issuer from Step 2. cert-manager uses this resource to request a certificate from Let's Encrypt for your domain.
Use the kubeconfig file of the ACK cluster on the data plane and create the following Certificate resource:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: istio-ingressgateway-certs
namespace: istio-system
spec:
# Domain names to include in the certificate
dnsNames:
- <your-domain.com>
issuerRef:
group: cert-manager.io
kind: Issuer
name: letsencrypt-prod-issuer
# Secret where the issued certificate and private key are stored
secretName: istio-ingressgateway-certsReplace <your-domain.com> with your actual domain name (for example, example.com).
Verify that the Certificate is ready:
kubectl -n istio-system get certificate istio-ingressgateway-certsExpected output:
NAME READY SECRET AGE
istio-ingressgateway-certs True istio-ingressgateway-certs 59mIf READY shows False after several minutes, see Troubleshooting.
What happens during certificate issuance
After you create the Certificate resource, cert-manager triggers the following sequence:
cert-manager sends a certificate signing request to Let's Encrypt through the Issuer.
Let's Encrypt initiates an HTTP-01 challenge by requesting
http://<your-domain>/.well-known/acme-challenge/<token>.cert-manager creates a temporary Ingress (with
ingressClassName: istio), a Service, and a solver Deployment in theistio-systemnamespace. The ASM ingress gateway routes the challenge request to the solver pod.The solver pod responds with the expected key. Let's Encrypt validates domain ownership and issues the certificate.
cert-manager stores the certificate and private key in the Secret
istio-ingressgateway-certs.cert-manager deletes the temporary Ingress, Service, and Deployment resources.
To confirm that the challenge was completed, check the ASM ingress gateway access logs:
kubectl -n istio-system logs <ingress-gateway-pod-name> | grep letsencrypt | tail -1Replace <ingress-gateway-pod-name> with the name of your ASM ingress gateway pod.
Key indicators of a successful challenge:
response_codeis200.pathstarts with/.well-known/acme-challenge/.upstream_clusterroutes to the cert-manager solver (port8089).user_agentidentifies the Let's Encrypt validation server.
After the certificate is issued, kubectl -n istio-system get ingress does not show the solver Ingress because cert-manager automatically cleans up all solver-related resources.
Step 4: Configure the ASM ingress gateway to use the certificate
Create an Istio Gateway resource
Create an Istio Gateway that binds the issued certificate to port 443 on the ASM ingress gateway. For more information, see Manage Istio gateways.
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: httpbin-https
namespace: default
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- <your-domain.com>
port:
name: https
number: 443
protocol: HTTPS
tls:
# Reference the Secret created by cert-manager
credentialName: istio-ingressgateway-certs
mode: SIMPLEReplace <your-domain.com> with the same domain used in the Certificate resource.
Update the VirtualService
Add the new Gateway to your existing VirtualService so it handles both HTTP and HTTPS traffic. For more information, see Manage virtual services.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: httpbin-vs
namespace: default
spec:
gateways:
- httpbin
- httpbin-https # Add the HTTPS Gateway
hosts:
- '*'
http:
- name: test
route:
- destination:
host: httpbin.default.svc.cluster.local
port:
number: 8000Verify the certificate
Open https://<your-domain.com> in a browser. Click the lock icon in the address bar. The browser displays Connection is secure, confirming that it trusts the Let's Encrypt certificate.

To verify from the command line:
curl -v https://<your-domain.com> 2>&1 | grep -E "SSL certificate|issuer"The output shows Let's Encrypt as the certificate issuer.
Troubleshooting
Issuer stays in not-ready state
Check the Issuer status for error details:
kubectl -n istio-system describe issuer letsencrypt-prod-issuerCommon causes:
| Cause | Solution |
|---|---|
| Network connectivity | The ACK cluster cannot reach the Let's Encrypt server at https://acme-v02.api.letsencrypt.org/directory. Verify outbound Internet access. |
| Invalid email | The email field in the Issuer spec contains an invalid address. |
Certificate stays in not-ready state
Check the Certificate status and related resources:
kubectl -n istio-system describe certificate istio-ingressgateway-certsThen check the associated CertificateRequest and Order:
kubectl -n istio-system get certificaterequest
kubectl -n istio-system get order
kubectl -n istio-system get challengeCommon causes:
| Cause | Solution |
|---|---|
| DNS not propagated | The domain does not resolve to the ASM ingress gateway IP. Verify with dig <your-domain.com> or nslookup <your-domain.com>. |
| Port 80 blocked | HTTP-01 challenges require inbound traffic on port 80. Confirm that the ingress gateway exposes port 80 and that no firewall or security group blocks it. |
| Rate limit exceeded | Let's Encrypt may have rate-limited your account on the production server. Switch to the staging server URL and try again. |
| Ingress class mismatch | The ingressClassName in the Issuer must be istio. Confirm with kubectl get ingressclass. |
Certificate issued but HTTPS does not work
| Check | Command |
|---|---|
| Secret exists | kubectl -n istio-system get secret istio-ingressgateway-certs |
Gateway credentialName matches the Secret name | Verify credentialName: istio-ingressgateway-certs in the Gateway spec. |
| VirtualService includes the HTTPS Gateway | Verify httpbin-https is listed under gateways in the VirtualService spec. |