Skip to content

5.1.2 Certificates

cert-manager automates the issuance and renewal of X.509 TLS certificates from sources such as Let's Encrypt, Vault, or private CAs. Every ingress-exposed service in the cluster relies on cert-manager for its TLS certificates.

How to use this page

Each component has an Install section showing the Flux HelmRelease, a Configuration section with Helm values, and a Verify section to confirm it is working.

All code blocks are labelled with their file path in the repository. Select your target environment (AWS or Bare Metal) in any tab group — the choice syncs across the entire page.

  • Using the existing rciis-devops repository: All files already exist. Skip the mkdir and git add/git commit commands — they are for users building a new repository. Simply review the files, edit values for your environment, and push.
  • Building a new repository from scratch: Follow the mkdir, file creation, and git commands in order.
  • No Git access: Expand the "Alternative: Helm CLI" block under each Install section.

cert-manager

cert-manager runs as a Deployment with three components: the main controller, the webhook, and the CA injector. It watches for Certificate and ClusterIssuer resources and orchestrates certificate issuance, renewal, and injection into Secrets.

Install

The base HelmRelease tells Flux which chart to install. This file is shared across all environments — environment-specific settings are applied via patches (shown in the Configuration section).

Create the base directory and file:

mkdir -p flux/infra/base
Field Value Explanation
chart cert-manager The Helm chart name from the Jetstack registry
version 1.19.4 Pinned chart version — update this to upgrade cert-manager
sourceRef.name jetstack References a HelmRepository CR pointing to https://charts.jetstack.io
targetNamespace cert-manager cert-manager runs in its own namespace
crds: CreateReplace Automatically installs and updates cert-manager CRDs
remediation.retries 3 Flux retries up to 3 times if the install or upgrade fails

Save the following as flux/infra/base/cert-manager.yaml:

flux/infra/base/cert-manager.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cert-manager
  namespace: flux-system
spec:
  targetNamespace: cert-manager
  interval: 30m
  chart:
    spec:
      chart: cert-manager
      version: "1.19.4"
      sourceRef:
        kind: HelmRepository
        name: jetstack
        namespace: flux-system
  releaseName: cert-manager
  install:
    createNamespace: true
    crds: CreateReplace
    remediation:
      retries: 3
  upgrade:
    crds: CreateReplace
    remediation:
      retries: 3
  values:
    crds:
      enabled: true
      keep: true
    replicaCount: 2
    topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        nodeTaintsPolicy: Honor
        labelSelector:
          matchLabels:
            app.kubernetes.io/instance: cert-manager
            app.kubernetes.io/component: controller
    startupapicheck:
      timeout: 5m
    webhook:
      hostNetwork: false
      replicaCount: 2
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app.kubernetes.io/instance: cert-manager
              app.kubernetes.io/component: webhook
    cainjector:
      replicaCount: 2
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app.kubernetes.io/instance: cert-manager
              app.kubernetes.io/component: cainjector
    global:
      rbac:
        create: true
    resources:
      requests:
        cpu: 50m
        memory: 64Mi
      limits:
        cpu: 200m
        memory: 256Mi
    prometheus:
      enabled: true
      servicemonitor:
        enabled: true
        labels:
          release: prometheus
Alternative: Helm CLI

If you do not have Git access, install cert-manager directly:

helm repo add jetstack https://charts.jetstack.io
helm repo update
helm upgrade --install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version 1.19.4 \
  -f values.yaml

Configuration

The environment patch overrides the base HelmRelease with cluster-specific settings. The values file controls how cert-manager behaves. Select your environment and deployment size below.

Create the environment overlay directory:

mkdir -p flux/infra/aws/cert-manager
mkdir -p flux/infra/baremetal/cert-manager
mkdir -p flux/infra/baremetal/cert-manager

Environment Patch

The patch file enables Gateway API support and adjusts replica counts based on cluster size. Save the following as the patch file for your environment:

