Using multiple SSL certificates in HTTPS load balancing with Ingress

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:

multiple SSL certificates with Ingress system diagram

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 thetls field 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:

  1. Create a Deployment.

  2. Create a Service.

  3. Create two certificate files and two key files or twoManagedCertificateobjects. You must configure these certificates in the same project and thesame namespace as where the load balancer is deployed.

  4. Create an Ingress that uses either Secrets or pre-shared certificates.When you create the Ingress, GKE creates and configures anApplication Load Balancer.

  5. 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 thegcloud components update command. 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/zone instead. 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

  1. Save the following manifest asmy-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 runshello-app:1.0 and listens on TCP port 50001.The other container runshello-app:2.0 and listens on TCP port 50002.

  2. Apply the manifest to your cluster:

    kubectlapply-fmy-mc-deployment.yaml

Create a Service

  1. Save the following manifest asmy-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:50002

    This manifest describes a Service with the following fields:

    • selector: specifies that any Pod that has theapp: products label andthedepartment: sales label 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.
  2. 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

  1. Create your first key:

    opensslgenrsa-outtest-ingress-1.key2048
  2. Create your first certificate signing request:

    opensslreq-new-keytest-ingress-1.key-outtest-ingress-1.csr\-subj"/CN=FIRST_DOMAIN"

    ReplaceFIRST_DOMAIN with a domain name that youown, such asexample.com.

  3. Create your first certificate:

    opensslx509-req-days365-intest-ingress-1.csr-signkeytest-ingress-1.key\-outtest-ingress-1.crt
  4. Create your second key:

    opensslgenrsa-outtest-ingress-2.key2048
  5. Create your second certificate signing request:

    opensslreq-new-keytest-ingress-2.key-outtest-ingress-2.csr\-subj"/CN=SECOND_DOMAIN"

    ReplaceSECOND_DOMAIN with another domain namethat you own, such asexamplepetstore.com.

  6. 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.
Note: The words FIRST and SECOND don't designate an order. They help tokeep track of your two domains, your two certificate files, and thecorresponding key files.

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_DOMAIN

Replace the following:

  • FIRST_CERT_NAME: the name of your firstManagedCertificate object.
  • FIRST_DOMAIN: the first domain that you own.
  • SECOND_CERT_NAME: the name of the secondManagedCertificate object.
  • 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

  1. Create a Secret that holds your first certificate and key:

    kubectlcreatesecrettlsFIRST_SECRET_NAME\--cert=FIRST_CERT_FILE\--key=FIRST_KEY_FILE
  2. Create a Secret that holds your second certificate and key:

    kubectlcreatesecrettlsSECOND_SECRET_NAME\--cert=SECOND_CERT_FILE\--key=SECOND_KEY_FILE

Create an Ingress

  1. Save the following manifest asmy-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:60002

    ReplaceFIRST_DOMAIN andSECOND_DOMAINwith domain names that you own, for exampleexample.com andexamplepetstore.com.

  2. Apply the manifest to your cluster:

    kubectlapply-fmy-mc-ingress.yaml
  3. Describe your Ingress:

    kubectldescribeingressmy-mc-ingress

    The 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.1

    The 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

  1. Create a certificate:

    gcloudcomputessl-certificatescreateFIRST_CERT_NAME\--certificate=FIRST_CERT_FILE\--private-key=FIRST_KEY_FILE

    Replace the following:

  2. Create a second certificate:

    gcloudcomputessl-certificatescreateSECOND_CERT_NAME\--certificate=SECOND_CERT_FILE\--private-key=SECOND_KEY_FILE

    Replace the following:

    • SECOND_CERT_NAME: the name of your secondcertificate.
    • SECOND_CERT_FILE: your second certificate file.
    • SECOND_KEY_FILE: your second key file.
  3. View your certificate resources:

    gcloudcomputessl-certificateslist

    The 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

  1. Save the following manifest asmy-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:60002

    ReplaceFIRST_DOMAIN andSECOND_DOMAIN with your domain names.

    This manifest describes an Ingress that lists pre-shared certificateresources in an annotation.

  2. Apply the manifest to your cluster:

    kubectlapply-fmy-psc-ingress.yaml
  3. Describe your Ingress:

    kubectldescribeingressmy-psc-ingress

    The 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.2

    The output shows that the Ingress is associated with pre-sharedcertificates namedFIRST_CERT_NAME andSECOND_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

  1. Save the following manifest asmy-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:60002

    ReplaceFIRST_DOMAIN andSECOND_DOMAIN with your domain names.

    This manifest describes an Ingress that lists pre-shared certificateresources in an annotation.

  2. Apply the manifest to your cluster:

    kubectlapply-fmy-gmc-ingress.yaml
  3. Describe your Ingress:

    kubectldescribeingressmy-gmc-ingress

    The 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.2

    The output shows that the Ingress is associated with managed certificatesnamedFIRST_CERT_NAME andSECOND_CERT_NAME. GKE automaticallypopulates theingress.gcp.kubernetes.io/pre-shared-cert andingress.kubernetes.io/ssl-cert annotations with the Google-managedcertificates that you created using theManagedCertificate objects.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.

  1. Send a request to the load balancer by using your first domain name:

    curl-vhttps://FIRST_DOMAIN

    You might need to use thecurl -k option to perform an insecure SSLtransfer, so thatcurl will 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.

  2. Send a request to the load balancer by using your second domain name:

    curl-vhttps://SECOND_DOMAIN

    The 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.0

    This 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 thetls block, the primary certificate is in the firstSecret in the list.

  • For pre-shared certificates listed in theingress.gcp.kubernetes.io/pre-shared-cert annotation, 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 thenetworking.gke.io/managed-certificates annotation 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 yourManagedCertificate objects accordingly to control the sortorder. For example, to makemy-default-cert the primary overanother-cert, you might name them0-my-default-cert and1-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:

kubectldescribeingress

The 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

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.