helm upgrade --install kong-operator kong/kong-operator -n kong-system \
--create-namespace \
--set image.tag=2.2 \
--set env.ENABLE_CONTROLLER_KONNECT=true \
--set global.webhooks.options.certManager.enabled=trueAutomate TLS certificate provisioning and rotation with cert-manager
Annotate your Gateway with cert-manager.io/issuer and reference the resulting Secret in your Gateway listeners.
Label the Secret with konghq.com/secret: "true" so Kong Operator watches it.
Listener certificates become Certificate and SNI entities, so cert-manager renewals are served without a reload or a pod restart.
Prerequisites
Install cert-manager
Install cert-manager in your cluster to issue and rotate certificates automatically:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm upgrade --install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true
kubectl wait -n cert-manager --for=condition=ready pod --all --timeout=90sKong Konnect
If you don’t have a Konnect account, you can get started quickly with our onboarding wizard.
- The following Konnect items are required to complete this tutorial:
- Personal access token (PAT): Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.
-
Set the personal access token as an environment variable:
export KONNECT_TOKEN='YOUR KONNECT TOKEN'
Kong Operator running (with an Enterprise license)
-
Add the Kong Helm charts:
helm repo add kong https://charts.konghq.com helm repo update -
Install Kong Operator using Helm:
helm upgrade --install kong-operator kong/kong-operator -n kong-system \ --create-namespace \ --set image.tag=2.2 \ --set global.webhooks.options.certManager.enabled=trueKong Operator needs a certificate authority to sign the certificate for mTLS communication between the control plane and the data plane. This is handled automatically by the Helm chart. If you need to provide a custom CA certificate, refer to the
certificateAuthoritysection in thevalues.yamlof the Helm chart to learn how to create and reference your own CA certificate.
Apply a KongLicense. This assumes that your license is available in ./license.json
echo "
apiVersion: configuration.konghq.com/v1alpha1
kind: KongLicense
metadata:
name: kong-license
rawLicenseString: '$(cat ./license.json)'
" | kubectl apply -f -Create a KonnectAPIAuthConfiguration resource
kubectl create namespace kong --dry-run=client -o yaml | kubectl apply -f -
echo '
kind: KonnectAPIAuthConfiguration
apiVersion: konnect.konghq.com/v1alpha1
metadata:
name: konnect-api-auth
namespace: kong
spec:
type: token
token: "'$KONNECT_TOKEN'"
serverURL: us.api.konghq.com
' | kubectl apply -f -Integrating Kong Operator with cert-manager allows you to automatically provision and rotate TLS certificates for your Gateway listeners. This integration follows the standard Kubernetes Gateway API pattern.
When you annotate a Gateway resource with a cert-manager issuer, cert-manager automatically creates a Certificate and a corresponding Secret containing the TLS key pair. The Operator then configures the managed data planes to use this secret for TLS termination.
Renewals are handled the same way. When cert-manager writes a new key pair to the Secret, Kong Operator reconciles it and the data planes start serving the new certificate without a reload and without a pod restart. For more information, see When certificates are loaded and reloaded.
Create a cert-manager issuer
The Issuer resource represents the certificate authority that signs your certificates. Create a self-signed issuer in the kong namespace:
echo '
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: kong
spec:
selfSigned: {}' | kubectl apply -f -A self-signed issuer keeps this guide self-contained. In production, use an ACME issuer such as Let’s Encrypt, or a CA issuer. For all issuer types, see the cert-manager configuration documentation.
Configure the Gateway with cert-manager
Create the following resources:
- A
GatewayConfigurationand aGatewayClassto configure the gateway with the latest Kong Gateway version and Kong Operator as the controller. - A
Gatewaywith thecert-manager.io/issuer: "selfsigned-issuer"annotation and thetls.certificateRefspointing to the name of the Secret to provision. - A
Certificatethat references the cert-manager issuer and the provisioned Secret.
-
Create the
GatewayConfiguration:Set
spec.konnect.authRefto theKonnectAPIAuthConfigurationyou created in the prerequisites. This attaches the data planes to Konnect, which delivers the Kong Gateway license to them:echo ' apiVersion: gateway-operator.konghq.com/v2beta1 kind: GatewayConfiguration metadata: name: kong-gateway-configuration namespace: kong spec: konnect: authRef: name: konnect-api-auth dataPlaneOptions: deployment: podTemplateSpec: spec: containers: - image: kong/kong-gateway:3.15 name: proxy' | kubectl apply -f -echo ' apiVersion: gateway-operator.konghq.com/v2beta1 kind: GatewayConfiguration metadata: name: kong-gateway-configuration namespace: kong spec: dataPlaneOptions: deployment: podTemplateSpec: spec: containers: - image: kong/kong-gateway:3.15 name: proxy' | kubectl apply -f - -
Create the
GatewayClass,Gateway, andCertificate:echo ' apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: kong-cert-manager spec: controllerName: konghq.com/gateway-operator parametersRef: group: gateway-operator.konghq.com kind: GatewayConfiguration name: kong-gateway-configuration namespace: kong --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: kong-gateway namespace: kong annotations: cert-manager.io/issuer: "selfsigned-issuer" spec: gatewayClassName: kong-cert-manager listeners: - name: https port: 443 protocol: HTTPS hostname: example.localdomain.dev tls: mode: Terminate certificateRefs: - group: "" kind: Secret name: example-tls-secret --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: example-tls-certificate namespace: kong spec: secretName: example-tls-secret issuerRef: name: selfsigned-issuer kind: Issuer dnsNames: - example.localdomain.dev secretTemplate: labels: konghq.com/secret: "true"' | kubectl apply -f -
Create an echo Service
-
Create a sample echo Service:
kubectl apply -f https://developer.konghq.com/manifests/kic/echo-service.yaml -n kong -
Wait for it to be ready:
kubectl wait --for=condition=ready pod -l app=echo -n kong --timeout=120s
Create a Route
Deploy a sample HTTPRoute to verify that TLS termination is working:
echo '
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: echo-route
namespace: kong
spec:
parentRefs:
- name: kong-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /echo
backendRefs:
- name: echo
kind: Service
port: 1027' | kubectl apply -f -Validate
Confirm that the certificate is provisioned and served, then that a renewal reaches the data planes without restarting them.
-
Check that cert-manager has created the
Certificateresource and that theSecrethas been provisioned:kubectl get certificate -n kong kubectl get secret example-tls-secret -n kong -
Wait for the Gateway to be programmed, so that it has an address to connect to:
kubectl wait --for=condition=Programmed gateway/kong-gateway -n kong --timeout=180s -
Get the Gateway’s external IP:
export PROXY_IP=$(kubectl get gateway kong-gateway -n kong -o jsonpath='{.status.addresses[0].value}') echo $PROXY_IP -
Test the connection:
curl -ivk --resolve example.localdomain.dev:443:$PROXY_IP https://example.localdomain.dev/echoYou should get a TLS handshake and a 200 response.
-
Check the serial number and validity dates of the certificate Kong Gateway is currently serving, so we can compare after a renewal:
echo "" | openssl s_client -connect $PROXY_IP:443 -servername example.localdomain.dev 2>/dev/null \ | openssl x509 -noout -serial -dates -ext subjectAltNameThe output should look like this:
serial=5B2C9A1E7F04D8B3A6E1C0F29D3847BA notBefore=Aug 7 10:12:04 2026 GMT notAfter=Nov 5 10:12:04 2026 GMT X509v3 Subject Alternative Name: critical DNS:example.localdomain.dev -
Check how long the data plane pods have been running, so we can compare afterwards:
kubectl get pods -n kong -
Force cert-manager to issue a new certificate ahead of schedule by deleting the Secret. cert-manager detects that the
Certificateno longer has a valid Secret and reissues it immediately:kubectl delete secret example-tls-secret -n kong kubectl wait --for=condition=Ready certificate/example-tls-certificate -n kong --timeout=90s -
Query the proxy again. The serial number and the validity dates should have changed:
echo "" | openssl s_client -connect $PROXY_IP:443 -servername example.localdomain.dev 2>/dev/null \ | openssl x509 -noout -serial -dates -ext subjectAltName -
Confirm that no data plane pod restarted:
kubectl get pods -n kongThe value of the
RESTARTScolumn should still be0. -
Confirm that traffic was never interrupted:
curl -sk --resolve example.localdomain.dev:443:$PROXY_IP https://example.localdomain.dev/echo -o /dev/null -w "%{http_code}\n"The response should be
200.
Cleanup
Uninstall cert-manager
helm uninstall cert-manager -n cert-manager
kubectl delete namespace cert-managerUninstall Kong Operator
helm uninstall kong-operator -n kong-system
kubectl delete namespace kong kong-systemFAQs
How do I check whether my deployment uses certificates that don’t rotate this way?
Certificates set through kong.conf are read once at startup, so they don’t follow the rotation described here. Check whether your GatewayConfiguration overrides any of them:
kubectl get gatewayconfiguration kong-gateway-configuration -n kong -o yaml | grep -i "ssl_cert\|cluster_cert"If the command returns nothing, every certificate in your deployment rotates automatically. If it returns a proxy certificate, remove the ssl_cert override and serve the certificate from a Gateway listener as shown in this guide.
Kong Operator provisions and rotates the clustering and Admin API certificates for the data planes it manages, so you don’t need to handle those yourself.
For the full list of parameters that behave this way and why, see When certificates are loaded and reloaded.