On AWS, cert-manager is configured with a single replica to reduce costs while maintaining functionality. Gateway API support is enabled for automatic certificate provisioning on Gateway resources.

flux/infra/aws/cert-manager/patch.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cert-manager
spec:
  storageNamespace: cert-manager
  values:
    replicaCount: 1
    topologySpreadConstraints: []
    config:
      enableGatewayAPI: true
    webhook:
      replicaCount: 1
      topologySpreadConstraints: []
    cainjector:
      replicaCount: 1
      topologySpreadConstraints: []
Setting Value Why
replicaCount: 1 Single replica AWS has built-in HA; single cert-manager instance is sufficient
topologySpreadConstraints: [] Disabled No need for spread constraints with one replica
config.enableGatewayAPI true Enables automatic certificate provisioning for Gateway API resources
webhook.replicaCount 1 Webhook runs on a single replica for cost efficiency

On Bare Metal, cert-manager is configured for HA with topology spreading to ensure availability across cluster nodes. Gateway API support is enabled.

flux/infra/baremetal/cert-manager/patch.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cert-manager
spec:
  values:
    config:
      enableGatewayAPI: true
Setting Value Why
config.enableGatewayAPI true Enables automatic certificate provisioning for Gateway API resources

On Bare Metal, cert-manager is configured for HA with topology spreading to ensure availability across cluster nodes. Gateway API support is enabled.

flux/infra/baremetal/cert-manager/patch.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cert-manager
spec:
  values:
    config:
      enableGatewayAPI: true
Setting Value Why
config.enableGatewayAPI true Enables automatic certificate provisioning for Gateway API resources

Helm Values

The values file controls cert-manager's core features. For Bare Metal, the base configuration in flux/infra/base/cert-manager.yaml already includes HA settings and does not require additional values overrides — the patch above is sufficient.

For AWS, if you need to customize beyond the patch, you can create a values file. Most AWS deployments use the patch only.

flux/infra/aws/cert-manager/values.yaml
# cert-manager — AWS HA configuration
# Use only if you need to override base values

crds:
  enabled: true
  keep: true

replicaCount: 2

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: DoNotSchedule
    nodeTaintsPolicy: Honor
    labelSelector:
      matchLabels:
        app.kubernetes.io/instance: cert-manager
        app.kubernetes.io/component: controller

startupapicheck:
  timeout: 5m

webhook:
  hostNetwork: false
  replicaCount: 2
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app.kubernetes.io/instance: cert-manager
          app.kubernetes.io/component: webhook

cainjector:
  replicaCount: 2
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app.kubernetes.io/instance: cert-manager
          app.kubernetes.io/component: cainjector

global:
  rbac:
    create: true

resources:
  requests:
    cpu: 50m
    memory: 64Mi
  limits:
    cpu: 200m
    memory: 256Mi

prometheus:
  enabled: true
  servicemonitor:
    enabled: true
    labels:
      release: prometheus
flux/infra/aws/cert-manager/values.yaml
# cert-manager — AWS Non-HA configuration
# Single replica per component, reduced resources

crds:
  enabled: true
  keep: true

replicaCount: 1

startupapicheck:
  timeout: 5m

webhook:
  hostNetwork: false
  replicaCount: 1

cainjector:
  replicaCount: 1

global:
  rbac:
    create: true

resources:
  requests:
    cpu: 25m
    memory: 32Mi
  limits:
    cpu: 100m
    memory: 128Mi

prometheus:
  enabled: true
  servicemonitor:
    enabled: false

The base configuration already includes HA settings. No additional values file is required unless you want to customize specific behavior. The patch above is sufficient for most deployments.

If customization is needed:

flux/infra/baremetal/cert-manager/values.yaml
# cert-manager — Bare Metal HA configuration (Base settings)
# This is already defined in flux/infra/base/cert-manager.yaml
# Only create this file if you need to override specific values

crds:
  enabled: true
  keep: true

replicaCount: 2

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: DoNotSchedule
    nodeTaintsPolicy: Honor
    labelSelector:
      matchLabels:
        app.kubernetes.io/instance: cert-manager
        app.kubernetes.io/component: controller

startupapicheck:
  timeout: 5m

webhook:
  hostNetwork: false
  replicaCount: 2
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app.kubernetes.io/instance: cert-manager
          app.kubernetes.io/component: webhook

cainjector:
  replicaCount: 2
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app.kubernetes.io/instance: cert-manager
          app.kubernetes.io/component: cainjector

global:
  rbac:
    create: true

resources:
  requests:
    cpu: 50m
    memory: 64Mi
  limits:
    cpu: 200m
    memory: 256Mi

prometheus:
  enabled: true
  servicemonitor:
    enabled: true
    labels:
      release: prometheus

The base configuration already includes HA settings. No additional values file is required unless you want to customize specific behavior. The patch above is sufficient for most deployments.

If customization is needed:

flux/infra/baremetal/cert-manager/values.yaml
# cert-manager — Bare Metal HA configuration (Base settings)
# This is already defined in flux/infra/base/cert-manager.yaml
# Only create this file if you need to override specific values

crds:
  enabled: true
  keep: true

replicaCount: 2

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: DoNotSchedule
    nodeTaintsPolicy: Honor
    labelSelector:
      matchLabels:
        app.kubernetes.io/instance: cert-manager
        app.kubernetes.io/component: controller

startupapicheck:
  timeout: 5m

webhook:
  hostNetwork: false
  replicaCount: 2
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app.kubernetes.io/instance: cert-manager
          app.kubernetes.io/component: webhook

cainjector:
  replicaCount: 2
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app.kubernetes.io/instance: cert-manager
          app.kubernetes.io/component: cainjector

global:
  rbac:
    create: true

resources:
  requests:
    cpu: 50m
    memory: 64Mi
  limits:
    cpu: 200m
    memory: 256Mi

prometheus:
  enabled: true
  servicemonitor:
    enabled: true
    labels:
      release: prometheus

Key settings (all environments):

Setting Value Why
crds.enabled true Automatically installs cert-manager CRDs (Certificate, Issuer, ClusterIssuer)
replicaCount 1 (AWS) / 2 (Bare Metal) Controller instances — bare metal uses 2 for HA
topologySpreadConstraints Enabled (Bare Metal) Spreads replicas across different nodes for availability
config.enableGatewayAPI true Enables automatic cert provisioning for Gateway API resources
webhook.replicaCount 1 (AWS) / 2 (Bare Metal) Webhook instances for certificate validation
prometheus.enabled true Exports cert-manager metrics for monitoring

Extra Manifests

Save the following ClusterIssuer for your environment. This enables automatic certificate provisioning from Let's Encrypt using Cloudflare DNS-01 challenges.

Save as flux/infra/aws/cert-manager/cluster-issuer.yaml:

flux/infra/aws/cert-manager/cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: cloudflare-issuer
spec:
  acme:
    email: [email protected]
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: cloudflare-issuer-account-key
    solvers:
    - dns01:
        cloudflare:
          email: [email protected]
          apiKeySecretRef:
            name: cloudflare-api-key
            key: api-key

Info

The ClusterIssuer uses the cloudflare-api-key Secret in the cert-manager namespace. This Secret is deployed via SOPS-encrypted secrets in apps/infra/secrets/. The issuer automatically renews certificates 30 days before expiration.

Save as flux/infra/baremetal/cert-manager/cluster-issuer.yaml:

flux/infra/baremetal/cert-manager/cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: cloudflare-issuer
spec:
  acme:
    email: [email protected]
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: cloudflare-issuer-account-key
    solvers:
    - dns01:
        cloudflare:
          email: [email protected]
          apiKeySecretRef:
            name: cloudflare-api-key
            key: api-key

