services: postgres: image: postgres:16-alpine container_name: cio-postgres environment: POSTGRES_DB: ${POSTGRES_DB:-classroomio} POSTGRES_USER: ${POSTGRES_USER:-postgres} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} volumes: - postgres-data:/var/lib/postgresql/data healthcheck: # Require a successful query so we only pass once Postgres is out of recovery # (pg_isready can pass while DB is still in recovery, causing 57P03 on sign-in) test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-classroomio} && psql -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-classroomio} -tAc 'SELECT 1'", ] interval: 5s timeout: 5s retries: 15 start_period: 10s redis: image: redis:7-alpine container_name: cio-redis command: ["redis-server", "--appendonly", "yes"] volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 10 start_period: 5s minio: image: minio/minio:latest container_name: cio-minio command: server /data --console-address ":9001" ports: - "9000:9000" - "9001:9001" environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} volumes: - minio-data:/data profiles: - minio minio-init: image: minio/mc:latest container_name: cio-minio-init depends_on: - minio entrypoint: > /bin/sh -c " echo 'Waiting for MinIO to be ready...'; sleep 5; mc alias set local http://minio:9000 $${MINIO_ROOT_USER:-minioadmin} $${MINIO_ROOT_PASSWORD:-minioadmin}; mc mb local/videos --ignore-existing; mc mb local/documents --ignore-existing; mc mb local/media --ignore-existing; mc anonymous set download local/media; echo 'MinIO buckets created'; exit 0; " environment: MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} profiles: - minio db-init: build: context: .. dockerfile: docker/Dockerfile.api container_name: cio-db-init depends_on: postgres: condition: service_healthy environment: DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-classroomio} PRIVATE_DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-classroomio} command: - sh - -c - pnpm --filter @cio/db db:setup restart: "no" api: build: context: .. dockerfile: docker/Dockerfile.api container_name: cio-api ports: - "3081:3081" depends_on: postgres: condition: service_healthy redis: condition: service_healthy db-init: condition: service_completed_successfully environment: NODE_ENV: production PORT: 3081 PUBLIC_SERVER_URL: ${PUBLIC_SERVER_URL:-http://localhost:3081} TRUSTED_ORIGINS: ${TRUSTED_ORIGINS:-http://localhost:3082} AUTH_COOKIE_DOMAIN: ${AUTH_COOKIE_DOMAIN:-} DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-classroomio} PRIVATE_DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-classroomio} REDIS_URL: redis://redis:6379 GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-docker-local-client-id} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-docker-local-client-secret} SMTP_HOST: ${SMTP_HOST:-} SMTP_USER: ${SMTP_USER:-} SMTP_PASSWORD: ${SMTP_PASSWORD:-} SMTP_PORT: ${SMTP_PORT:-} SMTP_SENDER: ${SMTP_SENDER:-} CLOUDFLARE_BUCKET_DOMAIN: ${CLOUDFLARE_BUCKET_DOMAIN:-} CLOUDFLARE_ACCESS_KEY: ${CLOUDFLARE_ACCESS_KEY:-} CLOUDFLARE_SECRET_ACCESS_KEY: ${CLOUDFLARE_SECRET_ACCESS_KEY:-} CLOUDFLARE_ACCOUNT_ID: ${CLOUDFLARE_ACCOUNT_ID:-} CLOUDFLARE_IMAGE_BUCKET_DOMAIN: ${CLOUDFLARE_IMAGE_BUCKET_DOMAIN:-} CLOUDFLARE_RENDERING_API_KEY: ${CLOUDFLARE_RENDERING_API_KEY:-} OBJECT_STORAGE_ENDPOINT: ${OBJECT_STORAGE_ENDPOINT:-} OBJECT_STORAGE_PUBLIC_ENDPOINT: ${OBJECT_STORAGE_PUBLIC_ENDPOINT:-} OBJECT_STORAGE_ACCESS_KEY_ID: ${OBJECT_STORAGE_ACCESS_KEY_ID:-} OBJECT_STORAGE_SECRET_ACCESS_KEY: ${OBJECT_STORAGE_SECRET_ACCESS_KEY:-} OBJECT_STORAGE_FORCE_PATH_STYLE: ${OBJECT_STORAGE_FORCE_PATH_STYLE:-true} OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL: ${OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL:-} AUTH_BEARER_TOKEN: ${AUTH_BEARER_TOKEN:-local-dev-api-key} PRIVATE_SERVER_KEY: ${PRIVATE_SERVER_KEY:-local-dev-api-key} LICENSE_KEY: ${LICENSE_KEY:-} UNSPLASH_API_KEY: ${UNSPLASH_API_KEY:-} BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:-local-dev-only-secret-change-this} restart: unless-stopped dashboard: build: context: .. dockerfile: docker/Dockerfile.dashboard args: PUBLIC_IS_SELFHOSTED: ${PUBLIC_IS_SELFHOSTED:-true} PUBLIC_SERVER_URL: ${PUBLIC_SERVER_URL:-http://localhost:3081} ALLOWED_EXTERNAL_DOMAINS: ${ALLOWED_EXTERNAL_DOMAINS:-} CSP_SCRIPT_SRC_DOMAINS: ${CSP_SCRIPT_SRC_DOMAINS:-} CSP_STYLE_SRC_DOMAINS: ${CSP_STYLE_SRC_DOMAINS:-} CSP_CONNECT_SRC_DOMAINS: ${CSP_CONNECT_SRC_DOMAINS:-} CSP_FRAME_SRC_DOMAINS: ${CSP_FRAME_SRC_DOMAINS:-} CSP_FONT_SRC_DOMAINS: ${CSP_FONT_SRC_DOMAINS:-} container_name: cio-dashboard ports: - "3082:3082" depends_on: api: condition: service_started environment: NODE_ENV: production PORT: 3082 ORIGIN: ${DASHBOARD_ORIGIN:-http://localhost:3082} PUBLIC_IS_SELFHOSTED: ${PUBLIC_IS_SELFHOSTED:-true} PUBLIC_SERVER_URL: ${PUBLIC_SERVER_URL:-http://localhost:3081} PRIVATE_SERVER_URL: ${PRIVATE_SERVER_URL:-http://api:3081} PRIVATE_SERVER_KEY: ${PRIVATE_SERVER_KEY:-local-dev-api-key} PRIVATE_APP_HOST: ${PRIVATE_APP_HOST:-localhost} PRIVATE_APP_SUBDOMAINS: ${PRIVATE_APP_SUBDOMAINS:-app} AUTH_BEARER_TOKEN: ${AUTH_BEARER_TOKEN:-} restart: unless-stopped volumes: postgres-data: redis-data: minio-data: