Support for Cloudflare Origin CA certificates in Dokploy
What problem will this feature address?
Currently, Dokploy only supports Let's Encrypt for SSL certificates. When using Cloudflare in Full (Strict) mode, we need to install Cloudflare Origin CA certificates on the server. This process is manual and prone to breaking during Dokploy updates.
Describe the solution you'd like
I would like Dokploy to natively support Cloudflare Origin CA certificates alongside Let's Encrypt. This would allow users to:
- Upload/paste their Cloudflare Origin CA certificate and private key in the Dokploy UI
- Select "Cloudflare Origin CA" as a certificate option when configuring domains
- Automatically configure Traefik to use these certificates
- Maintain the configuration across Dokploy updates
Describe alternatives you've considered
I've researched the following potential alternatives, although I haven't implemented them personally:
-
Manual configuration: Configuring Traefik directly by modifying Docker service definitions and mounting certificate files. This approach seems functional but likely breaks during Dokploy updates and requires advanced technical knowledge.
-
Cloudflare Tunnels: This eliminates the need for origin certificates by establishing a secure tunnel between Cloudflare and the origin server. However, this adds complexity and potential dependencies on Cloudflare's infrastructure.
-
Using Full mode instead of Full (Strict): This is a simpler but less secure alternative that doesn't validate origin certificates. It doesn't provide the same security guarantees as Full (Strict) mode.
-
Custom deployment scripts: Creating scripts to reinstall certificate configurations after Dokploy updates. This would be a brittle solution requiring maintenance with each Dokploy release.
Additional context
Many users deploy their applications behind Cloudflare for enhanced security and performance. The Full (Strict) SSL mode in Cloudflare requires a trusted certificate on the origin server, and Cloudflare provides free Origin CA certificates for this purpose.
Currently, the workaround involves manually configuring Traefik and mounting certificate files, which is complex and breaks during updates.
Will you send a PR to implement it?
Maybe, need help
@soccerlover096 there is a dynamic folder already mounted in traefik so you can just do the following,
Save your certs in dir below on the host
/etc/dokploy/traefik/dynamic/certs/
tls:
certificates:
- certFile: /etc/dokploy/traefik/dynamic/certs/domain.cert
keyFile: /etc/dokploy/traefik/dynamic/certs/domain.key
Then save the above file at /etc/dokploy/traefik/dynamic/certs.yaml
That should be enough, check logs after in case of any errors, yes it isn't done within the ui, but it still works fine and is persistent, there is no need to alter any mounts
I am using cloudflare tunnels and am using the same approach as @djsisson, it works like a charm.
Just make sure in your service -> domain config in Dokploy that you set your domain to https, Certificate Provider to Custom, and Custom Certificate Resolver to file.
In cloudflare tunnels Origin Server Name should be your domain with subdomain if you have one and it points to https://localhost:443
The origin certs last for 15 years so this really won't need to be touched again
I am using cloudflare tunnels and am using the same approach as @djsisson, it works like a charm.
Just make sure in your service -> domain config in Dokploy that you set your domain to
https,Certificate ProvidertoCustom, andCustom Certificate Resolvertofile.In cloudflare tunnels Origin Server Name should be your domain with subdomain if you have one and it points to
https://localhost:443The origin certs last for 15 years so this really won't need to be touched again
Hello, I’m really glad to find someone else using Dokploy together with Cloudflare Tunnel, but I'm encountering an issue and hope to get some help.
I deployed Meilisearch using the provided template, and then created a tunnel using the Cloudflare-provided Docker image.
In the Cloudflare Tunnels dashboard, I set the public hostname to my domain, and the service address to the Meilisearch instance running in Dokploy, which is:
http://projectsname-meilisearch-gktdsr:7700
However, when I access the domain, I see this error:
502 Bad Gateway
Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared
When I checked the Cloudflare container logs, I found the following error:
2025-05-28T08:38:38Z ERR error="Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared: dial tcp: lookup projectsname-meilisearch-gktdsr on 127.0.0.11:53: no such host" connIndex=2 event=1 ingressRule=0 originService=http://projectsname-meilisearch-gktdsr:7700
I’ve confirmed that http://projectsname-meilisearch-gktdsr:7700 is reachable from other services/containers (for example, from my Next.js project).
@youyou-sudo that service name will only work if cloudflare is in the same network as that service
if it is running on the host or in a different network, it will not work
you instead just need to point it to your proxy, so localhost 80 if its running on the host or the proxy service name if it's in the same network as your proxy.
please note, if you have set up http->https redirects you will need to switch to using https localhost 443 and set the origin domain in your tunnel as well as ensure the cf origin certificate is being used by your proxy. otherwise you would get infinite redirect error
i will add, you can use that service name, if you used a host updater, basically it will edit your vps hosts file and map container names to ip's, but thats not usually used.
Here is a guide I wrote for myself copied and pasted from my Notion. This is a setup where you point your cloudflare tunnel to your traefik reverse proxy Cloudflare Tunnel → Dokploy Reverse Proxy → Service Container. In the guide I have an example dokploy service called coder and domain example.io.
HTTPS Setup with Traefik
1: Go to cloudflared and get an Origin Server cert as PEM format
- Navigate to your domain (
example.io) - Go to SSL/TLS → Origin Server
2: Save Certificate Files on Your Server
- Add
.crtand.keyfiles to/etc/dokploy/traefik/dynamic/certificates - Set permissions
sudo chmod 644 '/etc/dokploy/traefik/dynamic/certificates/exampleio.crt'
sudo chmod 600 '/etc/dokploy/traefik/dynamic/certificates/exampleio.key'
3: Update Traefik Config
- Remove lets encrypt from the main config, we will be using certs from cloudflare
providers:
swarm:
exposedByDefault: false
watch: true
docker:
exposedByDefault: false
watch: true
network: dokploy-network
file:
directory: /etc/dokploy/traefik/dynamic
watch: true
entryPoints:
web:
address: ':80'
websecure:
address: ':443'
http3:
advertisedPort: 443
# http:
# tls:
# certResolver: letsencrypt
api:
insecure: true
# # Disabled because I am using cloudflare origin certificates with cloudflared tunnels
# certificatesResolvers:
# letsencrypt:
# acme:
# email: [email protected]
# storage: /etc/dokploy/traefik/dynamic/acme.json
# httpChallenge:
# entryPoint: web
- Create a dynamic config for the new certificates
# /etc/dokploy/traefik/dynamic/certificates.yml
tls:
certificates:
- certFile: /etc/dokploy/traefik/dynamic/certificates/exampleio.crt
keyFile: /etc/dokploy/traefik/dynamic/certificates/exampleio.key
stores:
- default
stores:
default:
defaultCertificate:
certFile: /etc/dokploy/traefik/dynamic/certificates/exampleio.crt
keyFile: /etc/dokploy/traefik/dynamic/certificates/exampleio.key
- Update middleware
# /etc/dokploy/traefik/dynamic/middlewares.yml
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
permanent: true
4: Set Service Domain
- Add the sub domain to the service in dokploy as https
- Set
Certificate ProvidertoCustom - Set
Custom Certificate Resolvertofile
- Set
- Add the sub domain to cloudflared tunnel online
Hostname Config:
subdomain: coder
domain: example.io
path: none
Service:
type: https
URL: localhost:443 ← Change from port 80 to 443
Additional application settings:
TLS → Origin Server Name: coder.example.io ← THIS IS IMPORTANT or get 502 Bad Gateway. THIS MUST ALSO MATCH DOKPLOY DOMAIN SETTING
5: Restart services
# Restart Traefik to pick up new certificates
docker restart $(docker ps -q --filter "name=traefik")
# Redeploy your coder service in Dokploy (this will apply the HTTPS labels)
Requires manaual re-deploy
6: Verify deploy
View logs
docker logs $(docker ps -q --filter "name=traefik") 2>&1 | grep -i cert
Check cert
openssl s_client -connect localhost:443 -servername coder.example.io
That's it!
Here is a guide I wrote for myself copied and pasted from my Notion. This is a setup where you point your cloudflare tunnel to your traefik reverse proxy
Cloudflare Tunnel → Dokploy Reverse Proxy → Service Container. In the guide I have an example dokploy service calledcoderand domainexample.io.HTTPS Setup with Traefik
.....
docker logs $(docker ps -q --filter "name=traefik") 2>&1 | grep -i cert Check cert
openssl s_client -connect localhost:443 -servername coder.example.io That's it!
This is actually really helpful, it worked for me too i was struggling with the certificates and traefik config for days, i hope you guys find some easier way, besides that... great work 💪💪
+1 to have this integrated into the Dokploy UI somehow 😊
@moorej2400 Could you kindly share a sample compose file of how the traefik labels look like with this cloudflare tunnel setup?
@moorej2400 Could you kindly share a sample compose file of how the traefik labels look like with this cloudflare tunnel setup?
Dokploy Domain config
traefik labels are autogenerated by Dokploy, you can see your config like this if you click on preview compose. You DO NOT need to add these manually, they will be injected by configuring your domain properly as in the screenshot.
version: '3.8'
services:
n8n:
image: docker.n8n.io/n8nio/n8n:1.97.1
restart: always
environment:
- N8N_HOST=${N8N_HOST}
- N8N_PORT=${N8N_PORT}
- N8N_PROTOCOL=http
- NODE_ENV=production
- WEBHOOK_URL=https://${N8N_HOST}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- N8N_SECURE_COOKIE=false
- N8N_RUNNERS_ENABLED=true
volumes:
- n8n_data-jproduction-n8n-sgfv2h:/home/node/.n8n
networks:
- jproduction-n8n-sgfv2h
labels:
- traefik.http.routers.jproduction-n8n-sgfv2h-4-web.rule=Host(`n8n.example.io`)
- traefik.http.routers.jproduction-n8n-sgfv2h-4-web.entrypoints=web
- traefik.http.services.jproduction-n8n-sgfv2h-4-web.loadbalancer.server.port=5678
- traefik.http.routers.jproduction-n8n-sgfv2h-4-web.service=jproduction-n8n-sgfv2h-4-web
- traefik.http.routers.jproduction-n8n-sgfv2h-4-web.middlewares=redirect-to-https@file
- traefik.http.routers.jproduction-n8n-sgfv2h-4-websecure.rule=Host(`n8n.example.io`)
- traefik.http.routers.jproduction-n8n-sgfv2h-4-websecure.entrypoints=websecure
- traefik.http.services.jproduction-n8n-sgfv2h-4-websecure.loadbalancer.server.port=5678
- traefik.http.routers.jproduction-n8n-sgfv2h-4-websecure.service=jproduction-n8n-sgfv2h-4-websecure
- traefik.http.routers.jproduction-n8n-sgfv2h-4-websecure.tls.certresolver=file
- traefik.enable=true
volumes:
n8n_data-jproduction-n8n-sgfv2h: null
networks:
jproduction-n8n-sgfv2h:
name: jproduction-n8n-sgfv2h
external: true
@moorej2400 Thanks. Finally got my apps working
Does this mean that following the guide for Cloudflare's Strict mode in the docs will simply not work? https://docs.dokploy.com/docs/core/domains/cloudflare#assign-a-domain-full-strict
@MerlinB you can use strict
- install cf origin domain cert or use dns challenge in traefik
- change to HTTPS localhost:443 in tunnel entry
- set origin domain in tls settings for each https entry, can also turn on http2
- use https in your domains
Optional If you want to close 80/443 ports on your host Put both cloudflared and traefik in same docker network, no host mode. Set tunnel entries to use traefik service name not localhost (dokploy-traefik) Remove port bindings in traefik or set to private network ip. Run cloudflare warp locally and turn on split tunnels for it, set so your private network subnet is sent via cloudflare You can now access your host from local using its private network ip Means you do not need any ssh entries in your tunnel, can just ssh your network ip As well as avoid any 413 errors with >100mb layers in registries when pushing to your registry if you use cf warp as part of your workflow
I found a way to make HTTPS work with Cloudflare's Full (Strict) mode using Dokploy and Traefik, without using Let's Encrypt or exposing ports manually.
This setup uses manually installed Cloudflare origin certificates (domain.cert + domain.key) as @djsisson suggested.
These files need to be placed inside the Traefik container file system. Since Dokploy’s UI doesn’t currently allow uploading files into the Traefik file system, for certificates.yaml, domain.cert and domain.key you’ll need to create them manually (e.g., via docker cp, bind mounts, or by rebuilding your container image).
The domain.cert and domain.key are the TLS certificate signed by Cloudflare that you can generate for free.
# /etc/dokploy/traefik/traefik.yml
global:
sendAnonymousUsage: false
providers:
swarm:
exposedByDefault: false
watch: true
docker:
exposedByDefault: false
watch: true
network: dokploy-network
file:
directory: /etc/dokploy/traefik/dynamic
watch: true
entryPoints:
web:
address: ':80'
websecure:
address: ':443'
http3:
advertisedPort: 443
# http:
# tls:
# certResolver: letsencrypt
api:
insecure: true
# certificatesResolvers:
# letsencrypt:
# acme:
# email: [email protected]
# storage: /etc/dokploy/traefik/dynamic/acme.json
# httpChallenge:
# entryPoint: web
# /etc/dokploy/traefik/dynamic/middlewares.yml
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
permanent: true
# /etc/dokploy/traefik/dynamic/certificates/certificates.yaml
tls:
certificates:
- certFile: /etc/dokploy/traefik/dynamic/certificates/domain.cert
keyFile: /etc/dokploy/traefik/dynamic/certificates/domain.key
⚙️ Routing your apps (the trick)
Finally, for dokploy.yml there is a bit of a trick. I had to create routers and services for each application I wanted to expose with HTTPS enabled, it means that, if you want, for example, to have dokploy.yourdomain.com, subdomain1.yourdomain.com, subdomain2.yourdomain.com, etc, you need to create a router and a service for every single one of them.
I did the creation of routers and services all inside the dokploy.yml but you can separate them on many files in case you want to organize it better. For example, instead of having the subdomain1-router-app and subdomain1-service-app inside the dokploy.yml I could've create a file called subdomain1.yml and it would still work fine.
This step bypasses the need for the Domains tab in Dokploy UI, as long as you configure routes manually in .yml files inside /etc/dokploy/traefik/dynamic. It means that your 'Domains' tab inside the service will be useless, you must rely strictly on your routers-services configured by the yml files.
# /etc/dokploy/traefik/dynamic/dokploy.yml
http:
routers:
dokploy-router-app:
rule: Host(`dokploy.yourdomain.com`) && PathPrefix(`/`)
service: dokploy-service-app
entryPoints:
- websecure
tls: {}
subdomain1-router-app:
rule: Host(`subdomain1.yourdomain.com`) && PathPrefix(`/`)
service: subdomain1-service-app
entryPoints:
- websecure
tls: {}
services:
dokploy-service-app:
loadBalancer:
servers:
- url: http://dokploy:3000
passHostHeader: true
subdomain1-service-app:
loadBalancer:
servers:
- url: http://subdomain1-container:8080
passHostHeader: true
Tips below:
The "- url" under "servers:" refers to the internal address of the container inside the Docker network used by Traefik. So, for this line:
subdomain1-service-app:
loadBalancer:
servers:
- url: http://subdomain1-container:8080
passHostHeader: true
You're telling Traefik:
"When a request comes for subdomain1.yourdomain.com, forward it to a container reachable as subdomain1-container on port 8080."
The hostname (like subdomain1-container) must match the Docker service name or container name.
IMPORTANT: The container (like subdomain1-container) must be sharing at least one network connection with the Traefik container otherwise Traefik won't be able to resolve the http://subdomain1-container:8080 internally and probably will show Bad Gateway on your browser when accessing subdomain1.yourdomain.com.
Also, the port (like 8080 for subdomain1-container) must be the one that is listening on internally (not the host-exposed port).
is there any simple way to use Cloudflare API instead of Manually generating Cloudflare Origin CA certificates and insert Dokploy + Treafik ? (That are proven and solid working)
- token with Zone:DNS:Edit permissions
- Scope to specific zones
# /etc/dokploy/traefik/traefik.yml
global:
sendAnonymousUsage: false
providers:
docker:
exposedByDefault: false
watch: true
network: dokploy-network
file:
directory: /etc/dokploy/traefik/dynamic
watch: true
entryPoints:
web:
address: ':80'
websecure:
address: ':443'
http:
tls:
certResolver: cloudflare
certificatesResolvers:
cloudflare:
acme:
email: [email protected]
storage: /etc/dokploy/traefik/dynamic/acme.json
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 30
# In Dokploy
environment:
- CF_DNS_API_TOKEN=your_cloudflare_dns_token
As a note for future readers: You can upload certificates directly via /dashboard/settings/certificates and change etc/dokploy/traefik/dynamic/dokploy.yml via /dashboard/traefik:
# /etc/dokploy/traefik/dynamic/dokploy.yml
http:
routers:
dokploy-router-app:
rule: Host(`dokploy.docker.localhost`) && PathPrefix(`/`)
service: dokploy-service-app
entryPoints:
- web
middlewares:
- redirect-to-https
dokploy-router-app-secure:
rule: Host(`example.com`)
service: dokploy-service-app
entryPoints:
- websecure
tls:
certResolver: {} # <--- do this
services:
dokploy-service-app:
loadBalancer:
servers:
- url: http://dokploy:3000
passHostHeader: true