Quick Links

Cert-Manager automates the provisioning of certificates within Kubernetes clusters. It provides a set of custom resources to issue certificates and attach them to services.

One of the most common use cases is securing web apps and APIs with SSL certificates from Let's Encrypt. Here's how to add Cert-Manager to your cluster, set up a Let's Encrypt certificate issuer, and acquire a certificate for Pods exposed via an Ingress.

Installing Cert-Manager

Cert-Manager is easiest to install using Helm. Helm is a Kubernetes package manager which lets you add applications to your cluster using repositories of pre-built charts. Make sure you've got Helm installed and setup with a connection to your Kubernetes cluster.

Begin by adding the Jetstack repository to your Helm installation. Jetstack originally developed Cert-Manager before it was donated to the CNCF.

helm repo add jetstack https://charts.jetstack.io

helm repo update

Now install Cert-Manager into your cluster:

helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.5.3 --set installCRDs=true

Replace the version number shown above with the latest release shown in the Cert-Manager documentation.

The command will install Cert-Manager in a new Kubernetes namespace called cert-manager. The installCRDs setting will add Cert-Manager's custom Kubernetes resources during the installation. This only works with Helm version 3.2 and newer - if you're using an older version, you must manually add the resource definitions with Kubectl:

kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.3/cert-manager.crds.yaml

Adding the Kubectl Plugin

Cert-Manager has a Kubectl plugin which simplifies some common management tasks. It also lets you check whether Cert-Manager is up and ready to serve requests.

Install the plugin by downloading its archive and extracting it to the correct directory:

curl -L -o kubectl-cert-manager.tar.gz https://github.com/jetstack/cert-manager/releases/latest/download/kubectl-cert_manager-linux-amd64.tar.gz

tar xzf kubectl-cert-manager.tar.gz

sudo mv kubectl-cert_manager /usr/local/bin

Now use the plugin to check your Cert-Manager installation is working:

kubectl cert-manager check api

You should see the following output:

The cert-manager API is ready

Now you're ready to add an issuer to get certificates from Let's Encrypt.

Creating a Certificate Issuer

Issuers and cluster issuers are resources which supply certificates to your cluster. The basic Cert-Manager installation created so far is incapable of issuing certificates. Adding an issuer that's configured to use Let's Encrypt lets you dynamically acquire new certificates for services in your cluster.

Create a YAML file in your working directory and name it issuer.yml. Add the following content:

apiVersion: cert-manager.io/v1

kind: ClusterIssuer

metadata:

name: letsencrypt-staging

spec:

acme:

server: https://acme-staging-v02.api.letsencrypt.org/directory

email: example@example.com

privateKeySecretRef:

name: letsencrypt-staging

solvers:

- http01:

ingress:

class: nginx

You must replace the email address with your own contact email. This will be included in your certificates. Let's Encrypt may also email you at the address if it needs to send you alerts about your certificates.

We're creating a ClusterIssuer as these are available to all resources in your cluster, irrespective of namespace. A standard Issuer is a namespaced resource which can only supply certificates within its own namespace.

Our issuer configuration instructs Cert-Manager to get certificates from the Let's Encrypt staging server. It's a good idea to use the staging environment while you're setting up your integration to avoid hitting Let's Encrypt's stringent production rate limits.

Use kubectl to add the issuer to your cluster:

kubectl create -f issuer.yml

Getting a Certificate

Now you can use your issuer to acquire a certificate for a service exposed via an Ingress resource. Cert-Manager automatically monitors Ingress resources and creates certificates using the configuration in their tls field. You just need to add an annotation that names the issuer or cluster issuer you want to use.

apiVersion: apps/v1

kind: Deployment

metadata:

name: my-app

spec:

replicas: 1

selector:

matchLabels:

app: my-app

template:

metadata:

labels:

app: my-app

spec:

containers:

- name: my-app

image: wordpress:latest

ports:

- containerPort: 80

---

apiVersion: v1

kind: Service

metadata:

name: my-service

spec:

selector:

app: my-app

ports:

port: 80

---

apiVersion: networking.k8s.io/v1beta1

kind: Ingress

metadata:

name: my-ingress

annotations:

kubernetes.io/ingress.class: nginx

cert-manager.io/cluster-issuer: letsencrypt-staging

spec:

rules:

- host: example.com

http:

paths:

- path: /

backend:

serviceName: my-service

servicePort: 80

tls:

- hosts:

- example.com

This YAML file defines a Pod, a Service, and an Ingress exposing the service. It assumes use of nginx-ingress as the Ingress controller. The Pod runs a WordPress container which will be accessible over HTTPS at example.com.

The presence of the cert-manager.io/cluster-issuer annotation in the Ingress resource will be detected by Cert-Manager. It'll use the letsencrypt-staging cluster issuer created earlier to acquire a certificate covering the hostnames defined in the Ingress' tls.hosts field.

Using Let's Encrypt in Production

Once you've successfully acquired a staging certificate, you can migrate to the Let's Encrypt production servers. Staging certificates are valid but not trusted by browsers so you must get a production replacement before putting your site live.

It's best to add a separate cluster issuer for the production server. You can then reference the appropriate issuer in each of your Ingress resources, depending on whether they're production-ready.

Copy the issuer configuration shown above and change the name fields to letsencrypt-production. Next, replace the server URL with the value shown below:

https://acme-v02.api.letsencrypt.org/directory

Create the new issuer in your cluster:

kubectl create -f issuer-production.yml

Update your Ingress resource to request a production certificate by changing the value of the cert-manager.io/cluster-issuer annotation to letsencrypt-production (or the name you assigned to your own production issuer). Use kubectl to apply the change:

kubectl apply -f my-ingress.yaml

You should now have fully functioning HTTPS enabled for your Ingress resource. Cert-Manager will automatically manage your certificates and renew them before they expire.

Upgrading Cert-Manager

Cert-Manager releases usually support in-place upgrades with Helm:

helm repo update

helm upgrade --version <new version> cert-manager jetstack/cert-manager

Certificates remain available during upgrades but the renewal process will be halted.

Although upgrades are now normally straightforward, you should always review the release notes to identify potential changes you need to make. This is particularly important if you're upgrading Kubernetes or moving through several Cert-Manager versions. If you're still using an older Kubernetes release, you might be on an obsolete Cert-Manager version that requires significant manual intervention to bring up to date.

Summary

Let's Encrypt is easily added to a Kubernetes cluster using Cert-Manager. You need to install Cert-Manager with Helm, create an issuer that uses the Let's Encrypt API, then reference that issuer in your Ingress resources.

You can supply Cert-Manager with your own configuration for more advanced use cases. You can specify a certificate lifetime (use the cert-manager.io/duration Ingress annotation), manually declare the certificate's common name (cert-manager.io/common-name), and use DNS challenges instead of HTTP. The latter option can be useful in specific scenarios, such as when you want to acquire a wildcard certificate.

Straightforward usage to protect web apps and APIs should work as-is using the resources shown above. HTTP verification works by manipulating the Ingress controller to provide a temporary .well-known URL that Let's Encrypt can access. If your domain provides the correct value at that URL, Let's Encrypt trusts you're in control and issues the certificate.