Secure Ingress with TLS Using cert-manager
Introduction
In this guide, you will learn how to automatically secure your Kubernetes Ingress resources with TLS certificates using cert-manager and Let's Encrypt.
What is cert-manager?
cert-manager is a powerful Kubernetes add-on that automates the management of TLS certificates. It can request, renew, and manage certificates from a variety of sources — including public certificate authorities like Let's Encrypt. It helps you keep your services secure without manual intervention.
cert-manager introduces several Custom Resource Definitions (CRDs) to automate TLS certificate management:
- Certificate: Defines the desired certificate, including the domain names, the secret to store it in, and which issuer to use.
- CertificateRequest: Automatically created from a Certificate resource. Contains the certificate signing request (CSR).
- Issuer / ClusterIssuer: Define how and from where the certificate should be requested. An Issuer works in a single namespace, while a ClusterIssuer can be used cluster-wide.
- Order: Automatically created when using Let's Encrypt. Represents a certificate order and is handled internally by cert-manager.
- Challenge: Automatically created to verify domain ownership. cert-manager manages the challenge-response process for you.
In addition to these CRDs, cert-manager interacts with standard Kubernetes Secret resources:
- Secret: The certificate and private key are stored here, making them available for use by Ingress controllers or other workloads.
These resources form a chain of automation. Once a Certificate resource is defined, cert-manager:
- Creates a CertificateRequest.
- Uses the Issuer to fulfill the request.
- Solves challenges via Order and Challenge.
- Stores the final certificate in a Kubernetes Secret.
What is Let's Encrypt?
Let's Encrypt is a free, automated, and trusted certificate authority (CA). It allows anyone to get valid TLS certificates for their domains — without needing to pay or do complex manual setup. Certificates issued by Let's Encrypt are trusted by all major browsers and operating systems.
What Will You Do?
-
Install cert-manager using Helm.
-
Deploy a ClusterIssuer resource that uses Let's Encrypt.
-
Automatically issue and renew TLS certificates for your Kubernetes Ingress resources.
Step 0: Prerequisites
Helm
Ensure that Helm is installed locally. If it isn't, follow the official Helm installation guide.
Ingress Resources
This guide assumes you have followed the Deploy Ingress NGINX Controller guide.
Make sure you have:
-
Deployed the Ingress NGINX controller using Helm.
-
Deployed backend services.
-
Created corresponding Ingress resources for those services.
A similar setup will also work, but may require adapting commands and values.
DNS Records
Let's Encrypt uses DNS and HTTP challenges to validate domain ownership. For the HTTP-01 challenge used in this guide, ensure that each domain name has a valid A-record pointing to the external IP address of your Ingress controller service.
Download Templates
Download the following templates and store them in a dedicated folder:
Or copy and paste them from below:
Step 1: Deploy cert-manager
cert-manager provides a Helm chart for easy, out-of-the-box installation.
To install the cert-manager Helm chart, run the following command:
helm upgrade --install \
cert-manager cert-manager \
--repo https://charts.jetstack.io \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true
- Download the chart from the Jetstack Helm repository.
- Install cert-manager in the
cert-manager
namespace. - Create the
cert-manager
namespace if it does not exist yet. - Install the latest stable version available at the time you run it.
- Install the
CustomResourceDefinitions
provided and required by cert-manager.
Important
You can find a full list of available Helm values on cert-manager's ArtifactHub page.
Example output
Step 2: Configure a Let's Encrypt ClusterIssuer
Now that cert-manager is up and running, you can set up a ClusterIssuer in order to generate signed certificates.
In this guide, you will set up both a staging and production Let's Encrypt ClusterIssuer. The production issuer has rate limits, and therefore the staging issuer should be used for experimenting and learning. As soon as you're confident that the issuer is working well, you can switch to the production issuer.
In the templates provided in Download Templates (staging_issuer.yaml
, production_issuer.yaml
), you can optionally provide an email address. The email address might be used by Let's Encrypt for important account-related updates.
Info
In this guide, we use ClusterIssuer, which is a cluster-scoped resource and must not specify a namespace
field in its metadata.
If you prefer to use an Issuer instead (which is namespaced), you must:
- Change the
kind
field fromClusterIssuer
toIssuer
. - Add a
namespace
field undermetadata
, specifying the namespace where the Issuer will be available.
Apply the staging issuer:
staging_issuer.yaml explained
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
# ACME server URL
# For prod: https://acme-v02.api.letsencrypt.org/directory
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Email used for ACME registration
email: user@example.com
# The name of a Kubernetes Secret resource that will be used to
# store the automatically generated ACME account private key
privateKeySecretRef:
name: letsencrypt-staging
solvers:
# Configures cert-manager to attempt to complete authorizations by performing
# the HTTP01 challenge flow (see https://letsencrypt.org/docs/challenge-types/#http-01-challenge)
- http01:
ingress:
class: nginx
When cert-manager uses a ClusterIssuer for the first time, it automatically registers an ACME account with Let's Encrypt. This account identifies your cluster and is associated with the private key stored in the privateKeySecretRef
Secret.
If the referenced Secret already exists, cert-manager will reuse the existing ACME account. If the Secret does not exist yet, cert-manager generates a new private key, registers a new ACME account, and stores it in the Secret.
Info
One ACME account can issue certificates for many different domains and subdomains. It is independent of any specific certificate.
Warning
If the Secret referenced in privateKeySecretRef
already exists, cert-manager reuses the existing ACME account. Changing the email
field in the ClusterIssuer will have no effect — Let's Encrypt continues using the email address associated with the existing ACME account. To use a new email address, you must create a new Secret and register a new ACME account.
Now, repeat this process (enter your email address and apply production_issuer.yaml
) for the production ClusterIssuer:
Step 3: Issue Staging and Production Let's Encrypt Certificates
Now that the ClusterIssuer is created for both staging and production, you can modify the Ingress resources deployed previously in order to enable TLS encryption.
Using the ingress.yaml
template, make the following changes for each ingress resource:
...
metadata:
name: cheddar
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-staging"
spec:
tls:
- hosts:
- cheddar.example.com
secretName: cheddar-tls
ingressClassName: nginx
rules:
...
- In Lines 5 and 6, you tell cert-manager to use the
letsencrypt-staging
ClusterIssuer that you deployed in Step 2. - In the
tls
block on Line 8, you specify the hosts for which you want to generate certificates. In addition, a name is defined for the secret that will contain the TLS private key.
Warning
Be sure to swap out the example domain with your own.
Apply the changes to the existing Ingress resources (or deploy them from anew):
To track the state of the changes you just applied to the Ingress resources, run:
For each Ingress deployed, you should see that Certificates are created:
Example output
Info
Kubernetes events are ephemeral and often expire within an hour. If you run the command some time after applying your resources the Events section may be empty — this is normal and does not indicate a problem.
To confirm that the certificates have been successfully issued, run:
This command shows detailed information about the Certificate resource, including its current status, validity period, and associated events.
Example output
...
Status:
Conditions:
Last Transition Time: 2025-04-23T09:13:56Z
Message: Certificate is up to date and has not expired
Observed Generation: 2
Reason: Ready
Status: True
Type: Ready
Not After: 2025-07-22T08:15:21Z
Not Before: 2025-04-23T08:15:22Z
Renewal Time: 2025-06-22T08:15:21Z
Revision: 1
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Generated 7m43s cert-manager-certificates-key-manager Stored new private key in temporary Secret resource "cheddar-tls-f6m5k"
Normal Requested 7m43s cert-manager-certificates-request-manager Created new CertificateRequest resource "cheddar-tls-7x5hq"
Normal Issuing 6m51s cert-manager-certificates-issuing The certificate has been successfully issued
HTTPS encryption is now active for the domains configured. You are ready to send a request to one of the backend services in order to test that HTTPS is working correctly.
Run the following command to send a request to the cheddar
backend and print the response in your terminal:
You should see the following output:
Example output
This confirms HTTP requests are correctly redirected to HTTPS, and that a TLS certificate is being served. The warning about an untrusted certificate is expected when using Let's Encrypt's staging environment, and it will disappear once you're using a valid certificate from Let's Encrypt's live environment.
To fix this, you can now roll out production certificates.
To do so, update the ClusterIssuer name in all your ingress resources like in the example below:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cheddar
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
...
And apply the changes:
It might take a few minutes for the Let's Encrypt production server to issue the certificate. You can track the progress on the certificate object:
The certificate has been successfully issued if you see the following output messages:
Example output
Normal Issuing 28s cert-manager Issuing certificate as Secret was previously issued by ClusterIssuer.cert-manager.io/letsencrypt-staging
Normal Reused 84s cert-manager-certificates-key-manager Reusing private key stored in existing Secret resource "cheddar-tls"
Normal Requested 84s cert-manager-certificates-request-manager Created new CertificateRequest resource "cheddar-tls-zj2hc"
Normal Issuing 53s cert-manager-certificates-issuing The certificate has been successfully issued
Once again, you can send a request to the cheddar
backend:
Example output
This indicates that HTTP requests are being redirected to HTTPS. Navigate to one of your domains (i.e. cheddar.example.com
) in a browser or run curl https://cheddar.example.com
. You should see your backend service running as expected. The padlock on the left of the address bar will confirm that your connection is secure.
Alternatively, run curl -v https://cheddar.example.com
in a terminal. The verbose output will display the successful certificate handshake process as well as some certificate information.
Certificate Validity and Renewal
Let's Encrypt certificates have a relatively short validity period of 90 days by design. This short lifetime improves security, but it also means that certificates must be renewed regularly.
You do not need to manually renew certificates. cert-manager automatically handles the renewal process and requests new certificates from Let's Encrypt before the current certificates expire. As long as cert-manager is operating correctly and the required Issuer (or ClusterIssuer) and Certificate resources exist, certificate renewal happens seamlessly in the background.
Info
cert-manager typically initiates the renewal process 30 days before a certificate's expiration date. You can customize this behavior by setting the renewBefore
field in the Certificate resource. For more details, see the cert-manager Certificate documentation.
Conclusion
In this guide, you secured your Ingress by installing the cert-manager certificate provisioner and setting up a Let's Encrypt ClusterIssuer to automatically issue TLS certificates. Your HTTP traffic is now redirected to HTTPS, and your backend services are accessible over a secure connection.