Skip to content

Self-hosted

For customers who want everything on their own hardware, the repo ships a docker-compose.selfhost.yml that bundles Postgres, Redis, the migration sidecar, the API, the worker process, and the dashboard on one Docker network. Requires Docker Engine 24+ with the compose plugin.

Terminal window
cat > .env <<'EOF'
NEXT_PUBLIC_SITE_URL=https://carbon.internal.example
NEXT_PUBLIC_CARBON_API_URL=https://carbon.internal.example/api
BETTER_AUTH_SECRET=$(openssl rand -hex 32)
ALLOWED_ORIGINS=https://carbon.internal.example
CARBON_METRICS_TOKEN=$(openssl rand -hex 16)
EOF
docker compose -f docker-compose.selfhost.yml up -d
ServiceRole
postgresControl-plane database
redisRate limits, idempotency, queue
migrateRuns pnpm --filter @carbon/database migrate:apply and exits
apiFastify API, exposes :4000
workersIngest queue consumers
dashboardNext.js dashboard, exposes :3001

api, workers, and dashboard all wait on service_completed_successfully for the migrate sidecar, so you should never run migrations by hand in the self-host path.

The compose file exposes plain HTTP on :3001 (dashboard) and :4000 (API). Front the stack with your own TLS terminator — Caddy, nginx, or Cloudflare Tunnel all work fine.

The dashboard signs in humans via Better Auth; API traffic uses Carbon API keys. Mint the first one from a shell that can reach Postgres:

Terminal window
DATABASE_URL="postgres://…" NODE_ENV=production \
pnpm --filter @carbon/api bootstrap

The secret is displayed exactly once. Store it in your password manager or your CI secret store.

docker compose pull && docker compose up -d — the migration sidecar runs first, then the rest of the stack restarts against the new schema. No manual migration step.

See Prometheus for scraping the same /metrics endpoint in the self-host path.