Traefik TLS & Certificate Management#
Overview#
Dokploy uses Traefik as its reverse proxy and handles TLS at two levels: static configuration (entrypoints, ACME resolver definition) and dynamic configuration (per-router tls settings). Understanding the interaction between the entrypoint-level certResolver default and the per-router override is critical to debugging certificate behavior.
Static Configuration: Entrypoint TLS Default#
The websecure entrypoint is configured with a default certResolver: "letsencrypt" at the entrypoint level in production :
websecure:
http:
tls:
certResolver: letsencrypt
This means any router on websecure that does not explicitly set tls.certResolver will automatically use Let's Encrypt — including routers that set tls: {} with no resolver specified. This is a Traefik behavior: the entrypoint-level resolver acts as a fallback.
The ACME resolver itself is named letsencrypt, uses httpChallenge on the web entrypoint, and stores certificates in /etc/dokploy/traefik/dynamic/acme.json . The email is a placeholder (test@localhost.com) — this does not affect cert issuance but means renewal notices are not received.
The remote server config (getDefaultServerTraefikConfig) always enables the production resolvers and entrypoint default , regardless of NODE_ENV.
Per-Router certResolver: The Three Cases#
createRouterConfig() in domain.ts maps domain.certificateType to a router-level TLS config on the websecure entrypoint :
certificateType | Router tls field | Effective behavior |
|---|---|---|
letsencrypt | { certResolver: "letsencrypt" } | Explicit ACME via Let's Encrypt |
custom | { certResolver: "<domain.customCertResolver>" } | Named custom resolver |
none | tls: undefined (field omitted) | Falls back to entrypoint default — the websecure entrypoint-level certResolver: letsencrypt still applies |
Key implication: Setting
certificateType: "none"on a domain does not disable TLS certificate acquisition. Becausetlsis omitted from the router, the entrypoint-level default kicks in and Traefik will still attempt to obtain a Let's Encrypt certificate. The router remains onwebsecureand uses the entrypoint's resolver.
No ACME Cleanup on Certificate Type Change#
When a domain's certificateType is changed (e.g., from letsencrypt to custom or none), the domain.update tRPC mutation calls manageDomain() to rewrite the Traefik YAML config . This correctly updates the router's tls field.
However, there is no code that modifies acme.json at any point. The ACME storage file is only touched on startup (chmod to 600) and is written entirely by Traefik itself. When a domain stops referencing certResolver: letsencrypt, its certificate entry persists in acme.json indefinitely. Traefik stops renewing it (no router references the cert), but the entry is never cleaned up.
This applies equally to domain deletion. The domain.delete mutation removes the router from the YAML, but acme.json is left untouched .
Manual cleanup: edit /etc/dokploy/traefik/dynamic/acme.json, remove the domain's entry, then restart the Traefik container.
Dual-Router HTTPS Pattern#
When https=true and no customEntrypoint is set, Dokploy generates two routers for a domain :
{appName}-router-{key}onweb: only theredirect-to-https@filemiddleware; notlsblock.{appName}-router-websecure-{key}onwebsecure: all real middlewares + TLS config.
If customEntrypoint is set, a single router is created for that entrypoint . TLS is only added when the entrypoint is websecure or when customEntrypoint is set and https=true .
Key Files#
| File | Purpose |
|---|---|
packages/server/src/setup/traefik-setup.ts | Static config generation, ACME resolver, entrypoint defaults |
packages/server/src/utils/traefik/domain.ts | Per-domain router config, createRouterConfig(), manageDomain(), removeDomain() |
packages/server/src/utils/traefik/file-types.ts | HttpRouter type — tls.certResolver field definition |
packages/server/src/utils/traefik/types.ts | MainTraefikConfig — entrypoint-level tls.certResolver |
/etc/dokploy/traefik/dynamic/acme.json | Traefik-managed ACME certificate store (600 permissions required) |