Securing NGINX-ingress via cert-manager

Article Image

I think today (2025-07-11) marks the day I finally cracked cert-manager and LetsEncrypt. I have been struggling with HTTPS warnings and hosting self-signed certs. Let me tell you, it has been a right pain in the ass.

With the confusion of using a Cloudflare tunnel to point to my ingress controller's internal IP, and setting all that up. I was scratching my head for a while until I saw this clip on YouTube. Where Travis Media starts talking about TXT records and DNS-01 challenges.

This challenge asks you to prove that you control the DNS for your domain name by putting a specific value in a TXT record under that domain name. It is harder to configure than HTTP-01, but it can work in scenarios that HTTP-01 can’t. It also allows you to issue wildcard certificates. After Let’s Encrypt gives your ACME client a token, your client will create a TXT record derived from that token and your account key, and put that record at _acme-challenge.<YOUR_DOMAIN>. Then Let’s Encrypt will query the DNS system for that record. If it finds a match, you can proceed to issue a certificate!

Lets get started...

Since my domain name 70ld.dev is hosted on Cloudflare let's set that up.

To use Cloudflare, you will need an API Token These are application-scoped keys bound to specific zones and permissions. To create your API Token they can be created at User Profile > API Tokens > API Tokens. The following settings are recommended:

Permissions

  • Zone - DNS - Edit
  • Zone - Zone - Read

Zone Resources

  • Include - All Zones

Now we have our key, encode it using base64 and create a secret.

apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token-secret
type: Opaque
data:
  api-token: <API Token>

Configure a Let's Encrypt ClusterIssuer

Now let's create a ClusterIssuer if you follow the instructions on cert-manager's site it goes through setting up Issuer but I want something cluster-wide as it's just my homelab. With the ClusterIssuer you can see we're using solvers.dns01 which will allow us to use the DNS01 Challenge.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-cloudflare-dns-issuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <email>
    privateKeySecretRef:
      name: letsencrypt-cloudflare-dns-issuer-secret
    solvers:
 - dns01:
          cloudflare:
            apiTokenSecretRef:
              name: cloudflare-api-token-secret
              key: api-token

Now we can deploy everything and move on to creating an ingress with TLS.

Deploy an ingress with TLS

With everything in place, it's time to test it out by deploying an ingress with tls. So we can get cert-manager to use the DNS resolver and create a cert for us. Here is an example I have used for PGAdmin.

You will need to add the annotation of:

annotations:
  cert-manager.io/cluster-issuer: '<ClusterIssuer Name>'

To tell the ingress to use the cluster issuer we created above. Then simply give the ingress some tls data

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: pgadmin-ingress
  namespace: pgadmin
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: 'letsencrypt-cloudflare-dns-issuer'
spec:
  ingressClassName: nginx
  rules:
 - host: pgadmin-prod.70ld.dev
      http:
        paths:
 - path: /
            pathType: Prefix
            backend:
              service:
                name: pgadmin-service
                port:
                  number: 5050
  tls:
 - hosts:
 - pgadmin-prod.70ld.dev
      secretName: pgadmin-tls

Once you have deployed everything, cert manager should do its job and provide a cert for that application. I have noticed it can take some time, but for me it works nicely. I now don't have to worry about exposing my services outside of my network and I get TLS certs.