# The whole VoiceGateway stack: six services (decision 0005), plus a one-shot migrate. Start it with `make up`, which creates # deploy/.env with fresh random secrets on first run. Every pulled image is pinned by tag and digest. name: voicegateway x-voicegateway-env: &voicegateway-env VOICEGATEWAY_POSTGRES_URL: postgres://voicegateway:${POSTGRES_PASSWORD:?set it in deploy/.env}@postgres:5432/voicegateway?sslmode=disable VOICEGATEWAY_CLICKHOUSE_URL: clickhouse://voicegateway:${CLICKHOUSE_PASSWORD:?set it in deploy/.env}@clickhouse:9000/voicegateway VOICEGATEWAY_REDIS_URL: redis://:${REDIS_PASSWORD:?set it in deploy/.env}@redis:6379/0 VOICEGATEWAY_S3_ENDPOINT: s3:8333 VOICEGATEWAY_S3_BUCKET: recordings VOICEGATEWAY_S3_ACCESS_KEY: ${S3_ACCESS_KEY:?set it in deploy/.env} VOICEGATEWAY_S3_SECRET_KEY: ${S3_SECRET_KEY:?set it in deploy/.env} VOICEGATEWAY_SECRET_KEY: ${VOICEGATEWAY_SECRET_KEY:?set it in deploy/.env} # Where people open the console; https makes the session cookie Secure (decision 0027). VOICEGATEWAY_PUBLIC_URL: ${VOICEGATEWAY_PUBLIC_URL:-} # Proxies whose X-Forwarded-For and X-Forwarded-Proto web believes, comma-separated. VOICEGATEWAY_TRUSTED_PROXIES: ${VOICEGATEWAY_TRUSTED_PROXIES:-} # SMTP for invitation and reset emails: smtps://user:pass@host:465, or smtp://user:pass@host:587 # with STARTTLS. Needs VOICEGATEWAY_PUBLIC_URL, the base of every emailed link. Empty sends # nothing, and Settings shows each link to copy. VOICEGATEWAY_SMTP_URL: ${VOICEGATEWAY_SMTP_URL:-} VOICEGATEWAY_SMTP_FROM: ${VOICEGATEWAY_SMTP_FROM:-} # Google and GitHub sign-in (decision 0035): each on when its client id and secret are both set, # off when both are empty. Needs VOICEGATEWAY_PUBLIC_URL, the base of the callback URL that # `voicegateway preflight` prints. VOICEGATEWAY_GOOGLE_CLIENT_ID: ${VOICEGATEWAY_GOOGLE_CLIENT_ID:-} VOICEGATEWAY_GOOGLE_CLIENT_SECRET: ${VOICEGATEWAY_GOOGLE_CLIENT_SECRET:-} VOICEGATEWAY_GITHUB_CLIENT_ID: ${VOICEGATEWAY_GITHUB_CLIENT_ID:-} VOICEGATEWAY_GITHUB_CLIENT_SECRET: ${VOICEGATEWAY_GITHUB_CLIENT_SECRET:-} # Auth0 sign-in (decision 0036): on when the tenant's domain (a bare host such as # voicegateway.us.auth0.com), client id and secret are all set. VOICEGATEWAY_AUTH0_DOMAIN: ${VOICEGATEWAY_AUTH0_DOMAIN:-} VOICEGATEWAY_AUTH0_CLIENT_ID: ${VOICEGATEWAY_AUTH0_CLIENT_ID:-} VOICEGATEWAY_AUTH0_CLIENT_SECRET: ${VOICEGATEWAY_AUTH0_CLIENT_SECRET:-} # Product analytics for the console (decision 0038): off unless set. A PostHog project API key # (phc_...); the host defaults to https://us.i.posthog.com. No call content or email is sent. VOICEGATEWAY_POSTHOG_KEY: ${VOICEGATEWAY_POSTHOG_KEY:-} VOICEGATEWAY_POSTHOG_HOST: ${VOICEGATEWAY_POSTHOG_HOST:-} # Turns on the instance API (/api/v1/instance, ee/), for an operator's program that creates # organisations: at least 32 characters, and needs VOICEGATEWAY_PUBLIC_URL and # VOICEGATEWAY_WORKER_ENDPOINTS_PUBLIC_ONLY=true (every role refuses to start without it). Empty # keeps it off. VOICEGATEWAY_INSTANCE_TOKEN: ${VOICEGATEWAY_INSTANCE_TOKEN:-} # Enforce explicit organisation allowances on Cloud ingestion; requires the instance token. VOICEGATEWAY_CLOUD_QUOTAS: ${VOICEGATEWAY_CLOUD_QUOTAS:-false} VOICEGATEWAY_LOG_LEVEL: ${VOICEGATEWAY_LOG_LEVEL:-info} # Report errors and request timings to your own Sentry projects (decision 0039). Empty, as by # default, sends nothing anywhere. The console DSN is handed to every visitor's browser. VOICEGATEWAY_SENTRY_DSN: ${VOICEGATEWAY_SENTRY_DSN:-} VOICEGATEWAY_SENTRY_CONSOLE_DSN: ${VOICEGATEWAY_SENTRY_CONSOLE_DSN:-} VOICEGATEWAY_SENTRY_ENVIRONMENT: ${VOICEGATEWAY_SENTRY_ENVIRONMENT:-production} VOICEGATEWAY_SENTRY_TRACES_SAMPLE_RATE: ${VOICEGATEWAY_SENTRY_TRACES_SAMPLE_RATE:-0.1} # true refuses private and loopback worker metrics endpoints too; link-local and cloud metadata # addresses are always refused; it also guards the worker's LiveKit API reads. Leave false when # your agent workers share this network. Required with VOICEGATEWAY_INSTANCE_TOKEN. VOICEGATEWAY_WORKER_ENDPOINTS_PUBLIC_ONLY: ${VOICEGATEWAY_WORKER_ENDPOINTS_PUBLIC_ONLY:-false} # Retired in phase 06c and no longer accepted. Passed through only so web can warn an upgrade # that still sets it; removed after V1. VOICEGATEWAY_API_TOKEN: ${VOICEGATEWAY_API_TOKEN:-} x-voicegateway-depends: &voicegateway-depends postgres: { condition: service_healthy } clickhouse: { condition: service_healthy } redis: { condition: service_healthy } s3: { condition: service_healthy } services: postgres: image: postgres:16.15-alpine@sha256:721873c34ceb9f8d8fc265984940dc982404c105f19ad51be9fdc5970a6080ea environment: POSTGRES_USER: voicegateway POSTGRES_DB: voicegateway POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set it in deploy/.env} volumes: [postgres:/var/lib/postgresql/data] healthcheck: test: ["CMD", "pg_isready", "-U", "voicegateway", "-d", "voicegateway"] interval: 2s retries: 30 clickhouse: image: clickhouse/clickhouse-server:26.1.12.23-alpine@sha256:369bd68942569e0effdf0aa68b4f844a74d97b73e281cf8012ce90e9865cc411 environment: CLICKHOUSE_DB: voicegateway CLICKHOUSE_USER: voicegateway CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?set it in deploy/.env} volumes: [clickhouse:/var/lib/clickhouse] ulimits: nofile: { soft: 262144, hard: 262144 } healthcheck: # The initialization server listens only on loopback and shuts down before the final # server starts. Query this container's network address with migrate's credentials. test: ["CMD-SHELL", "clickhouse-client --host \"$$HOSTNAME\" --user \"$$CLICKHOUSE_USER\" --password \"$$CLICKHOUSE_PASSWORD\" --database \"$$CLICKHOUSE_DB\" -q 'SELECT 1'"] interval: 2s retries: 30 redis: image: redis:8.10.2-alpine@sha256:3811787313eba226a2ef38658c6ccb91cd5e110edc89c37767de373120a0e5a0 command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD:?set it in deploy/.env}"] environment: REDISCLI_AUTH: ${REDIS_PASSWORD:?set it in deploy/.env} volumes: [redis:/data] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 2s retries: 30 # Any S3 bucket works (decision 0005). SeaweedFS stands in for MinIO, which no longer publishes # images (decision 0026); `weed mini` creates the bucket and the key pair from its environment. s3: image: chrislusf/seaweedfs:4.48@sha256:4e61d15fd35994cb1e43e1e553dff106794841fd9a99ade2fc8c8bfce4d7872d command: ["mini", "-dir=/data", "-ip.bind=0.0.0.0"] environment: AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY:?set it in deploy/.env} AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY:?set it in deploy/.env} S3_BUCKET: recordings volumes: [s3:/data] healthcheck: test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8333/healthz"] interval: 2s retries: 60 # Applies pending migrations once, then exits; web and worker wait for it to succeed. migrate: build: { context: .., dockerfile: deploy/Dockerfile } # VOICEGATEWAY_IMAGE runs a published image instead of building one, for example # ghcr.io/mahimailabs/voicegateway:1.0.0-preview.2 (see docs/self-hosting.md). image: ${VOICEGATEWAY_IMAGE:-voicegateway:dev} command: ["migrate"] environment: *voicegateway-env restart: "no" depends_on: *voicegateway-depends web: image: ${VOICEGATEWAY_IMAGE:-voicegateway:dev} command: ["web"] environment: *voicegateway-env ports: ["${VOICEGATEWAY_WEB_PORT:-8080}:8080"] depends_on: <<: *voicegateway-depends migrate: { condition: service_completed_successfully } worker: image: ${VOICEGATEWAY_IMAGE:-voicegateway:dev} command: ["worker"] environment: *voicegateway-env depends_on: <<: *voicegateway-depends migrate: { condition: service_completed_successfully } volumes: postgres: clickhouse: redis: s3: