Ett utgånget TLS-certifikat är en av de mest onödiga driftstopp-orsakerna som finns. Du vet att det kommer, du vet när det kommer, och ändå hamnar det på en incidentrapport minst en gång per år hos de flesta team som kör Kubernetes. cert-manager löser problemet genom att göra certifikatutfärdande och förnyelse till en del av klustrets egen reglerade loop, precis som en Deployment håller antal repliker eller en HorizontalPodAutoscaler håller belastningsmål. I den här guiden bygger vi upp en komplett installation från grunden: vi installerar cert-manager 1.21.2, kopplar den mot Let’s Encrypt via både HTTP-01 och DNS-01, kopplar in ingress-nginx, och går igenom de fel som faktiskt stoppar folk i produktion.
Målgruppen är utvecklare och drifttekniker som redan har ett Kubernetes-kluster igång men som fortfarande hanterar certifikat manuellt, eller som ärvt en cert-manager-installation de inte riktigt litar på. Vi går igenom alla steg med riktiga YAML-manifest, kommandon du kan köra direkt, och konkreta felsökningstips för de buggar som upprepas om och om igen i supportforum och GitHub-issues.
Vad är cert-manager och varför behöver du det
cert-manager är ett open source-projekt som lägger till certifikat och certifikatutfärdare som egna resurstyper i ett Kubernetes-kluster. Enligt projektets egen README-dokumentation “lägger cert-manager till certifikat och certifikatutfärdare som resurstyper i Kubernetes-kluster, och förenklar processen att skaffa, förnya och använda dessa certifikat”. Det är precis den meningen som förklarar varför verktyget blivit standardval i så många kluster: det flyttar certifikatlogik från ett skript som körs på en cron-server till en kontroller som ständigt jämför önskat tillstånd med verkligt tillstånd.
Praktiskt betyder det att du beskriver ett Certificate-objekt, pekar det mot en Issuer eller ClusterIssuer, och låter cert-manager sköta resten: beställning hos certifikatutfärdaren, lösning av utmaningen (challenge), lagring av nyckel och kedja i en Kubernetes-secret, och förnyelse i god tid innan utgång. Projektets dokumentation beskriver det själva som att cert-manager “säkerställer att certifikat förblir giltiga och uppdaterade, och försöker förnya certifikat vid lämplig tidpunkt före utgång för att minska risken för driftstopp och ta bort manuellt arbete” (källa: cert-manager README). Det är skillnaden mellan att vakna klockan tre på natten för ett utgånget certifikat, och att aldrig tänka på det alls.
Den senaste stabila versionen i oktober 2026 är cert-manager v1.21.2, släppt 11 september 2026, som en patch i 1.21-serien som inleddes med v1.21.0 den 8 juli 2026. Version 1.22 var vid skrivande stund inte stabil och väntades runt november 2026 enligt projektets supportöversikt. Det är den versionen vi använder i hela denna guide, och du bör undvika att blanda CRD:er, kontrollkomponenter och webhook från olika huvudversioner eftersom det är en av de vanligaste orsakerna till konstiga fel efter en uppgradering.
Förutsättningar och versioner
Innan du börjar behöver du ett fungerande kluster och några verktyg installerade lokalt. Nedan listas exakt vad vi testat denna guide mot. Om du kör äldre versioner fungerar det mesta ändå, men CRD-strukturen för Certificate och ClusterIssuer har ändrats tillräckligt över åren att gamla exempel från bloggar ofta inte funkar rakt av.
| Komponent | Version som används i guiden | Minimikrav |
|---|---|---|
| Kubernetes | 1.31 eller senare | 1.28+ |
| cert-manager | v1.21.2 | v1.15+ (för moderna CRD:er) |
| kubectl | 1.31.x | Matchande eller nyare än klustret |
| Helm | 3.15+ | 3.8+ |
| ingress-nginx | controller v1.12+ | v1.9+ |
| DNS-leverantör-API (om DNS-01 används) | Cloudflare, Route53 eller Azure DNS | API-nyckel med skrivrättigheter till DNS-zon |
Du behöver också ett domännamn du kontrollerar DNS för, och ett e-postkonto du är beredd att registrera hos Let’s Encrypt för utgångsnotiser. Om du testar mot ett internt kluster utan publik DNS kan du fortfarande följa guiden med DNS-01, men HTTP-01-delarna kräver att port 80 faktiskt är nåbar utifrån.
Steg 1: Installera CRD:erna
cert-manager definierar sex egna resurstyper: Certificate, CertificateRequest, Issuer, ClusterIssuer, Order och Challenge. Dessa måste registreras i klustret innan kontrollern startar, annars kraschar den direkt vid uppstart. Installera CRD:erna separat, innan du installerar själva Helm-chartet.
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.2/cert-manager.crds.yaml
Verifiera att de sex CRD:erna faktiskt kom in i klustret innan du går vidare:
kubectl get crds | grep cert-manager.io
Du ska se sex rader, en för varje resurstyp. Om listan är kortare har något gått fel i nedladdningen eller nätverksåtkomsten mot GitHub, och du bör inte gå vidare till nästa steg.
Steg 2: Installera cert-manager med Helm
Helm är det enklaste sättet att hantera livscykeln för cert-manager, särskilt vid framtida uppgraderingar. Lägg till det officiella Helm-repot och installera i ett eget namespace, separerat från applikationerna.
helm repo add jetstack https://charts.jetstack.io
helm repo update
kubectl create namespace cert-manager
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v1.21.2 \
--set crds.enabled=false
Flaggan crds.enabled=false är avsiktlig här eftersom vi redan installerade CRD:erna manuellt i steg 1. Om du låter Helm installera CRD:erna också, kan en framtida helm uninstall råka ta bort CRD:erna och därmed radera alla dina Certificate-resurser i hela klustret, vilket är ett av de farligaste misstagen i hela den här guiden. Håll CRD-hanteringen separat från Helm-livscykeln.
Kontrollera att alla tre delar av cert-manager (controller, webhook, cainjector) har startat korrekt:
kubectl get pods --namespace cert-manager
Förväntat resultat efter cirka 30 sekunder:
NAME READY STATUS RESTARTS AGE
cert-manager-7d4c9f8b6d-x2k9p 1/1 Running 0 45s
cert-manager-cainjector-5b9c7d4f6c-m8nq2 1/1 Running 0 45s
cert-manager-webhook-8f6d9c5b7d-p3vwt 1/1 Running 0 45s
Om webhook-podden fastnar i CrashLoopBackOff är det nästan alltid ett nätverkspolicy- eller DNS-problem internt i klustret, inte ett fel i cert-manager självt. Vi går igenom det specifika felet längre ner i felsökningsavsnittet.
I produktionskluster bör du även sätta explicita resursgränser för cert-manager-podden istället för att lita på Helm-chartets standardvärden rakt av. Kontrollern är i sig lätt på resurser, men i kluster med tusentals Certificate-objekt och täta reconcile-loopar kan minnesanvändningen växa över tid om inga gränser är satta, vilket i värsta fall gör att noden kastar ut podden vid minnesbrist mitt under en pågående förnyelse. Sätt resources.requests och resources.limits explicit i din Helm-values-fil och håll ett öga på faktisk förbrukning de första veckorna efter installation.
Steg 3: Förstå skillnaden mellan Issuer och ClusterIssuer
Innan du skapar din första utfärdare behöver du bestämma scope. En Issuer är namespace-bunden och kan bara skapa certifikat inom sitt eget namespace. En ClusterIssuer är klusteromfattande och kan refereras från Certificate-objekt i vilket namespace som helst. De flesta team i produktion använder en handfull ClusterIssuer-objekt (en för staging, en för produktion) och låter alla applikationsnamespaces referera dem, istället för att duplicera Issuer-konfiguration i varje namespace.
| Egenskap | Issuer | ClusterIssuer |
|---|---|---|
| Scope | Ett namespace | Hela klustret |
| Typiskt antal i ett kluster | En per team/namespace | 1-3 totalt (staging + prod) |
| Lämpligt för | Multi-tenant-kluster med isolerade credentials | Delad Let’s Encrypt-konfiguration |
| RBAC-komplexitet | Lägre, begränsad per namespace | Högre, kräver klusterbehörighet att skapa |
Steg 4: Skapa en Let’s Encrypt staging-ClusterIssuer
Börja alltid med staging-miljön hos Let’s Encrypt. Staging-certifikat litar ingen webbläsare på, men du undviker att förbruka produktionens kvoter medan du felsöker DNS, ingress och nätverksregler. Det är ett av de råd som upprepas mest i cert-manager-dokumentationen och i produktionsincidenter: byt inte till produktionsendpointen förrän staging fungerar helt.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
email: [email protected]
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-staging-account-key
solvers:
- http01:
ingress:
ingressClassName: nginx
Spara filen som staging-issuer.yaml och applicera den:
kubectl apply -f staging-issuer.yaml
kubectl get clusterissuer letsencrypt-staging -o wide
Statuskolumnen ska visa True under READY. Om den istället visar False med ett felmeddelande om att ACME-kontot inte kunde registreras, kontrollera att e-postadressen är giltig och att klustret har utgående internetåtkomst mot acme-staging-v02.api.letsencrypt.org.
Steg 5: Beställ ditt första certifikat via HTTP-01
HTTP-01 är den enklaste utmaningstypen. cert-manager ber Let’s Encrypt besöka en specifik URL på din domän, och om servern svarar med rätt token anses du äga domänen. Det kräver att trafik på port 80 faktiskt routas fram till klustret och till rätt ingress-controller, vilket är precis där det brukar gå fel första gången.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: app-example-tls
namespace: web
spec:
secretName: app-example-tls
issuerRef:
name: letsencrypt-staging
kind: ClusterIssuer
group: cert-manager.io
dnsNames:
- app.exempel.se
Mer vanligt i praktiken är att du inte skapar Certificate-objektet direkt, utan låter cert-managers “ingress-shim” göra det automatiskt via en annotation på din Ingress-resurs:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
namespace: web
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
ingressClassName: nginx
tls:
- hosts:
- app.exempel.se
secretName: app-example-tls
rules:
- host: app.exempel.se
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80
Ange alltid ingressClassName explicit. Om klustret kör flera ingress-controllers samtidigt och fältet lämnas tomt, kan flera controllers försöka ta hand om samma challenge-resurs, vilket skapar race conditions som är notoriskt svåra att felsöka.
Följ processen med:
kubectl describe certificate app-example-tls -n web
kubectl get order -n web
kubectl get challenge -n web
Ett lyckat flöde går från Issuing till Ready: True inom någon minut. Om en Challenge-resurs blir kvar i pending längre än några minuter, är nästa steg nästan alltid att kontrollera om acmesolver-podden som cert-manager skapar tillfälligt faktiskt går att nå från internet.
Steg 6: Byt till DNS-01 för wildcard-certifikat
HTTP-01 kan inte utfärda wildcard-certifikat som *.exempel.se. Om du behöver det, eller om port 80 av andra skäl är blockerad, är DNS-01 rätt väg. Utmaningen löses genom att cert-manager skapar en TXT-post i din DNS-zon, vilket Let’s Encrypts servrar sedan verifierar.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-dns
spec:
acme:
email: [email protected]
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod-dns-account-key
solvers:
- dns01:
cloudflare:
email: [email protected]
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
API-token för DNS-leverantören lagras i en separat Kubernetes-secret, aldrig direkt i ClusterIssuer-manifestet:
kubectl create secret generic cloudflare-api-token-secret \
--namespace cert-manager \
--from-literal=api-token=DIN_CLOUDFLARE_TOKEN_HAR
DNS-01 har större säkerhetskonsekvenser än HTTP-01 eftersom cert-manager normalt behöver rättigheter att skapa och radera TXT-poster i hela DNS-zonen. Begränsa API-tokenet så mycket leverantören tillåter, gärna till enbart den specifika zonen och enbart till DNS-redigeringsrättigheter, inte fullständig kontoåtkomst.
Steg 7: Gå från staging till produktion
När staging-certifikat utfärdas utan problem byter du helt enkelt endpoint. Skapa en ny ClusterIssuer som pekar mot https://acme-v02.api.letsencrypt.org/directory istället för staging-adressen, och uppdatera dina Ingress-annotationer eller Certificate-objekt till att referera den nya utfärdaren. Radera inte staging-ClusterIssuern, den är värdefull nästa gång du felsöker ett nytt projekt eller en ny domän.
Let’s Encrypts produktionsmiljö har hårdare kvoter än staging. Enligt Let’s Encrypts egen dokumentation om rate limits gäller begränsningar per registrerad domän och per konto, och upprepade misslyckade försök eller duplicerade certifikatbeställningar kan snabbt förbruka dessa kvoter. Undvik att radera och återskapa Certificate-resurser i produktion i en loop medan du felsöker, eftersom varje borttagning kan trigga en ny ACME-order.
Steg 8: Styr förnyelse med renewBefore och det nya renewal-fältet
Som standard förnyar cert-manager certifikat när två tredjedelar av giltighetstiden har passerat. Det går att styra mer exakt med fältet renewBefore, som anger hur lång tid före utgång förnyelsen ska starta:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: app-example-tls
namespace: web
spec:
secretName: app-example-tls
renewBefore: 360h
issuerRef:
name: letsencrypt-prod-dns
kind: ClusterIssuer
dnsNames:
- app.exempel.se
cert-manager 1.21 introducerade även ett nytt, mer uttrycksfullt renewal-fält i Certificate-API:t som kompletterar renewBefore och renewBeforePercentage, och som bland annat kan användas för att styra underhållsfönster eller inaktivera automatisk förnyelse för ett specifikt certifikat. Version 1.21 lade också till experimentellt stöd för ACME Renewal Information (ARI), ett koncept från RFC 9773, bakom feature-gaten ACMEUseARI. Med ARI kan cert-manager fråga certifikatutfärdarens renewalInfo-endpoint och anpassa förnyelsefönstret efter CA:ns egna rekommendationer, till exempel vid en massåterkallelse eller en nyckelrotation hos CA:n.
Steg 9: Koppla in Traefik som alternativ till ingress-nginx
Kör du Traefik istället för ingress-nginx fungerar samma grundprincip, men du måste vara extra noga med ägarskapet av certifikaten. Traefik har en egen inbyggd ACME-klient, och om den är aktiverad samtidigt som cert-manager försöker hantera samma domän eller samma secret uppstår konkurrerande förnyelser som är svåra att diagnostisera. Välj en tydlig ägare: antingen låter du cert-manager sköta allt och Traefik bara konsumerar den secret som cert-manager skapat, eller så stänger du av Traefiks egen ACME-funktion helt för de domäner cert-manager hanterar.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-traefik
namespace: web
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod-dns
spec:
ingressClassName: traefik
tls:
- hosts:
- app.exempel.se
secretName: app-example-tls-traefik
rules:
- host: app.exempel.se
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port:
number: 80
Steg 10: Övervaka certifikatens status löpande
Ett vanligt misstag är att installera cert-manager och sedan aldrig titta på det igen förrän något går sönder. Lägg in enkel övervakning från dag ett. Minimum är att regelbundet köra statuskommandot och att larma på certifikat som inte är Ready: True:
kubectl get certificate --all-namespaces -o custom-columns=\
NAMESPACE:.metadata.namespace,\
NAME:.metadata.name,\
READY:.status.conditions[0].status,\
EXPIRY:.status.notAfter
Kör cert-manager med Prometheus i klustret exponerar kontrollern egna mått, bland annat certmanager_certificate_expiration_timestamp_seconds, som går att larma på direkt i Alertmanager eller motsvarande. Det ger dig varning dagar i förväg om en förnyelse av någon anledning fastnar, istället för att upptäcka det när webbläsaren redan visar varningssidan för besökarna.
Steg 11: Hantera flera miljöer och namespaces korrekt
I kluster med flera team är det lockande att skapa en enda ClusterIssuer och låta alla namespaces dela den, vilket oftast är rätt val rent operativt. Men kom ihåg att en ClusterIssuer-konfiguration med DNS-01 ger alla namespaces i klustret indirekt möjlighet att begära certifikat för vilken domän din DNS-leverantörs API-token täcker. Om olika team äger olika domäner, överväg istället separata namespace-bundna Issuer-objekt med egna, begränsade DNS-credentials per team. Det är en avvägning mellan enkelhet och isolering som du bör göra medvetet, inte av slentrian.
Ett relaterat men separat projekt värt att nämna är cert-manager-csi-driver, som använder samma underliggande ACME- och PKI-logik men monterar certifikat direkt som filer i en pod via en CSI-driver, istället för att gå via en Kubernetes-secret. Det är ett alternativ för team som vill undvika att TLS-nycklar ens tillfälligt lagras som secrets i etcd, till exempel i miljöer med extra strikta krav på nyckelhantering mellan tjänster som pratar mTLS med varandra. För de allra flesta kluster räcker den vanliga secret-baserade modellen gott och väl, men det är bra att veta att alternativet finns innan du bygger en egen lösning för samma problem.
Vanliga pitfalls som kostar tid i produktion
De här fem misstagen återkommer om och om igen i cert-manager-installationer, oavsett molnleverantör eller ingress-controller.
- Blanda CRD-versioner vid uppgradering. Att uppdatera Helm-releasen men lämna gamla CRD:er kvar, eller tvärtom, är den absolut vanligaste orsaken till konstiga valideringsfel efter en uppgradering. Uppdatera alltid CRD:erna och kontrollkomponenterna tillsammans, från samma release.
- Testa direkt mot produktions-ACME. Att skippa staging “för att spara tid” leder ofta till att du snabbt slår i Let’s Encrypts rate limits när en felkonfiguration kräver flera försök, och då måste du vänta timmar eller dagar innan nästa försök tillåts.
- Glömma ingressClassName. I kluster med flera ingress-controllers skapar en utelämnad
ingressClassNamerace conditions där fel controller plockar upp HTTP-01-utmaningen, vilket får den att misslyckas trots att konfigurationen ser korrekt ut. - För breda DNS-API-rättigheter. Ett API-token med skrivåtkomst till hela DNS-kontot istället för en enskild zon är en onödig attackyta. Om tokenet läcker kan en angripare omdirigera betydligt mer än bara ACME-utmaningar.
- Radera och återskapa Certificate-resurser i en loop. Varje borttagning kan trigga en helt ny ACME-order. Gjort upprepade gånger under felsökning i produktion förbrukar det kvoter snabbt och gör problemet värre, inte bättre.
Säkerhet: vad du faktiskt behöver skydda
cert-manager hanterar hemligheter som är värda att skydda ordentligt. ACME-kontonycklar, DNS-leverantörens API-credentials och de utfärdade TLS-nycklarna lagras alla som Kubernetes-secrets. Begränsa RBAC så att cert-manager-kontrollern inte har bredare åtkomst än den faktiskt behöver, och begränsa i sin tur vilka vanliga användare eller service-konton i klustret som kan läsa secrets i cert-manager-namespacet.
Projektet har historiskt haft säkerhetsrådgivningar som andra mjukvaruprojekt av samma storlek. Ett konkret exempel är GHSA-r4pg-vg54-wxx4, en denial-of-service-sårbarhet i hanteringen av ACME-utmaningar som åtgärdades i v1.15.3 efter en koordinerad disclosure-process. Poängen är inte den specifika buggen, utan att du bör prenumerera på projektets säkerhetsrådgivningar på GitHub och faktiskt läsa release notes innan du uppgraderar, särskilt i kluster som exponerar certifikatutfärdande mot internet.
Webhook-komponenten är värd extra uppmärksamhet. Den validerar och muterar cert-manager-resurser innan de sparas i etcd, och om dess eget TLS-certifikat (hanterat av cainjector) blir ogiltigt eller webhooken blir onåbar, kan API-servern vägra acceptera nya eller uppdaterade cert-manager-resurser i hela klustret. Det är en av de mest förvirrande felsignaturerna eftersom felet dyker upp som ett generiskt admission webhook-fel, inte som ett tydligt cert-manager-fel.
Tänk också på leveranskedjan bakom installationen själv. Hämta Helm-chartet och CRD-manifestet från de officiella källorna, jetstack.io och GitHub-releasen, snarare än från speglade kopior på tredjepartsregister du inte kontrollerar. Verifiera checksumman på CRD-filen om du laddar ner den i ett automatiserat pipeline-steg, och lås versionsnumret explicit i både Helm-kommandot och i Git, istället för att peka mot latest eller en flytande tagg som kan ändra innehåll utan förvarning.
Komplett arbetsexempel: TLS för en webbapp från noll
Sätt ihop allt ovan till en komplett, fungerande installation för en enda applikation. Spara följande i en fil som heter webapp-tls.yaml, justera domän och e-post, och applicera den i tur och ordning.
# 1. Namespace för applikationen
apiVersion: v1
kind: Namespace
metadata:
name: web
---
# 2. Produktions-ClusterIssuer med DNS-01 (Cloudflare)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-dns
spec:
acme:
email: [email protected]
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod-dns-account-key
solvers:
- dns01:
cloudflare:
email: [email protected]
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
---
# 3. Ingress med automatisk certifikatutfärdande
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp
namespace: web
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod-dns
spec:
ingressClassName: nginx
tls:
- hosts:
- app.exempel.se
- "*.app.exempel.se"
secretName: webapp-tls
rules:
- host: app.exempel.se
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp-svc
port:
number: 80
Applicera i ordning och följ statusen:
kubectl create secret generic cloudflare-api-token-secret \
--namespace cert-manager \
--from-literal=api-token=DIN_TOKEN
kubectl apply -f webapp-tls.yaml
kubectl wait --for=condition=Ready certificate/webapp-tls -n web --timeout=180s
När kommandot returnerar certificate.cert-manager.io/webapp-tls condition met har du ett giltigt, Let’s Encrypt-utfärdat wildcard-certifikat som förnyar sig själv utan att du rör det igen.
Output-exempel: lyckad och misslyckad certifikatutfärdning
Det är lätt att missa vad man faktiskt letar efter i terminalen om man aldrig sett hur ett lyckat flöde ser ut. Kör kubectl describe certificate webapp-tls -n web efter en lyckad utfärdning, och du ska se ett händelseflöde ungefär som detta längst ner i utskriften:
Status:
Conditions:
Message: Certificate is up to date and has not expired
Observed Generation: 1
Reason: Ready
Status: True
Type: Ready
Not After: 2027-01-01T08:12:03Z
Not Before: 2026-10-03T08:12:03Z
Renewal Time: 2026-12-02T08:12:03Z
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 90s cert-manager Issuing certificate as Secret does not exist
Normal Generated 89s cert-manager Stored new private key in temporary Secret
Normal Requested 88s cert-manager Created new CertificateRequest
Normal Issuing 40s cert-manager The certificate has been successfully issued
Raden Status: True, Type: Ready är det enda du strikt behöver för att veta att allt fungerar. Jämför det med en misslyckad DNS-01-utmaning, där kubectl describe challenge istället visar något i stil med:
Status:
Presented: true
Processing: true
Reason: Waiting for DNS-01 challenge propagation: NS used
to check propagation (ns1.cloudflare.com:53) returned
NXDOMAIN for _acme-challenge.app.exempel.se
State: pending
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 3m cert-manager Challenge scheduled for processing
Warning PresentError 2m cert-manager Error presenting challenge: failed
to change Cloudflare record: 403 Forbidden
Det här exemplet pekar direkt på orsaken: ett 403-svar från Cloudflare betyder nästan alltid att API-tokenet saknar skrivrättigheter för just den zonen, inte att hela integrationen är trasig. Att lära sig läsa Reason-fältet i Challenge-objektet sparar mer felsökningstid än något annat enskilt steg i denna guide.
GitOps och CI/CD: hantera cert-manager deklarativt
I team som redan driver infrastruktur via GitOps-verktyg som Argo CD eller Flux bör cert-manager-konfigurationen in i samma flöde som allt annat, inte hanteras som en engångsinstallation via ett manuellt Helm-kommando. Lägg Helm-värdena för cert-manager i ett Git-repo och låt Argo CD sköta synkroniseringen:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cert-manager
namespace: argocd
spec:
project: infra
source:
repoURL: https://charts.jetstack.io
chart: cert-manager
targetRevision: v1.21.2
helm:
values: |
crds:
enabled: false
destination:
server: https://kubernetes.default.svc
namespace: cert-manager
syncPolicy:
automated:
prune: false
selfHeal: true
Notera prune: false för just den här applikationen. Om cert-manager-raderingen av CRD:er eller webhook-konfigurationen av misstag triggas av en automatisk pruning-cykel i Argo CD, kan det i värsta fall ta bort Certificate-resurser i hela klustret samtidigt. ClusterIssuer-manifesten kan med fördel ligga i ett separat Git-repo från applikationsmanifesten, så att ett team som bara äger en applikation inte kan av misstag ändra den delade certifikatutfärdaren.
Ett vanligt mönster är att även versionshantera ClusterIssuer-objekten och köra dem genom samma pull request-granskning som andra klusterresurser. Det gör det möjligt att se i Git-historiken exakt när en DNS-leverantör bytts eller en e-postadress för ACME-kontot ändrats, vilket annars är svårt att spåra om ändringen bara gjordes med ett löst kubectl apply-kommando från en bärbar dator.
Kombinerar du cert-manager med extern DNS-automatisering, till exempel projektet external-dns, bör du vara tydlig med vem som äger vilken DNS-post. external-dns hanterar normalt A- och CNAME-poster för att peka domäner mot din ingress, medan cert-manager hanterar TXT-posterna för ACME-utmaningar. De två kan samverka utan konflikt så länge de inte konfigureras att skriva över varandras poster i samma zon.
Felsökning: de åtta vanligaste problemen
Den här listan täcker de problem som dyker upp oftast i supportkanaler och GitHub-issues, i ungefärlig ordning efter hur vanliga de är.
- Challenge fastnar i “pending”. Kontrollera först om acmesolver-podden som cert-manager skapar tillfälligt faktiskt är nåbar från internet på port 80. Kör
kubectl get pods -n web | grep acmesolveroch verifiera att podden ärRunning, sedan att din ingress-controller och eventuell brandvägg faktiskt släpper igenom trafiken till den. - ClusterIssuer visar READY: False. Beskriv objektet med
kubectl describe clusterissuer NAMNoch läs fältetstatus.conditions[].message. Vanligaste orsaken är felaktig e-postadress eller att klustret saknar utgående DNS-upplösning mot ACME-serverns adress. - Webhook svarar inte, “context deadline exceeded”. Kontrollera att nätverkspolicies i klustret inte blockerar trafik mellan API-servern och webhook-podden på port 10250 eller den port webhooken konfigurerats med. Detta är särskilt vanligt i kluster med strikta NetworkPolicy-regler som lagts till efter att cert-manager redan installerats.
- DNS-01-utmaning misslyckas trots korrekt token. TXT-posten kan ha hög TTL hos din DNS-leverantör, vilket gör att Let’s Encrypts verifiering sker innan posten har propagerat globalt. Sänk TTL för ACME-relaterade poster, eller vänta längre mellan försök.
- Rate limit nådd hos Let’s Encrypt. Detta uppstår oftast efter upprepad felsökning direkt mot produktionsendpointen. Växla till staging, lös grundproblemet där, och vänta ut tidsfönstret innan du försöker igen mot produktion.
- Certifikatet förnyas inte trots att det snart går ut. Kontrollera cert-manager-kontrollerns loggar med
kubectl logs -n cert-manager deploy/cert-manager. En vanlig orsak är att den ursprungliga Issuer-referensen pekar på en ClusterIssuer som sedan raderats eller bytt namn. - Flera ingress-controllers tävlar om samma challenge. Sätt alltid
ingressClassNameexplicit i både Ingress-resursen och i solver-konfigurationen i din Issuer, så att cert-manager skapar sin tillfälliga challenge-ingress med samma klass. - CRD-valideringsfel efter uppgradering. Verifiera version med
kubectl get crd certificates.cert-manager.io -o jsonpath='{.spec.versions[*].name}'och jämför mot vilken cert-manager-version som faktiskt körs. Installera om CRD-manifestet för samma version som kontrollern innan du felsöker vidare.
Avancerade tips för team som redan kör cert-manager
Om du redan har en fungerande installation finns några förbättringar värda att lägga till. Aktivera ARI-stödet i cert-manager 1.21 för att låta certifikatutfärdaren själv styra förnyelsefönstret snarare än ett statiskt renewBefore-värde, särskilt om du förväntar dig att CA:n kan behöva göra massåterkallelser av säkerhetsskäl. Använd den nya “Modern2026”-profilen för PKCS#12-export om du behöver certifikat i det formatet, eftersom den bygger på AES-256 och en SHA-256-baserad nyckelderivering istället för äldre 3DES/RC2-konstruktioner, och är tänkt att vara kompatibel med FIPS 140-3-krav.
För team som kör flera kluster, överväg att dela en central DNS-01-lösning via en dedikerad webhook-leverantör istället för att duplicera DNS-credentials i varje kluster. Och om du använder Gateway API istället för klassisk Ingress, vet att cert-manager 1.21 har fått förbättrat stöd för Gateway API och cainjector, vilket gör TLS-terminering på Gateway-nivå enklare att koppla ihop med automatisk certifikatutfärdande.
Slutligen: cert-manager 1.21 förbättrade också webhookens återhämtning efter systemavbrott, exempelvis vid en VM-suspend eller live migration hos din molnleverantör. Kontrollern upptäcker nu missade certifikatförnyelser efter ett sådant avbrott och återupptar hanteringen inom ungefär en minut efter att driften återupptagits, vilket är särskilt relevant om du kör noder på spot-instanser eller andra avbrottskänsliga VM-typer.
Kostnad och skalning i stora kluster
cert-manager själv kostar ingenting utöver de resurser kontrollern förbrukar, vilket normalt är blygsamt: en enda replika klarar tusentals Certificate-objekt utan problem på de resursramar som Helm-chartet sätter som standard. Den verkliga kostnadsfrågan handlar istället om vilken certifikatutfärdare du väljer och hur många domäner du hanterar. Let’s Encrypt är gratis oavsett volym, men har som nämnt kvoter per domän och per konto, vilket gör att team med tusentals subdomäner ibland går över till en betald utfärdare som Vault PKI, Venafi eller en intern CA för att undvika kvotbegränsningar helt.
I riktigt stora kluster, med hundratals namespaces och tusentals Certificate-objekt, blir reconcile-loopens frekvens en faktor att hålla koll på. cert-manager räknar om status för varje certifikat med viss periodicitet, och en alltför aggressiv inställning kan belasta både kontrollern och ACME-serverns API i onödan. Standardinställningarna är satta för att fungera bra för de allra flesta kluster, men om du driver en plattform med många tusen certifikat är det värt att läsa igenom kontrollerns egna flaggor för batch-storlek och concurrency innan du skalar upp ytterligare.
Hanterade databaser och extern TLS: vad cert-manager inte gör
Kör du applikationer som kopplar mot hanterade databaser, till exempel en molnleverantörs databastjänst, behöver du komma ihåg att cert-manager bara hanterar certifikaten inom ditt eget kluster. Anslutningen mellan din app och en extern hanterad tjänst har oftast sin egen TLS-hantering på leverantörens sida, och det är inte något cert-manager tar över. Håll de två isär konceptuellt: cert-manager löser “mina egna ingress-endpoints behöver giltiga certifikat”, inte “alla TLS-anslutningar i hela min arkitektur”.
Jämförelse: cert-manager mot manuell Certbot-hantering i kluster
En del äldre kluster kör fortfarande Certbot i en sidecar eller en separat cronjob istället för cert-manager. Skillnaden i driftsbörda är betydande när klustret växer förbi en handfull domäner. Certbot i sig är ett moget och pålitligt verktyg, problemet uppstår när det ska samverka med Kubernetes egna livscykler för pods och secrets. Ett certifikat som Certbot förnyat på disk måste fortfarande packas om till en Kubernetes-secret och rullas ut till rätt pods, vilket ofta löses med ett eget hopkok av skript som i praktiken återskapar en sämre version av det cert-manager redan gör inbyggt.
| Egenskap | cert-manager | Certbot i cronjob |
|---|---|---|
| Hanteringsmodell | Deklarativ, Kubernetes-native kontroller | Imperativt skript, körs på schema |
| Skalning till många domäner | Ett Certificate-objekt per domän, ingen extra logik | Manuell listhantering i skript |
| Wildcard via DNS-01 | Inbyggt stöd för många DNS-leverantörer | Kräver plugin per leverantör |
| Synlighet i klustret | kubectl get certificate ger status direkt | Kräver egen loggning/övervakning |
| Integration med Ingress | Automatisk via annotationer | Manuell montering av certifikatfiler |
Vanliga frågor
Är cert-manager gratis att använda?
Ja, cert-manager är ett open source-projekt och kostar ingenting att installera eller köra. Den löpande kostnaden kommer i stället från certifikatutfärdaren du väljer, och Let’s Encrypt är helt gratis för de flesta användningsfall.
Kan jag använda cert-manager med andra certifikatutfärdare än Let’s Encrypt?
Ja. Utöver ACME-baserade utfärdare som Let’s Encrypt och ZeroSSL stödjer cert-manager även Vault, Venafi och en intern CA-typ för kluster som behöver certifikat som inte är publikt betrodda, till exempel för intern service-till-service-kommunikation.
Vad händer om cert-manager-podden kraschar mitt i en förnyelse?
Kontrollern är designad för att vara återupptagningsbar. Kubernetes startar om podden automatiskt, och vid nästa reconcile-loop fortsätter cert-manager där den var, eller startar om den specifika ACME-ordern om den gamla hunnit gå ut. Ingen manuell åtgärd krävs normalt.
Behöver jag cert-manager om jag bara har ett certifikat totalt?
Tekniskt sett nej, ett enda certifikat går att hantera manuellt med Certbot eller liknande. Men även med ett enda certifikat slipper du bygga och underhålla ett eget förnyelsesystem, vilket för de flesta team är värt installationskostnaden ändå.
Fungerar cert-manager med OpenShift, eller bara ren Kubernetes?
cert-manager stödjer uttryckligen både Kubernetes och OpenShift-kluster, och installationsprocessen är i grunden densamma eftersom OpenShift bygger på Kubernetes under huven.
Hur lång tid innan utgång bör ett certifikat förnyas?
Standardbeteendet förnyar vid två tredjedelars giltighetstid, vilket fungerar bra för de flesta. Behöver du mer kontroll kan du sätta renewBefore explicit, eller i cert-manager 1.21 och senare använda det nya renewal-fältet för finare styrning, inklusive underhållsfönster.
Kan DNS-01-utmaningen läcka information om mina interna domäner?
TXT-posterna som skapas för verifiering är publikt synliga i DNS så länge utmaningen pågår, vilket normalt är några sekunder till minuter. Det är sällan ett praktiskt problem, men är värt att känna till om du utfärdar certifikat för domännamn du annars håller interna.
Vad är skillnaden mellan att radera en Certificate-resurs och att låta den gå ut?
Att radera Certificate-objektet tar bort både resursen och den tillhörande secreten, men återkallar inte certifikatet hos utfärdaren. Att bara låta det gå ut innebär att secreten finns kvar men innehåller ett ogiltigt certifikat efter utgångsdatumet. I båda fallen bör du beställa ett nytt certifikat snarast, snarare än att försöka återanvända det gamla.
Är det dyrt eller riskabelt att byta certifikatutfärdare senare?
Nej, det är en av fördelarna med att lägga logiken i cert-manager från början. Att byta utfärdare handlar om att skapa en ny Issuer eller ClusterIssuer och uppdatera referensen i dina Certificate-objekt eller Ingress-annotationer. cert-manager hanterar själva utbytet av det gamla certifikatet mot det nya utan att du behöver skriva om applikationskonfigurationen, eftersom appen fortfarande bara pekar mot samma secret-namn som tidigare.




