Guides

Run Ductor locally

Bring up Ductor with Docker Compose, apply migrations, and make your first API call.

This guide stands up a complete local Ductor stack — TimescaleDB, Dragonfly, and the Ductor binary — using the repository's docker-compose.yml, then verifies the API is healthy and serving.

This page is the environment reference; Getting Started is the journey

This guide covers bring-up mechanics and environment detail — service names, ports, DUCTOR_* variables, the compose stack, and the bare-binary path. If you want the guided walkthrough that seeds a tenant, defines a workflow, and proves durable execution by crashing a run, start with Getting Started.

Prerequisites

  • Docker and Docker Compose v2 (docker compose, not the legacy docker-compose)
  • The Ductor repository checked out locally

Bring up the core stack

The compose project defines three core services:

ServiceImagePurposeHost port
ductorductor:dev (built from docker/Dockerfile, development target)The application8080, 50051, 9090
timescaletimescale/timescaledb:2.25.0-pg16PostgreSQL + TimescaleDB5433 → 5432
dragonflydragonflydb/dragonfly:v1.39.0Redis-compatible cache / queue / flow6379

Service names, not host names

The Postgres service is named timescale and the Redis-compatible service is named dragonfly — there is no service called postgres or redis. Inside the compose network, Ductor reaches them at timescale:5432 and dragonfly:6379.

Start everything:

docker compose up

Compose wires the two connection URLs and other development defaults into the ductor service automatically:

DUCTOR_DATABASE_URL: postgres://ductor:ductor@timescale:5432/ductor?sslmode=disable
DUCTOR_CACHE_URL: redis://:ductor@dragonfly:6379
DUCTOR_DATABASE_AUTO_MIGRATE: "true"   # dev convenience; run migrations explicitly in prod
DUCTOR_AUTH_ENABLED: "false"           # open API for local development
DUCTOR_AUTH_ALLOW_ANONYMOUS: "true"
DUCTOR_LOG_LEVEL: info
DUCTOR_LOG_FORMAT: json

Because DUCTOR_DATABASE_AUTO_MIGRATE is true in compose, the database schema is migrated automatically on first boot. In production this flag defaults to false and you run migrations explicitly — see Database migrations.

Run without Docker

If you have Go 1.25 and a Postgres/Redis pair already, you can run the binary directly. Pick the dev subcommand for fast local hacking, or serve for a production-shaped run.

The dev subcommand embeds an in-process Redis (miniredis) and forces development-friendly defaults (auth off, anonymous allowed, auto-migrate on):

export DUCTOR_DATABASE_URL="postgres://ductor:ductor@localhost:5432/ductor?sslmode=disable"
go run ./cmd/ductor dev

dev still needs Postgres

ductor dev only replaces Redis with an in-process stand-in. A reachable PostgreSQL database is still required — set DUCTOR_DATABASE_URL or pass --database-url.

Ports

Ductor serves on three listeners by default:

PortProtocolNotes
8080HTTP — REST (grpc-gateway) and Connect-RPC on the same portserver.http_addr
50051gRPCexposed by the container; api.addr defaults to :50052 for the native Connect listener
9090Prometheus metrics (/metrics)server.metrics_addr

Verify it's healthy

The container ships a healthcheck (wget --spider http://localhost:8080/health). You can hit the same endpoints yourself:

# process-level check via the CLI
ductor health

# liveness / readiness over HTTP
curl -s http://localhost:8080/health
curl -s http://localhost:8080/ready

/health reports process liveness; /ready reports readiness (dependencies reachable, migrations current). See Health checks & readiness for what each probe actually verifies and for the registered path aliases.

Explore the API

With DUCTOR_AUTH_ENABLED=false (the compose default), you can call the REST surface without a token — every /api/... route is open on :8080. The interactive API reference is served straight from the running instance at http://localhost:8080/docs (Scalar), and the raw spec at http://localhost:8080/openapi.yaml.

For a guided first request — seeding a tenant, defining a workflow, and running it end to end — follow Getting Started.

Turn on the observability stack (optional)

The compose file defines a full VictoriaMetrics-based observability stack behind the observability profile (VictoriaMetrics, VictoriaLogs, VictoriaTraces, vmagent, vmalert, Alertmanager, Vector, Grafana). Bring it up with:

docker compose --profile observability up
# or, if the repo Makefile is present:
make up-full

Grafana is then available at http://localhost:3000 (admin / admin by default). See Observability for how traces, metrics, and logs are wired.

Next steps