Dokploy / Dokploy/dokploy

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

Open
#4,503 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug pr-open
Dominant language
TypeScript
Stars
37.4k
Forks
3k
Avg merge
1d 3h
Merged PRs (30d)
73

Description

To Reproduce
  1. Create an application in Dokploy and add a domain (e.g. example.com) with HTTPS: ON and Certificate: Let's Encrypt. Confirm it gets a valid LE cert.
  2. Go to Certificates (top-level) -> Add Certificate and paste a valid Cloudflare Origin CA certificate + private key covering example.com (and/or *.example.com). Save.
  3. Go back to the application -> Domains -> edit example.com and change Certificate to None. Save.
  4. Restart Traefik (docker restart $(docker ps -q -f name=traefik)) to be safe.
  5. From outside the server, inspect the cert served by the origin:
echo | openssl s_client -servername example.com -connect <ORIGIN-IP>:443 2>/dev/null \
  | openssl x509 -noout -issuer -dates
Current vs. Expected behavior

Expected: Traefik serves the uploaded Cloudflare Origin CA certificate via SNI for example.com.

Actual: Traefik continues to serve the previously-issued Let's Encrypt certificate (or fails over to the default cert). The uploaded cert is never presented, even though it appears in the Dokploy UI under Certificates.

Root cause: Dokploy writes uploaded certificates to:

/etc/dokploy/traefik/dynamic/certificates/<cert-id>/certificate.yml

But the Traefik file provider is configured as:

providers:
  file:
    directory: /etc/dokploy/traefik/dynamic
    watch: true

Traefik's file provider with directory: does not recurse into subdirectories, so certificates/<cert-id>/certificate.yml is never loaded. The cert PEM files exist on disk but are never registered with Traefik's TLS store, so SNI matching never picks them up.

Additionally, the websecure entrypoint has a default certResolver: letsencrypt, and router YAMLs generated by Dokploy include tls.certResolver: letsencrypt explicitly. Even after setting Certificate: None on the domain, the regenerated router YAML may still contain the resolver line, which takes precedence over SNI-matched certs.

Workaround that fixes it:

sudo cp /etc/dokploy/traefik/dynamic/certificates/<cert-id>/certificate.yml \
        /etc/dokploy/traefik/dynamic/zz-<name>-cert.yml
docker restart $(docker ps -q -f name=traefik)

Suggested fixes:

  1. Write the certificate.yml registration file directly into /etc/dokploy/traefik/dynamic/ (not a subdirectory), while keeping the PEM files wherever convenient.
  2. Or use Traefik's filename: provider entries pointing to the subdir paths.
  3. Ensure router YAMLs generated by Dokploy fully remove tls.certResolver when the domain is set to Certificate: None.
Provide environment information
- OS: Ubuntu 22.04 (also reproduced on 20.04)
- Arch: x86_64
- Dokploy version: 0.29.4
- VPS Provider: Hetzner
- Application: Next.js frontend (also affects Docker Compose services)
Which area(s) are affected? (Select all that apply)

Traefik

Are you deploying the applications where Dokploy is installed or on a remote server?

Same server where Dokploy is installed

Additional context

Followed the official docs at https://docs.dokploy.com/docs/core/domains/cloudflare (Using Cloudflare's Origin CA section).

Will you send a PR to fix it?

No

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing how Dokploy generates Traefik configuration under /etc/dokploy/traefik/dynamic/, especially certificates//certificate.yml and the generated router YAMLs. Reproduce with the provided openssl SNI check and inspect whether the file provider loads the certificate and whether tls.certResolver remains after Certificate: None. Done means the uploaded certificate is served for the domain without the unwanted resolver.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, typescript
Domain
devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.