DarDevOps ·DarDev Team · 7 min read

Ingress and TLS with cert-manager on Kubernetes

Terminate HTTPS on k3s with ingress-nginx or Traefik and cert-manager plus Let's Encrypt—HTTP-01 vs DNS-01, OVH VPS wiring, and renewal failure fixes we use in production.

Kubernetes ingress controller terminating TLS with cert-manager certificates

Ingress and TLS with cert-manager means your Kubernetes cluster terminates HTTPS at the edge: an Ingress controller routes traffic to Services, and cert-manager obtains and renews Let's Encrypt certificates automatically. On DarDevOps stacks—k3s on OVH VPS hosts operated from Tunis—we pair ingress-nginx (or Traefik) with cert-manager so every public hostname gets valid TLS without manual copy-paste from a registrar panel.

This guide targets self-hosted clusters where you control DNS (GoDaddy, Cloudflare, or OVH) and open ports 80 and 443 on the VPS. We assume you already chose k3s or a lightweight distribution; if you are still deciding between managed control planes and VPS economics, read our managed vs self-hosted K8s comparison first.

Stack layout on a single OVH VPS

A typical DarDev production VPS runs k3s with the bundled Traefik disabled when we standardize on ingress-nginx, cert-manager in its own namespace, and workloads in application namespaces. Host nginx is optional: some teams terminate TLS twice (host nginx → ingress) for legacy reasons; we prefer a single TLS termination at Ingress unless a non-Kubernetes service shares the same public IP.

  • k3s single-node or three-node on OVH (Gravelines or Strasbourg regions are common for MENA latency)
  • ingress-nginx Helm chart with Service type LoadBalancer or NodePort plus host firewall rules
  • cert-manager v1.x with ClusterIssuer for Let's Encrypt production and staging
  • DNS A records pointing app subdomains to the VPS public IP (PTR set to your mail/MTA hostname if you also run SMTP)

Tunisia-based teams often host in EU datacenters for peering and support hours while keeping data residency decisions documented for B2B contracts—ingress TLS is one layer; policy sits above it.

Install ingress-nginx and cert-manager

On k3s, disable the default Traefik if you want nginx: install with --disable traefik or pass the equivalent in your k3s config. Add ingress-nginx via Helm, publish HTTP/HTTPS on 80/443, and confirm an external IP or node port responds before touching certificates.

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
 -n ingress-nginx --create-namespace

helm install cert-manager jetstack/cert-manager \
 -n cert-manager --create-namespace \
 --set crds.enabled=true

Traefik remains a valid choice—especially if you already run it on k3s defaults or need middleware-heavy routing. cert-manager integrates the same way: annotate Ingress or create Certificate resources; the controller brand only changes the ingressClassName you reference.

HTTP-01 vs DNS-01: which challenge to use

Let's Encrypt validates domain control through ACME challenges. HTTP-01 serves a token on http://your-host/.well-known/acme-challenge/... and is the fastest path when port 80 reaches ingress-nginx and each hostname resolves to that cluster. Use it for public web apps on dedicated subdomains (crm.example.com, mail.example.com).

DNS-01 creates a _acme-challenge TXT record. Choose DNS-01 when you need wildcard certificates (*.apps.example.com), when port 80 is blocked, when the app is internal but DNS is public, or when multiple clusters share a domain and only DNS can prove ownership. On GoDaddy-managed zones we store API credentials in a Kubernetes Secret and reference a cert-manager DNS01 webhook or native provider configuration—rotate API keys on the same cadence as cloud IAM tokens.

ClusterIssuer and Ingress example

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
 name: letsencrypt-prod
spec:
 acme:
 email: ops@example.com
 server: https://acme-v02.api.letsencrypt.org/directory
 privateKeySecretRef:
 name: letsencrypt-prod-account-key
 solvers:
 - http01:
 ingress:
 class: nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
 name: api
 annotations:
 cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
 ingressClassName: nginx
 tls:
 - hosts: [api.example.com]
 secretName: api-tls
 rules:
 - host: api.example.com
 http:
 paths:
 - path: /
 pathType: Prefix
 backend:
 service:
 name: api
 port:
 number: 80

