docs Domains & SSL
Domains & SSL
Route custom domains to your apps through Traefik, with automatic Let's Encrypt certificates.
Adding Domains to an Application
In the Domains tab of an application, click Add Domain and enter the hostname and the Container Port your app listens on. Adding or removing domains requires the Operator or Admin role on the project.
- An application can have as many domains as you like — root domains, subdomains, or both (example.com and www.example.com)
- Each domain has its own container port, so one container can serve different ports on different hostnames
- Hostnames must be valid FQDNs; they are lowercased automatically
- Domains added from the dashboard always use HTTPS
- Changes take effect on the next deploy — routing labels are set when the container is created, so redeploy after adding or removing a domain
DNS Configuration
Point each domain at the public IP of the server the app runs on, then add it in DeployKit:
# Root domain
A example.com → YOUR_SERVER_IP
# Subdomain
A api.example.com → YOUR_SERVER_IP
# or
CNAME api.example.com → example.comAutomatic SSL with Let's Encrypt
Traefik v3 obtains and renews a certificate for every HTTPS domain using the Let's Encrypt HTTP challenge. Plain HTTP requests on port 80 are redirected to HTTPS. For a certificate to be issued:
- The domain's DNS must already resolve to the server
- Ports 80 and 443 must be open — port 80 is required for the challenge even though traffic is then redirected
- Wildcard certificates (*.example.com) cannot be obtained with the HTTP challenge; each hostname gets its own certificate
How Routing Works
When a container is deployed, DeployKit attaches Traefik labels to it: one router and one service per domain, with each router bound explicitly to its own service. Traefik picks the labels up and routes the hostname to the right container and port. Nothing is exposed unless it has a domain.
- With a domain: no host port is published, and replicas are load-balanced by Traefik
- Without a domain: the app's port is published on the host and only one instance runs — replicas require a domain
- Maintenance mode answers on the app's domains, so it needs at least one
- Compose stacks have their own domains: each domain targets one service of the stack (and can add a path prefix)
Several domains returning 404? Before v0.5.0, an app or stack service with two or more domains produced Traefik routers that were not bound to a service, so Traefik dropped all of them (its log shows cannot be linked automatically with multiple Services) while the dashboard still showed the app as running. This is fixed, but labels are baked in when the container is created: redeploy any app or stack that already had several domains after upgrading.
The Dashboard's Own Domain
The panel is routed the same way. In production, set DOMAIN and ACME_EMAIL in your .env. The API answers on /api, /trpc, /socket.io and /ws of that host; everything else goes to the dashboard.
DOMAIN=panel.yourdomain.com
ACME_EMAIL=you@yourdomain.com
# WEB_URL is derived automatically as https://${DOMAIN}WEB_URL — the public URL of the instance, used for CORS, WebSockets and the GitHub App's webhook and callback URLs — is set to https://DOMAIN by docker-compose.prod.yml; you don't set it yourself. If you later change DOMAIN, a GitHub App registered earlier still points at the old URL and must be updated or registered again.