Skip to content

fix(traefik): load uploaded custom certificates from top-level dynamic dir#4595

Open
ravindu0823 wants to merge 2 commits into
Dokploy:canaryfrom
ravindu0823:fix/traefik-load-custom-certificates
Open

fix(traefik): load uploaded custom certificates from top-level dynamic dir#4595
ravindu0823 wants to merge 2 commits into
Dokploy:canaryfrom
ravindu0823:fix/traefik-load-custom-certificates

Conversation

@ravindu0823

Copy link
Copy Markdown

What

Uploaded custom certificates (e.g. Cloudflare Origin CA) were never presented by Traefik over SNI, even though they appeared in the Dokploy UI under Certificates.

Root cause

The per-certificate Traefik registration YAML was written into a subdirectory:

/etc/dokploy/traefik/dynamic/certificates/<certificatePath>/certificate.yml

But Traefik's file provider is configured with directory: /etc/dokploy/traefik/dynamic and watch: true. The file.directory provider is non-recursive, so it never reads files nested in subdirectories. As a result the cert YAML was never loaded into Traefik's TLS store and SNI matching never picked it up.

Fix

  • Add an exported pure helper getCertificateConfigPath(dynamicTraefikPath, certificatePath) that returns the registration YAML path at the top level of the dynamic Traefik directory: <dynamicTraefikPath>/<certificatePath>-certificate.yml.
  • createCertificateFiles now pulls DYNAMIC_TRAEFIK_PATH from paths() and writes the YAML to the top-level path. The PEM files (chain.crt / privkey.key) stay in the per-certificate subdirectory — the YAML references them by absolute path, so this keeps working. The subdir mkdir for PEMs is unchanged.
  • removeCertificateById now also deletes the top-level YAML (rm -f for remote servers, fs.rmSync if it exists for local) in addition to removing the per-cert subdirectory.

Test

New test apps/dokploy/__test__/traefik/certificate.test.ts covers the getCertificateConfigPath contract: the path is top-level, is not under a certificates/ subdirectory, and is unique per certificatePath (expected values built with path.join to avoid Windows separator flakiness).

RED (before adding the helper): getCertificateConfigPath is not a function — 3 failed.

GREEN (after fix):

 ✓ __test__/traefik/certificate.test.ts (3 tests) 3ms
 Test Files  1 passed (1)
      Tests  3 passed (3)

Both typechecks clean: pnpm --filter=@dokploy/server run typecheck and pnpm --filter=dokploy run typecheck.

Fixes #4503

🤖 Generated with Claude Code

…c dir

Traefik's file.directory provider (directory: /etc/dokploy/traefik/dynamic,
watch: true) is non-recursive, so the per-certificate registration YAML written
to certificates/<certificatePath>/certificate.yml was never loaded and uploaded
custom certificates were never presented over SNI.

Write the registration YAML to the TOP LEVEL of the dynamic Traefik directory
as <certificatePath>-certificate.yml via a new exported pure helper
getCertificateConfigPath. The PEM files (chain.crt/privkey.key) stay in the
per-certificate subdirectory since the YAML references them by absolute path.
removeCertificateById now also deletes the top-level YAML in addition to the
subdirectory.

Fixes Dokploy#4503

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ravindu0823 ravindu0823 requested a review from Siumauricio as a code owner June 9, 2026 09:09
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jun 9, 2026
Address review: certificatePath is user-supplied (apiCreateCertificate does not
omit it), so interpolating it unquoted into the remote shell commands widened a
pre-existing injection/word-splitting surface. Quote ${certDir} and ${configFile}
in the create (mkdir) and remove (rm) commands, matching the already-quoted PEM
path redirects.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Uploaded custom certificates are not loaded by Traefik because file provider does not recurse into certificates/ subdirectory

1 participant