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=trueTraefik 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: 80cert-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.

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.



