Using multiple SSL certificates in HTTPS load balancing with Ingress Stay organized with collections Save and categorize content based on your preferences.
This page shows you how to configure multiple SSL certificates for Ingressresources in Google Kubernetes Engine (GKE) clusters.
Overview
If you want to accept HTTPS requests from your clients, theApplication Load Balancer must have a certificate so it can prove its identity toyour clients. The load balancer must also have a private key to complete theHTTPS handshake.
When the load balancer accepts an HTTPS request from a client, the trafficbetween the client and the load balancer is encrypted using TLS. However, theload balancer terminates the TLS encryption, and forwards the request withoutencryption to the application. When you configure an Application Load BalancerthroughIngress, youcan configure the load balancer to present up to 15 TLS certificates to theclient.
The load balancer uses Server Name Indication (SNI) to determine whichcertificate to present to the client, based on the domain name in the TLShandshake. If the client does not use SNI, or if the client uses a domain namethat does not match the Common Name (CN) in one of the certificates, the loadbalancer uses the first certificate listed in the Ingress.
The following diagram shows the load balancer sending traffic to differentbackends, depending on the domain name used in the request:
You can provide an Application Load Balancer with SSL certificates using thefollowing methods:
Google-managed SSL certificates.Refer to the managed certificates page for information on how to use them.
Google Cloud SSL certificate that you manage yourself. The SSL Certificateuses a pre-shared certificate you upload to your Google Cloud project.
KubernetesSecrets.The Secret holds a certificate and key that you create yourself. You add thename of the Secret to the
tlsfield of your Ingress manifest.
You can use more than one method in the same Ingress. This allows forno-downtime migrations between methods.
The big picture
Here's an overview of the steps in this document:
Create a Deployment.
Create a Service.
Create two certificate files and two key files or two
ManagedCertificateobjects. You must configure these certificates in the same project and thesame namespace as where the load balancer is deployed.Create an Ingress that uses either Secrets or pre-shared certificates.When you create the Ingress, GKE creates and configures anApplication Load Balancer.
Test the Application Load Balancer.
Before you begin
Before you start, make sure that you have performed the following tasks:
- Enable the Google Kubernetes Engine API. Enable Google Kubernetes Engine API
- If you want to use the Google Cloud CLI for this task,install and theninitialize the gcloud CLI. If you previously installed the gcloud CLI, get the latest version by running the
gcloud components updatecommand. Earlier gcloud CLI versions might not support running the commands in this document.Note: For existing gcloud CLI installations, make sure to set thecompute/regionproperty. If you use primarily zonal clusters, set thecompute/zoneinstead. By setting a default location, you can avoid errors in the gcloud CLI like the following:One of [--zone, --region] must be supplied: Please specify location. You might need to specify the location in certain commands if the location of your cluster differs from the default that you set.
- You must own two domain names. The domain names must be no longer than 63characters.
- Ensure that you have an existing Autopilot or Standardcluster. To create a new cluster, seeCreate an Autopilot cluster.
Limitations
Google-managed certificates are only supported withGKE Ingress using the external Application Load Balancer. Google-managedcertificates don't support third-party Ingress controllers.
For internal Application Load Balancers, you mustdisable HTTP in the Ingress manifest.This is not required for the external load balancer.
You must not manually change or update the configuration of theApplication Load Balancer. This means that you must not edit any of theload balancer's components, including target proxies, URL maps, and backendservices. Any changes that you make are overwritten by GKE.
Create a Deployment
Save the following manifest as
my-mc-deployment.yaml:apiVersion:apps/v1kind:Deploymentmetadata:name:my-mc-deploymentspec:selector:matchLabels:app:productsdepartment:salesreplicas:3template:metadata:labels:app:productsdepartment:salesspec:containers:-name:helloimage:"us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0"env:-name:"PORT"value:"50001"-name:hello-againimage:"us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0"env:-name:"PORT"value:"50002"This manifest describes a Deployment with three Pods. Each Pod has twocontainers. One container runs
hello-app:1.0and listens on TCP port 50001.The other container runshello-app:2.0and listens on TCP port 50002.Apply the manifest to your cluster:
kubectlapply-fmy-mc-deployment.yaml
Create a Service
Save the following manifest as
my-mc-service.yaml:apiVersion:v1kind:Servicemetadata:name:my-mc-servicespec:type:NodePortselector:app:productsdepartment:salesports:-name:my-first-portprotocol:TCPport:60001targetPort:50001-name:my-second-portprotocol:TCPport:60002targetPort:50002This manifest describes a Service with the following fields:
selector: specifies that any Pod that has theapp: productslabel andthedepartment: saleslabel is a member of this Service.ports: specifies that when a client sends a request to the Service onmy-first-port, GKE forwards the request to one of themember Pods on port 50001. When a client sends a request to the Service onmy-second-port, GKE forwards the request to one of themember Pods on port 50002.
Apply the manifest to your cluster:
kubectlapply-fmy-mc-service.yaml
Create certificates and keys
To do the exercises on this page, you need two certificates, each with acorresponding key. Each certificate must have a Common Name (CN) that is equalto a domain name that you own.
You can create those certificates manually or use Google-managed certificates.
If you already have two certificate files withthe appropriate values for Common Name, you can skip ahead to the next section.
User-managed certs
Create your first key:
opensslgenrsa-outtest-ingress-1.key2048Create your first certificate signing request:
opensslreq-new-keytest-ingress-1.key-outtest-ingress-1.csr\-subj"/CN=FIRST_DOMAIN"Replace
FIRST_DOMAINwith a domain name that youown, such asexample.com.Create your first certificate:
opensslx509-req-days365-intest-ingress-1.csr-signkeytest-ingress-1.key\-outtest-ingress-1.crtCreate your second key:
opensslgenrsa-outtest-ingress-2.key2048Create your second certificate signing request:
opensslreq-new-keytest-ingress-2.key-outtest-ingress-2.csr\-subj"/CN=SECOND_DOMAIN"Replace
SECOND_DOMAINwith another domain namethat you own, such asexamplepetstore.com.Create your second certificate:
opensslx509-req-days365-intest-ingress-2.csr-signkeytest-ingress-2.key\-outtest-ingress-2.crt
For more information about certificates and keys, see theSSL certificates overview.
You now have two certificate files and two key files.
The remaining tasks use the following placeholders to refer to your domains,certificates, and keys:
FIRST_CERT_FILE: the path to your first certificatefile.FIRST_KEY_FILE: the path to the key file that goeswith your first certificate.FIRST_DOMAIN: a domain name that you own.FIRST_SECRET_NAME: the name of the Secretcontaining your first certificate and key.SECOND_CERT_FILE: the path to your secondcertificate file.SECOND_KEY_FILE: the path to the key file thatgoes with your second certificate.SECOND_DOMAIN: a second domain name that you own.SECOND_SECRET_NAME: the name of the Secretcontaining your second certificate and key.
Google-managed certs
To create Google-managed certificates, you must addManagedCertificateobjects to the namespace of your Ingress. You canuse the following template to define certificates for your domains:
apiVersion:networking.gke.io/v1kind:ManagedCertificatemetadata:name:FIRST_CERT_NAMEspec:domains:-FIRST_DOMAIN---apiVersion:networking.gke.io/v1kind:ManagedCertificatemetadata:name:SECOND_CERT_NAMEspec:domains:-SECOND_DOMAINReplace the following:
FIRST_CERT_NAME: the name of your firstManagedCertificateobject.FIRST_DOMAIN: the first domain that you own.SECOND_CERT_NAME: the name of the secondManagedCertificateobject.SECOND_DOMAIN: the second domain that you own.
The names of theManagedCertificate objects are different from the names ofthe actual certificates that they create. You only need to know the names oftheManagedCertificate objects to use them in your Ingress.
Specify certificates for your Ingress
The next step is to create an Ingress object. In your Ingress manifest, you canuse one of the following methods to provide certificates for the load balancer:
- Secrets
- Pre-shared certificates
- Google-managed certificates
Secrets
Create a Secret that holds your first certificate and key:
kubectlcreatesecrettlsFIRST_SECRET_NAME\--cert=FIRST_CERT_FILE\--key=FIRST_KEY_FILECreate a Secret that holds your second certificate and key:
kubectlcreatesecrettlsSECOND_SECRET_NAME\--cert=SECOND_CERT_FILE\--key=SECOND_KEY_FILE
Create an Ingress
Save the following manifest as
my-mc-ingress.yaml:apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:my-mc-ingressspec:tls:-secretName:FIRST_SECRET_NAME-secretName:SECOND_SECRET_NAMErules:-host:FIRST_DOMAINhttp:paths:-pathType:ImplementationSpecificbackend:service:name:my-mc-serviceport:number:60001-host:SECOND_DOMAINhttp:paths:-pathType:ImplementationSpecificbackend:service:name:my-mc-serviceport:number:60002Replace
FIRST_DOMAINandSECOND_DOMAINwith domain names that you own, for exampleexample.comandexamplepetstore.com.Apply the manifest to your cluster:
kubectlapply-fmy-mc-ingress.yamlDescribe your Ingress:
kubectldescribeingressmy-mc-ingressThe output is similar to the following:
Name: my-mc-ingressAddress:203.0.113.1...TLS:FIRST_SECRET_NAME terminatesSECOND_SECRET_NAME terminatesRules: Host Path Backends ---- ---- --------FIRST_DOMAIN my-mc-service:my-first-port (<none>)SECOND_DOMAIN my-mc-service:my-second-port (<none>)Annotations:...Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ADD 3m loadbalancer-controller default/my-mc-ingress Normal CREATE 2m loadbalancer-controller ip: 203.0.113.1The output shows that two Secrets are associated with the Ingress. Theoutput also shows the external IP address of the load balancer. If theexternal IP address is not set, wait a few minutes and try the commandagain.
Pre-shared certs
Create a certificate:
gcloudcomputessl-certificatescreateFIRST_CERT_NAME\--certificate=FIRST_CERT_FILE\--private-key=FIRST_KEY_FILEReplace the following:
FIRST_CERT_NAME: the name of your firstcertificate.FIRST_CERT_FILE: yourfirst certificate file.FIRST_KEY_FILE: your first key file.
Create a second certificate:
gcloudcomputessl-certificatescreateSECOND_CERT_NAME\--certificate=SECOND_CERT_FILE\--private-key=SECOND_KEY_FILEReplace the following:
SECOND_CERT_NAME: the name of your secondcertificate.SECOND_CERT_FILE: your second certificate file.SECOND_KEY_FILE: your second key file.
View your certificate resources:
gcloudcomputessl-certificateslistThe output is similar to the following:
NAME CREATION_TIMESTAMPFIRST_CERT_NAME 2018-11-03T12:08:47.751-07:00SECOND_CERT_NAME 2018-11-03T12:09:25.359-07:00
Create an Ingress
Save the following manifest as
my-psc-ingress.yaml:apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:my-psc-ingressannotations:ingress.gcp.kubernetes.io/pre-shared-cert:"FIRST_CERT_NAME,SECOND_CERT_NAME"spec:rules:-host:FIRST_DOMAINhttp:paths:-pathType:ImplementationSpecificbackend:service:name:my-mc-serviceport:number:60001-host:SECOND_DOMAINhttp:paths:-pathType:ImplementationSpecificbackend:service:name:my-mc-serviceport:number:60002Replace
FIRST_DOMAINandSECOND_DOMAINwith your domain names.This manifest describes an Ingress that lists pre-shared certificateresources in an annotation.
Apply the manifest to your cluster:
kubectlapply-fmy-psc-ingress.yamlDescribe your Ingress:
kubectldescribeingressmy-psc-ingressThe output is similar to the following:
Name: my-psc-ingressAddress:203.0.113.2...Rules: Host Path Backends ---- ---- --------FIRST_DOMAIN my-mc-service:my-first-port (<none>)SECOND_DOMAIN my-mc-service:my-second-port (<none>)Annotations: ... ingress.gcp.kubernetes.io/pre-shared-cert:FIRST_CERT_NAME,SECOND_CERT_NAME ... ingress.kubernetes.io/ssl-cert:FIRST_CERT_NAME,SECOND_CERT_NAMEEvents: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ADD 2m loadbalancer-controller default/my-psc-ingress Normal CREATE 1m loadbalancer-controller ip: 203.0.113.2The output shows that the Ingress is associated with pre-sharedcertificates named
FIRST_CERT_NAMEandSECOND_CERT_NAME. The output also shows theexternal IP address of the load balancer. If the external IP address isnot set, wait a few minutes and try the command again.
Google-managed certs
Create an Ingress
Save the following manifest as
my-gmc-ingress.yaml:apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:my-gmc-ingressannotations:networking.gke.io/managed-certificates:"FIRST_CERT_NAME,SECOND_CERT_NAME"spec:rules:-host:FIRST_DOMAINhttp:paths:-pathType:ImplementationSpecificbackend:service:name:my-mc-serviceport:number:60001-host:SECOND_DOMAINhttp:paths:-pathType:ImplementationSpecificbackend:service:name:my-mc-serviceport:number:60002Replace
FIRST_DOMAINandSECOND_DOMAINwith your domain names.This manifest describes an Ingress that lists pre-shared certificateresources in an annotation.
Apply the manifest to your cluster:
kubectlapply-fmy-gmc-ingress.yamlDescribe your Ingress:
kubectldescribeingressmy-gmc-ingressThe output is similar to the following:
Name: my-gmc-ingressAddress:203.0.113.2...Rules: Host Path Backends ---- ---- --------FIRST_DOMAIN my-mc-service:my-first-port (<none>)SECOND_DOMAIN my-mc-service:my-second-port (<none>)Annotations: ... ingress.gcp.kubernetes.io/pre-shared-cert: mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4 ... ingress.kubernetes.io/ssl-cert: mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4 networking.gke.io/managed-certificates:FIRST_CERT_NAME,SECOND_CERT_NAMEEvents: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ADD 2m loadbalancer-controller default/my-gmc-ingress Normal CREATE 1m loadbalancer-controller ip: 203.0.113.2The output shows that the Ingress is associated with managed certificatesnamed
FIRST_CERT_NAMEandSECOND_CERT_NAME. GKE automaticallypopulates theingress.gcp.kubernetes.io/pre-shared-certandingress.kubernetes.io/ssl-certannotations with the Google-managedcertificates that you created using theManagedCertificateobjects.The output also shows the external IP address of the loadbalancer. If the external IP address is not set, wait a few minutes andtry the command again.
Test the load balancer
Wait about five minutes for GKE to finish configuring theload balancer.
If you used Google-managed certificates, it might takeconsiderably longer to complete the configuration, as the system needs toprovision the certificates and verify the DNS configuration for given domains.
To test the load balancer, you must own two domain names, and both of yourdomain names must resolve the external IP address of the external Application Load Balancer.
Send a request to the load balancer by using your first domain name:
curl-vhttps://FIRST_DOMAINYou might need to use the
curl -koption to perform an insecure SSLtransfer, so thatcurlwill accept self-signed certificates.The output is similar to the following:
...* Trying 203.0.113.1......* Connected toFIRST_DOMAIN (203.0.113.1) port 443 (#0)...* TLSv1.2 (IN), TLS handshake, Certificate (11):...* Server certificate:* subject: CN=FIRST_DOMAIN...> Host:FIRST_DOMAIN.com...Hello, world!Version: 1.0.0...This output shows that your first certificate was used in the TLS handshake.
Send a request to the load balancer by using your second domain name:
curl-vhttps://SECOND_DOMAINThe output is similar to the following:
...* Trying 203.0.113.1......* Connected toSECOND_DOMAIN (203.0.113.1) port 443 (#0)...* Server certificate:* subject: CN=SECOND_DOMAIN...> Host:SECOND_DOMAIN...Hello, world!Version: 2.0.0This output shows that your second certificate was used in the TLShandshake.
The hosts field of an Ingress object
AnIngressSpechas atls field that is an array ofIngressTLSobjects. EachIngressTLS object has ahosts field andSecretName field.In GKE, thehosts field is not used. GKEreads the Common Name (CN) from the certificate in the Secret. If the CommonName matches the domain name in a client request, then the load balancerpresents the matching certificate to the client.
Which certificate is presented?
The load balancerchooses a certificateaccording to these rules:
If both Secrets and pre-shared certificates are listed in the Ingress, thepre-shared certificates take priority over Secrets. In other words, Secretsare still included but pre-shared certificates are presented first.
If no certificate has a Common Name (CN) that matches the domain name in theclient request, the load balancer presents the primary certificate.
For Secrets listed in the
tlsblock, the primary certificate is in the firstSecret in the list.For pre-shared certificates listed in the
ingress.gcp.kubernetes.io/pre-shared-certannotation, the order in which youlist the certificates determines the primary certificate. The primarycertificate, which is presented when no other certificate matches the client'srequest, is the first certificate listed in the annotation.When you use Google-managed certificates, all certificates listed in the
networking.gke.io/managed-certificatesannotation are automatically sortedalphabetically by name. The primary certificate is the one that comes firstin this alphabetical list. To set a specific certificate as the primary, youmust name yourManagedCertificateobjects accordingly to control the sortorder. For example, to makemy-default-certthe primary overanother-cert, you might name them0-my-default-certand1-another-cert.
Certificate rotation best practices
If you want to rotate the contents of your Secret or pre-shared certificate,here are some best practices:
- Create a new Secret or pre-shared certificate with a different name thatcontains the new certificate data. Attach this resource (along with the existingone) to your Ingress using instructions provided earlier. Once satisfied withthe changes, you can remove the old cert from the Ingress.
- If you don't mind disrupting traffic, you can remove the old resource fromthe Ingress, provision a new resource with the same name but different contentsand then reattach it to the Ingress.
To avoid managing certificate rotation yourself, seeUse Google-managed SSL certificates.
Troubleshooting
Specifying invalid or non-existent Secrets results in a Kubernetes event error.You can check Kubernetes events for an Ingress as follows:
kubectldescribeingressThe output is similar to the following:
Name: my-ingressNamespace: defaultAddress: 203.0.113.3Default backend: hello-server:8080 (10.8.0.3:8080)TLS: my-faulty-Secret terminatesRules: Host Path Backends ---- ---- -------- * * my-service:443 (10.8.0.3:443)Events: Error during sync: cannot get certs for Ingress default/my-ingress: Secret "my-faulty-ingress" has no 'tls.crt'What's next
Read theGKE network overview.
Learn how toconfigure domain names with static IP addresses.
Learn how touse Google-managed SSL certificates.
If you have an application running on multiple GKE clustersin different regions, configure aMulti Cluster Ingressto route traffic to a cluster in the region closest to the user.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-12-15 UTC.