# Deployment (/docs/deployment)



Ductor is a single statically-linked Go binary that needs two backing services:
**PostgreSQL** (TimescaleDB recommended) and **Redis or Dragonfly**. Everything
else — the API, the routing engine, the workflow coordinator, the workers — runs
inside that one binary, selected by `server.role`. There is no separate scheduler,
broker, or executor image to operate; you scale by running more copies of the same
binary and, if you want, splitting roles across fleets.

Pick the path that matches your platform:

<Cards>
  <Card title="Docker & Compose" href="/docs/deployment/docker">
    The container image, its ports and user, and the compose stack for local and
    small deployments. Prefer `make up` / `make up-full` so you never run a stale
    image.
  </Card>

  <Card title="Dokploy" href="/docs/deployment/dokploy">
    Deploy the Traefik-fronted production compose stack on Dokploy — the setup
    pattern that runs api.ductor.io.
  </Card>

  <Card title="Kubernetes (Helm)" href="/docs/deployment/kubernetes-helm">
    The `deploy/helm/ductor` chart — replicas, autoscaling, the migration hook,
    secrets, and the fail-closed enterprise production gate.
  </Card>

  <Card title="Workflow release pinning" href="/docs/deployment/release-pinning">
    Bind public triggers to their live caller release and drain compatible worker
    sets safely through rollout and rollback.
  </Card>

  <Card title="Production concerns" href="/docs/deployment/production">
    Migrations, auth lockdown, the master key, egress enforcement, resource
    sizing, and scaling the coordinator/worker model.
  </Card>
</Cards>

## The shape of a deployment [#the-shape-of-a-deployment]

```mermaid
flowchart LR
  ductor["Ductor binary<br/>API · routing · workflow · workers"]
  pg[("PostgreSQL")]
  redis[("Redis / Dragonfly")]
  vm["VictoriaMetrics stack<br/>(optional)"]
  ductor --> pg
  ductor --> redis
  ductor -.-> vm
```

| Component             | Purpose                                                   | Required |
| --------------------- | --------------------------------------------------------- | -------- |
| Ductor binary         | API + routing + workflow runtime + workers                | Yes      |
| PostgreSQL            | Durable state — runs, attempts, pools, rules, idempotency | Yes      |
| Redis / Dragonfly     | Cache, flow control, tiered queue, pub/sub                | Yes      |
| VictoriaMetrics stack | Metrics, logs, traces, alerting, Grafana                  | Optional |

## Configuration is uniform [#configuration-is-uniform]

However you deploy, configuration follows one rule: a YAML key becomes an env var
by uppercasing and swapping dots for underscores (`database.url` →
`DUCTOR_DATABASE_URL`). See the
[Configuration reference](/docs/reference/configuration). In Kubernetes the Helm
chart also supports injecting a whole YAML document via the `DUCTOR_CONFIG`
Secret — the surface for enterprise-only settings that shouldn't live in
individual env vars.

Because the same binary runs everywhere, the difference between a laptop and a
production cluster is entirely configuration: which backing services it points
at, which security controls are enabled, and how many replicas run. The
deployment paths above differ only in how they deliver that configuration.

## Two Helm postures [#two-helm-postures]

The Helm chart ships one switch that draws the line between the two worlds:

| Posture     | `production`      | What it means                                                                                                                                                                           |
| ----------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Development | `false` (default) | Chart-managed database/cache Secrets, development runtime profile, permissive defaults. For local and internal installs.                                                                |
| Enterprise  | `true`            | Fail-closed gate: rendering **fails** unless the environment is `production`, the security profile is `enterprise`, and every credential comes from pre-provisioned Kubernetes Secrets. |

Flipping `production=true` is not a tuning knob — it turns on a hard guard rail
that refuses to render a chart that isn't fully hardened. See
[Kubernetes (Helm)](/docs/deployment/kubernetes-helm) for the exact requirements
and [Production concerns](/docs/deployment/production) for the config that backs
them.

## After you ship [#after-you-ship]

Getting Ductor running is day one. The day-two concerns — migrations you run on
every upgrade, backups you rehearse, metrics and alerts you watch, key rotation
you plan for — live in [Operations](/docs/operations):

<Cards>
  <Card title="Database migrations" href="/docs/operations/migrations">
    Running `ductor migrate up` deliberately, and why auto-migrate stays off in
    production.
  </Card>

  <Card title="Backup & restore" href="/docs/operations/backup-restore">
    What to back up (Postgres is the source of truth), and rehearsing restores.
  </Card>

  <Card title="Observability" href="/docs/operations/observability">
    Metrics, logs, traces, and the alerts to wire before you need them.
  </Card>

  <Card title="Security & auth" href="/docs/operations/security">
    Locking down authentication and the API host allowlist.
  </Card>
</Cards>
