← All articles

ENGINEERING · AI VM SETUP BLUEPRINT

Private apps, public certificates: two Traefik controllers and two ways to issue TLS

A browser-trusted certificate does not require a publicly accessible application. Domain validation and application traffic can take different paths. Our VM Blueprint uses that distinction to give private services HTTPS without routing public requests to their backends.

Application traffic
Internet → public Traefik → public apps
Operator → VPN → private Traefik → private apps
HTTP-01: public validation, private serving
Certificate authority → public IP :80 → public Traefik
  → temporary ACME solver only
cert-manager → TLS Secret → private Ingress
VPN client → private Traefik → private app
DNS-01: validation through DNS
Certificate automation → DNS API → public TXT record
Certificate authority → authoritative DNS → validation
Issued certificate → private Traefik → private app

Two controllers, two explicit classes

The public Traefik controller handles internet-facing routes. A second Traefik controller serves private routes over the VPN. Each watches its own explicit ingress class; neither class is the default. The deployment also checks provider filtering so another enabled route API cannot bypass the split.

The private backend has no application route on the public controller. Network policies and checks for alternate access paths support that boundary. Merely hiding a hostname in DNS would not protect it: an external client can send the hostname directly to a public IP.

Option 1: HTTP-01 on the public controller

For a name such as crm.int.example.com, public DNS points to the public address. Let’s Encrypt validates HTTP-01 on port 80 at /.well-known/acme-challenge/<token>. cert-manager creates a temporary solver route on the public ingress class. That route serves the challenge response, not the private application.

A standalone Certificate resource requests the certificate using an issuer whose HTTP-01 solver explicitly selects the public class. The private Ingress references the resulting TLS Secret in its own namespace. The private Traefik controller serves the certificate without owning an ACME resolver.

After validation, the temporary challenge route is removed. Public requests for the private application still have no matching application route. In this baseline, public HTTP returns the catch-all 404; public HTTPS can fail certificate validation because the public controller does not serve that private hostname’s certificate. Neither response should contain private application content.

The same hostname, a different address inside the VPN

VPN clients resolve crm.int.example.com to the tunnel-side address using a hosts entry or a resolver rule scoped to int.example.com. The browser keeps the same hostname, so the certificate remains valid even though the connection reaches a different address.

The public DNS record must continue pointing to the public challenge endpoint for HTTP-01 renewals. Replacing it with the VPN address may leave today’s certificate working while breaking its next renewal. Check published IPv6 records too: validation must reach the solver through the addresses you advertise.

Using a dedicated internal subdomain lets the resolver override affect private names without diverting all public DNS lookups. The VPN transport, client resolver configuration and certificate issuance each have a separate job.

Option 2: DNS-01 through an automated DNS API

DNS-01 proves control by publishing a TXT record at _acme-challenge.crm.int.example.com. The certificate authority checks authoritative public DNS; it does not need an inbound connection to the private app or a public HTTP challenge route. DNS-01 also supports wildcard certificates.

Automated renewal needs a supported DNS API integration, directly or through a delegated challenge zone. Use narrowly scoped credentials, account for propagation and record how credentials are rotated. Manually adding TXT records is not a durable unattended-renewal setup.

The blueprint’s alternative example uses Traefik’s own ACME DNS-01 resolver. cert-manager can also own DNS-01 issuance and write a TLS Secret. Pick one owner per hostname: do not configure Traefik and cert-manager to independently manage the same certificate. Back up the selected owner’s recovery-critical state and secret references.

Choosing between the two

HTTP-01 is the blueprint’s simpler default when a public challenge endpoint is available: it does not require DNS-writing credentials in the cluster. The trade-off is preserving the public DNS and port-80 validation path even though the application itself is private.

DNS-01 suits environments where public HTTP validation is unavailable or unwanted, or wildcard issuance is needed. It exchanges that network dependency for DNS API permissions and automation. With either method, a publicly trusted certificate does not make a hostname confidential: certificate names may appear in public Certificate Transparency logs.

Prove the route that must not exist

Test through the VPN and confirm the expected private response and valid certificate. Then test from outside the VPN, forcing the private hostname to each public address with the correct Host header and TLS SNI. The private response must never appear. Repeat with forged forwarding headers and inspect alternate ports and interfaces.

A 404 alone is insufficient if it comes from the private application. Compare response identity as well as status, and test controller class isolation. A wrong ingress class can expose a backend, so controller separation needs configuration checks and workload-level access controls.

Finally, verify certificate issuance and renewal behavior. An app that works today is not proof that its DNS credentials, challenge routing and resolver overrides will still work at renewal or after a rebuild.

Technical references