Skip to content

Local TLS with mkcert

Browsers reject self-signed certificates. mkcert solves this by creating a local certificate authority you install once, then issuing certificates that chain up to it. Run kb tls mkcert-setup to handle the full setup: CA installation, certificate generation, Firefox trust, and the Kubernetes secret Traefik needs.

  • mkcert installed and on your PATH
  • kubectl configured against your local cluster
  • Firefox: either certutil (nss-tools package) or podman
Terminal window
kb tls mkcert-setup

The command runs four steps in sequence.

1. Install the CA in the system trust store

Section titled “1. Install the CA in the system trust store”

Runs mkcert -install. This adds the mkcert root CA to your OS certificate store and to any Chromium-based browser that uses it. If the CA is already installed, mkcert skips the step silently.

Firefox ignores the system trust store and reads its own NSS databases under ~/.mozilla/firefox. The command locates every profile directory that contains a cert9.db file and installs the CA into each one.

It tries two installation methods in order:

  • certutil — uses certutil -A directly if the binary is available (install nss-tools on Fedora/RHEL or libnss3-tools on Debian/Ubuntu).
  • Podman fallback — if certutil is not found, pulls registry.fedoraproject.org/fedora:latest, mounts your Firefox profiles and the mkcert CA root into the container, and runs certutil inside it. This works on systems where you have a container runtime but not the NSS tools installed natively.

If neither tool is available, the command prints a warning and continues. Chromium-based browsers will still trust the certificates; Firefox will show a warning until you install the CA manually.

Generates a single certificate covering:

  • *.k8s.local and k8s.local
  • *.k8s.lan and k8s.lan
  • *.lan
  • localhost
  • 127.0.0.1

The certificate and key are written to ~/.local/share/mkcert-k8s/tls.crt and tls.key. If those files already exist, the step is skipped — re-running mkcert-setup is safe.

Creates the traefik namespace if it does not exist, then applies a TLS secret named mkcert-wildcard into that namespace:

Terminal window
kubectl create secret tls mkcert-wildcard \
--cert=~/.local/share/mkcert-k8s/tls.crt \
--key=~/.local/share/mkcert-k8s/tls.key \
-n traefik

The create command runs through --dry-run=client -o yaml piped to kubectl apply -f -, so re-running is idempotent.

The local overlay patches the Traefik HelmRelease to set the mkcert-wildcard secret as the default TLS certificate:

tlsStore:
default:
defaultCertificate:
secretName: mkcert-wildcard

Traefik serves this certificate for every HTTPS connection that does not have a more specific certificate configured. No per-ingress annotation is needed — add an ingress under *.k8s.local and it gets TLS automatically.

The same overlay sets service.type: ClusterIP so Traefik does not acquire a MetalLB address. Reach services via mise run traefik, which runs a kubectl port-forward to localhost.

Delete the files in ~/.local/share/mkcert-k8s/ and run kb tls mkcert-setup again. The command will regenerate the certificate and overwrite the Kubernetes secret.

Terminal window
rm ~/.local/share/mkcert-k8s/tls.crt ~/.local/share/mkcert-k8s/tls.key
kb tls mkcert-setup