Info

The ClusterIssuer uses the cloudflare-api-key Secret in the cert-manager namespace. This Secret is deployed via SOPS-encrypted secrets in apps/infra/secrets/. The issuer automatically renews certificates 30 days before expiration.

Save as flux/infra/baremetal/cert-manager/cluster-issuer.yaml:

flux/infra/baremetal/cert-manager/cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: cloudflare-issuer
spec:
  acme:
    email: [email protected]
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: cloudflare-issuer-account-key
    solvers:
    - dns01:
        cloudflare:
          email: [email protected]
          apiKeySecretRef:
            name: cloudflare-api-key
            key: api-key

Info

The ClusterIssuer uses the cloudflare-api-key Secret in the cert-manager namespace. This Secret is deployed via SOPS-encrypted secrets in apps/infra/secrets/. The issuer automatically renews certificates 30 days before expiration.

Commit and Deploy

Once all files are in place, commit and push to trigger Flux deployment:

git add flux/infra/base/cert-manager.yaml \
        flux/infra/aws/cert-manager/
git commit -m "feat(cert-manager): add cert-manager for AWS environment"
git push
git add flux/infra/base/cert-manager.yaml \
        flux/infra/baremetal/cert-manager/
git commit -m "feat(cert-manager): add cert-manager for bare metal environment"
git push
git add flux/infra/base/cert-manager.yaml \
        flux/infra/baremetal/cert-manager/
git commit -m "feat(cert-manager): add cert-manager for bare metal environment"
git push

Flux will detect the new commit and begin deploying cert-manager. To trigger an immediate sync instead of waiting for the next poll interval:

flux reconcile kustomization infra-cert-manager -n flux-system --with-source

Verify

After cert-manager is deployed, confirm it is working:

# Check cert-manager pods are running
kubectl get pods -n cert-manager
# Verify ClusterIssuer is ready
kubectl get clusterissuer cloudflare-issuer -o wide
# Test certificate issuance (optional)
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: test-cert
  namespace: default
spec:
  secretName: test-cert-tls
  issuerRef:
    name: cloudflare-issuer
    kind: ClusterIssuer
  dnsNames:
    - test.rciis.africa
EOF
# Check certificate status
kubectl describe certificate test-cert -n default
# Clean up test certificate
kubectl delete certificate test-cert -n default
kubectl delete secret test-cert-tls -n default

Flux Operations

This component is managed by Flux as HelmRelease cert-manager and Kustomization infra-cert-manager.

Check whether the HelmRelease and Kustomization are in a Ready state:

flux get helmrelease cert-manager -n flux-system
flux get kustomization infra-cert-manager -n flux-system

Trigger an immediate sync — pulls the latest Git revision and re-applies the manifests. Use after pushing config changes or to verify a fix:

flux reconcile kustomization infra-cert-manager -n flux-system --with-source

Trigger a Helm upgrade — re-runs the Helm install/upgrade for this release without waiting for the next interval. Use when the HelmRelease values have changed:

flux reconcile helmrelease cert-manager -n flux-system

View recent Flux controller logs for this release — useful for diagnosing why a sync or upgrade failed:

flux logs --kind=HelmRelease --name=cert-manager -n flux-system

Recovering a stalled HelmRelease

If the HelmRelease shows Stalled with RetriesExceeded, Flux will not retry automatically. Suspend and resume to clear the failure counter, then reconcile:

flux suspend helmrelease cert-manager -n flux-system
flux resume helmrelease cert-manager -n flux-system
flux reconcile kustomization infra-cert-manager -n flux-system

Only run this after confirming the underlying issue (e.g. pod crash, timeout) has been resolved. See Maintenance — Recovering Stalled Resources for details.


Next Steps

Certificates are now configured. Proceed to 5.1.3 GitOps & Delivery to set up Flux and the GitOps deployment pipeline.