Dokploy
Deploy the Traefik-fronted production compose stack on Dokploy.
Ductor ships a Dokploy-flavored compose file, docker-compose.dokploy.yml, that
runs the production image behind Dokploy's Traefik ingress. It serves the
REST/Connect API and the Scalar docs on :8080 and exports OTLP traces to
VictoriaTraces; Traefik routes the public hostnames and issues TLS. The
hostnames below (api.ductor.io, docs.ductor.io, …) are the reference
deployment's — treat them as examples and substitute your own.
What's different from the dev compose
- The
ductorservice builds theproductiontarget (notdevelopment). - Passwords come from Dokploy environment variables, not hardcoded defaults.
- The full VictoriaMetrics observability stack is included and tracing is on.
- The service joins both the internal
ductornetwork and Dokploy'sdokploy-network(with a stableductor-apialias) so Traefik and in-cluster callers can route to it.
The single :8080 surface
The API listener serves everything on :8080:
| Path | Serves |
|---|---|
/docs | Scalar API reference UI |
/openapi.yaml | The OpenAPI spec |
/health | Liveness endpoint (used by the container healthcheck) |
The ductor service command and key configuration:
# Overrides the baked Dockerfile CMD to drop the removed --grpc-addr flag —
# the current `serve` command no longer defines it (gRPC rides Connect on :8080).
command: ["serve", "--http-addr=:8080", "--metrics-addr=:9090"]
environment:
DUCTOR_DATABASE_URL: postgres://ductor:${POSTGRES_PASSWORD}@timescale:5432/ductor?sslmode=disable
DUCTOR_CACHE_URL: redis://:${DRAGONFLY_PASSWORD}@dragonfly:6379
DUCTOR_API_ENABLED: "true"
DUCTOR_API_ADDR: ":8080"
DUCTOR_API_ALLOWED_HOSTS: "localhost,127.0.0.1,::1,0.0.0.0,api.ductor.io,docs.ductor.io,ductor-api"
DUCTOR_CONNECTOR_ENCRYPTION_KEY: ${CONNECTOR_ENCRYPTION_KEY}
DUCTOR_ROUTER_QUEUE_ENABLED: "true"
DUCTOR_ROUTING_DAG_BRIDGE_ENABLED: "true"
DUCTOR_AGGREGATOR_BACKEND: "redis"
DUCTOR_TRACING_ENABLED: "true"
DUCTOR_TRACING_ENDPOINT: "victoriatraces:4317"
DUCTOR_TRACING_GRPC: "true"
networks: [ductor, dokploy-network]allowed_hosts must include your domains and loopback
DUCTOR_API_ALLOWED_HOSTS is a DNS-rebinding allowlist with exact-match
semantics — requests whose Host header isn't on it are rejected. It must
include every hostname Traefik forwards and loopback
(localhost/127.0.0.1), because the container's healthcheck hits
http://localhost:8080/health. Drop loopback and the container is marked
unhealthy and Traefik stops routing to it.
Required environment
Provide these as Dokploy environment variables (they come from the Dokploy Environment tab and are never committed):
| Variable | Purpose |
|---|---|
POSTGRES_PASSWORD | TimescaleDB password (used in DUCTOR_DATABASE_URL) |
DRAGONFLY_PASSWORD | Dragonfly password (used in DUCTOR_CACHE_URL) |
CONNECTOR_ENCRYPTION_KEY | Base64 32-byte AEAD master key for connector credentials |
GF_ADMIN_PASSWORD | Grafana admin password |
DUCTOR_AUTH_API_KEYS | DB/static API keys, if you enable key auth (optional) |
DUCTOR_VERSION | Image version tag (optional; defaults to prod) |
The reference deployment boots app-open (DUCTOR_AUTH_ENABLED defaults to
false); lock it down with Traefik basic-auth in front, or switch to OIDC /
DB-backed API keys via the auth env vars. For a production posture, follow the
production checklist.
Deploy steps
Create the app in Dokploy from the repository, selecting Docker Compose
and docker-compose.dokploy.yml.
Set the environment variables above.
Add domains in Dokploy for the ductor service (e.g. your API host),
which provisions Traefik routing and TLS. Ensure those hosts are in
DUCTOR_API_ALLOWED_HOSTS.
Deploy. The compose file sets DUCTOR_DATABASE_AUTO_MIGRATE: "true", so
the schema migrates on first boot. For stricter control, set it to false
and run ductor migrate up as a one-off (see
Migrations).
Verify by hitting https://<your-domain>/health and the Scalar reference
at https://<your-domain>/docs.
Backups and observability
Dokploy can schedule Postgres backups of the timescale service directly. The
compose file also brings up the full observability stack (Grafana on :3000),
so metrics, logs, and traces are available out of the box — see
Observability and
Backup & restore.