Introduction
This guide explains how to configure Let’s Encrypt certificates using CertManager in a Kubernetes cluster managed with GitOps using Flux. We’ll use Cloudflare for DNS validation. By the end, you’ll have automated certificate issuance and management, improving security and ease of use.
Prerequisites
Before proceeding, ensure you have the following:
- Kubernetes Cluster: A running cluster.
- Flux: Installed and configured for GitOps.
- Cloudflare Account: Access with API token privileges.
Overview of Steps
- Set up the namespace and Helm repository for cert-manager.
- Configure Cloudflare API tokens.
- Create staging and production issuers.
- Deploy certificates.
- Verify and troubleshoot the setup.
Here is the repository tree that you likely have at this moment with - new files to deploy in orange - files to update in green
..
├── apps
│ └── debugpod
│ └── base
│ ├── debugpod.yaml
│ └── kustomization.yaml
├── clusters
│ └── production
│ ├── apps
│ │ └── kustomization.yaml
│ ├── flux-system
│ │ ├── cert-manager.yaml
│ │ ├── gotk-components.yaml
│ │ ├── gotk-sync.yaml
│ │ ├── kustomization.yaml
│ │ ├── pull-secrets.yaml
│ │ └── sources
│ │ ├── cert-manager-helmrelease.yaml
│ │ ├── cert-manager-namespace.yaml
│ │ ├── jetstack-helmrepository.yaml
│ │ └── kustomization.yaml
│ └── kustomization.yaml
├── infrastructure
│ ├── monitoring
│ │ ├── grafana
│ │ └── prometheus
│ ├── networking
│ │ └── ingress-nginx
│ └── security
│ ├── cert-manager
│ │ ├── kustomization.yaml
│ │ ├── letsencrypt-cloudflare-api-token-secret.yaml
│ │ ├── letsencrypt-production-certificate.yaml
│ │ ├── letsencrypt-production-clusterissuer.yaml
│ │ ├── letsencrypt-staging-certificate.yaml
│ │ └── letsencrypt-staging-clusterissuer.yaml
│ └── pull-secrets
│ ├── docker-hub.yaml
│ └── kustomization.yaml
├── kubeconfig
├── policies
│ ├── gatekeeper
│ │ ├── constraints
│ │ └── templates
│ └── kyverno
│ ├── policies
│ └── rules
├── README.md
├── secrets.yaml
└── talosconfig
Step 1: Configure Cert-Manager Namespace and Helm Repository
Create Namespace
Save the following YAML to clusters/production/flux-system/sources/cert-manager-namespace.yaml:
# File: clusters/production/flux-system/sources/cert-manager-namespace.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager
Add Helm Repository
Save the following YAML to clusters/production/flux-system/sources/jetstack-helmrepository.yaml:
# File: clusters/production/flux-system/sources/jetstack-helmrepository.yaml
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: jetstack
namespace: flux-system
spec:
interval: 1h
url: https://charts.jetstack.io
Install Cert-Manager with HelmRelease
Save the following YAML to clusters/production/flux-system/sources/cert-manager-helmrelease.yaml:
# File: clusters/production/flux-system/sources/cert-manager-helmrelease.yaml
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: cert-manager
namespace: cert-manager
spec:
interval: 1h
releaseName: cert-manager
targetNamespace: cert-manager
chart:
spec:
chart: cert-manager
sourceRef:
kind: HelmRepository
name: jetstack
namespace: flux-system
version: 1.16.2
values:
installCRDs: true
install:
crds: CreateReplace
createNamespace: true
Refer Helm files
in clusters/production/flux-system/sources/kustomization.yaml
# File: clusters/production/flux-system/sources/kustomization.yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- cert-manager-namespace.yaml
- jetstack-helmrepository.yaml
- cert-manager-helmrelease.yaml
Step 2: Configure Cloudflare API Token
Generate a Cloudflare API token with DNS edit permissions.
Save the following YAML to infrastructure/security/cert-manager/letsencrypt-cloudflare-api-token-secret.yaml:
# File: infrastructure/security/cert-manager/letsencrypt-cloudflare-api-token-secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
name: letsencrypt-cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: <your-cloudflare-api-token>
Step 3: Create ClusterIssuers
Staging Issuer
Save the following YAML to infrastructure/security/cert-manager/letsencrypt-staging-clusterissuer.yaml:
# File: infrastructure/security/cert-manager/letsencrypt-staging-clusterissuer.yaml
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: <your--CF-account-email>
privateKeySecretRef:
name: letsencrypt-staging-issuer-secret
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: letsencrypt-cloudflare-api-token-secret
key: api-token
Production Issuer
Save the following YAML to infrastructure/security/cert-manager/letsencrypt-production-clusterissuer.yaml:
# File: infrastructure/security/cert-manager/letsencrypt-production-clusterissuer.yaml
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-production
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: <your_CF_registered@email@here>
privateKeySecretRef:
name: letsencrypt-production-issuer-secret
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: letsencrypt-cloudflare-api-token-secret
key: api-token
Step 4: Create Certificates
Staging Certificate
Save the following YAML to infrastructure/security/cert-manager/letsencrypt-staging-certificate.yaml:
# File: infrastructure/security/cert-manager/letsencrypt-staging-certificate.yaml
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: letsencrypt-staging-certificate
namespace: default
spec:
dnsNames:
- <endpoint>.<your_domain.com>
secretName: letsencrypt-staging-cert-tls
issuerRef:
kind: ClusterIssuer
name: letsencrypt-staging
Production Certificate
Save the following YAML to infrastructure/security/cert-manager/letsencrypt-production-certificate.yaml:
# File: infrastructure/security/cert-manager/letsencrypt-production-certificate.yaml
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: letsencrypt-production-certificate
namespace: default
spec:
dnsNames:
- <endpoint>.<your_domain.com>
secretName: letsencrypt-production-cert-tls
issuerRef:
kind: ClusterIssuer
name: letsencrypt-production
commonName: <endpoint>.<your_domain.com>
renewBefore: 720h
Optional. Certificates Clean-Up
Cron Job to clean-up expired certificates can be created with
infrastructure/security/cert-manager/cronjob-old-certificate-removal.yaml:
# File: infrastructure/security/cert-manager/cronjob-old-certificate-removal.yaml
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: cert-cleanup
namespace: default
spec:
schedule: "0 3 * * *" # Runs daily at 3 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: cert-cleanup
image: bitnami/kubectl
command:
- /bin/sh
- -c
- |
kubectl delete secret $(kubectl get secrets --no-headers | grep 'old-certificate' | awk '{print $1}')
restartPolicy: OnFailure
Refer cert-issuer files
in infrastructure/security/cert-manager/kustomization.yaml:
# File: infrastructure/security/cert-manager/kustomization.yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: cert-manager
# Include all YAML resources in this directory
resources:
- letsencrypt-cloudflare-api-token-secret.yaml
- letsencrypt-production-certificate.yaml
- letsencrypt-production-clusterissuer.yaml
- letsencrypt-staging-certificate.yaml
- letsencrypt-staging-clusterissuer.yaml
- cronjob-old-certificate-removal.yaml # Optional
Update FLux-System
Create clusters/production/flux-system/cert-manager.yaml:
# File: clusters/production/flux-system/cert-manager.yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: cert-manager
namespace: cert-manager
spec:
interval: 10m
path: ./infrastructure/security/cert-manager
prune: true
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
And refer it as well as sources folder in clusters/production/flux-system/kustomization.yaml:
# File: clusters/production/flux-system/kustomization.yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
- pull-secrets.yaml
- sources
- cert-manager.yaml
Commit and enjoy…or troubleshoot
git add -A
git commit -m "Certificates management"
git push origin main
flux reconcile kustomization flux-system --namespace flux-system
Check what has been deployed:
# repo
kubectl get helmrepository --all-namespaces
# release
kubectl get helmchart --all-namespaces
# cluster issuer
kubectl get clusterissuers
# pods for cert-manager
kubectl get pods --all-namespaces
# certificates
kubectl get certificate --all-namespaces