cert-manager creates or updates the api-tls Secret before ingress-nginx loads the certificate. For GitOps workflows, commit Issuer and Certificate manifests to git and let Flux reconcile—same pattern we describe in GitOps lessons, so TLS never depends on a one-off kubectl apply from a laptop in a café.

Renewal failures and how we debug them

Certificates renew automatically about 30 days before expiry. When they do not, start with kubectl describe certificate,certificaterequest,order,challenge -n <namespace> and cert-manager controller logs. These failures show up repeatedly on VPS clusters:

  • HTTP-01 timeout: firewall on OVH security group or ufw still blocking port 80, or DNS A record points at an old IP after VPS migration
  • Wrong ingress class: Ingress uses traefik but Issuer references nginx (or vice versa)
  • Rate limits: too many failed orders against Let's Encrypt production—switch to staging, fix, then wait for backoff
  • DNS-01 TXT not visible: TTL too high, typo in zone, or API token lacks edit permission on the subdomain
  • Clock skew: NTP drift on the VPS causes ACME auth errors—chrony must be healthy

Set Prometheus alerts on cert-manager certificate expiration metrics or run a weekly cron that checks secret notAfter dates. An expired cert on Friday night is avoidable; tie alerts to the same on-call channel you use for deploy rollbacks.

Hardening and operational habits

Force HTTPS with ingress annotations (ssl-redirect) and HSTS only after you confirm all clients handle TLS—internal health checks may still use HTTP inside the cluster mesh. Restrict who can create Ingress resources in production namespaces; a malicious Ingress can request a valid cert for a hostname if DNS already points at you. Pair RBAC with OPA or Kyverno if multiple teams share the cluster.

Document every hostname, Issuer, and DNS owner in a runbook next to your CI pipeline notes—teams that automate deploys with GitLab CI but leave TLS manual tend to rediscover expiry during demos. Right-size ingress controller CPU and memory; TLS termination is not free on small VPS instances, and starving the controller looks like random 502s under load.

Diagram of ingress controller, cert-manager, and Let's Encrypt ACME flow
HTTP-01 flows through ingress; DNS-01 updates your registrar zone directly.

DarDevOps applies this ingress plus cert-manager baseline on internal stacks (CRM, mailer, news) and client clusters before adding service mesh or multi-region complexity. That is Deployed Realities: TLS we operate daily on OVH-hosted k3s, not a slide-deck checklist.

Need hands-on help wiring ingress, GitOps, and observability on a Tunisian or MENA deployment? See dardev.net/products for DarDevOps engagements—we start from working HTTPS and documented renewals, then grow into full pipeline automation.

ingress-nginx or Traefik on k3s?

Both work with cert-manager. We disable k3s default Traefik when standardizing on ingress-nginx for broader community examples; keep Traefik if you already invested in its middleware CRDs. Pick one controller per cluster edge.

When must I use DNS-01 instead of HTTP-01?

Use DNS-01 for wildcard certs, internal-only services with public DNS names, or when port 80 cannot reach the cluster. HTTP-01 is simpler for single-hostname public web apps.

Why did my certificate stay in Pending?

Check Challenge objects: wrong DNS, blocked port 80, mismatched ingressClassName, or stale orders after repeated failures. Fix the root cause, delete stuck CertificateRequest resources if needed, and let cert-manager retry.

Does Let's Encrypt work from an OVH VPS in France for a.tn business?

Let's Encrypt validates domain control, not company country. Your legal data-residency obligations are separate—document where the VPS and backups live for customer contracts.

Can I reuse the same cert across namespaces?

Secrets are namespace-scoped. Use cert-manager Certificate in each namespace, or copy/sync the tls Secret with a tool like Reflector. One Certificate per namespace keeps RBAC boundaries clear.

Get company news

Releases and announcements — confirm from your inbox.

Subscribe to updates