Self-hosting

Production hardening — environment variables, version pinning, upgrades, backups, reverse proxy, resource sizing.

Self-hosting is a first-class target. The Docker Compose stack in the repo is the same software that runs in ScopeCall Cloud — no feature gating.

For the initial install, see Installation. This guide covers production concerns.

Environment variables

Set these in infra/.env. See infra/.env.example for the full template.

VariableRequiredPurpose
AUTH_SECRETSigns Auth.js JWTs. 32+ random bytes. Generate: openssl rand -hex 32
INTERNAL_API_KEYShared secret for the dashboard ↔ API internal channel. Generate: openssl rand -hex 32
POSTGRES_PASSWORDoptionalDefaults to scopecall. Change for production.
SCOPECALL_VERSIONoptionalImage tag. Defaults to latest. Pin to a version in production.

Both required secrets are enforced at startup — the stack will refuse to boot if they're unset.

Pinning versions

Always pin SCOPECALL_VERSION in production so deployments are reproducible:

echo "SCOPECALL_VERSION=v0.1.1" >> infra/.env

Upgrading

# Update the version pin in infra/.env, then:
docker compose -f infra/docker-compose.yml pull
docker compose -f infra/docker-compose.yml up -d

Schema migrations run automatically on startup. ClickHouse and Postgres migrations are forward-only and idempotent. Back up your volumes before a major version upgrade (see below).

Backups

State lives in four named Docker volumes:

VolumeContains
clickhouse-dataTrace events, cost data (the bulk of your data)
postgres-dataUsers, orgs, API keys, config
redpanda-dataIn-flight event buffer (transient)
redis-dataCaches (transient)

The two that matter for backup are clickhouse-data and postgres-data.

# Example: back up Postgres
docker compose -f infra/docker-compose.yml exec postgres \
  pg_dump -U scopecall scopecall > backup-postgres-$(date +%F).sql

# ClickHouse: use clickhouse-backup or volume snapshots for large datasets

Reverse proxy + TLS

The stack serves HTTP on port 3000 by default. For production, front it with a reverse proxy that terminates TLS (Caddy, nginx, or your cloud load balancer). Do not expose ClickHouse (8123/9000) or Postgres (5432) to the public internet — they are intended for the internal Docker network only.

Resource sizing

ScaleGuidance
Getting started4 vCPU / 8 GB / 50 GB (the documented minimum)
~1M events/day8 vCPU / 16 GB / 200 GB SSD
HigherClickHouse is the bottleneck — scale disk + RAM first; the Rust ingest path handles high throughput on modest CPU

Data retention

Trace events in ClickHouse have a default 90-day TTL. Adjust the TTL in the ClickHouse schema if you need longer retention (and size disk accordingly).

Health checks

Every service exposes a health endpoint and Compose health checks gate startup order. Check status with:

docker compose -f infra/docker-compose.yml ps

All services should show healthy. If one is stuck, check its logs:

docker compose -f infra/docker-compose.yml logs <service>

Troubleshooting

SymptomLikely cause
Stack refuses to startAUTH_SECRET or INTERNAL_API_KEY unset in infra/.env
/setup redirects to loginAn admin user already exists — setup is one-time
Dashboard shows no tracesSDK endpoint not pointing at your ingest URL (port 8080, full path /v1/ingest), or API key invalid
Events accepted but not visibleCheck processor logs; ClickHouse may be unhealthy

For security disclosure, see SECURITY.md in the public repo.