version: '3.8' services: server: extends: file: ./server/docker-compose.prebuilt.yaml service: server image: "ghcr.io/nine-minds/alga-psa-ce:${ALGA_IMAGE_TAG:-latest}" platform: linux/amd64 environment: EDITION: community DB_NAME: server PGBOSS_DATABASE: server DB_NAME_SERVER: server DB_USER_SERVER: app_user DB_USER_ADMIN: ${DB_USER_ADMIN:-postgres} VERSION: ${VERSION} APP_NAME: ${APP_NAME} # Prebuilt stacks should run the server in production mode (use the baked `.next` output). APP_ENV: production NODE_ENV: production # The prebuilt server image listens on port 3000 inside the container. APP_PORT: "3000" PORT: "3000" HOST: ${HOST} VERIFY_EMAIL_ENABLED: ${VERIFY_EMAIL_ENABLED:-false} REDIS_HOST: ${REDIS_HOST:-redis} REDIS_PORT: ${REDIS_PORT:-6379} DB_TYPE: ${DB_TYPE:-postgres} DB_HOST: ${PGBOUNCER_HOST:-pgbouncer} DB_PORT: ${PGBOUNCER_PORT:-6432} DB_NAME_HOCUSPOCUS: ${DB_NAME_HOCUSPOCUS:-hocuspocus} DB_USER_HOCUSPOCUS: ${DB_USER_HOCUSPOCUS:-hocuspocus_user} HOCUSPOCUS_JWT_SECRET: ${HOCUSPOCUS_JWT_SECRET} DB_PASSWORD_HOCUSPOCUS: ${DB_PASSWORD_HOCUSPOCUS} REQUIRE_HOCUSPOCUS: ${REQUIRE_HOCUSPOCUS:-false} LOG_LEVEL: ${LOG_LEVEL:-INFO} LOG_IS_FORMAT_JSON: ${LOG_IS_FORMAT_JSON:-false} LOG_IS_FULL_DETAILS: ${LOG_IS_FULL_DETAILS:-false} EMAIL_ENABLE: ${EMAIL_ENABLE:-false} EMAIL_FROM: ${EMAIL_FROM:-noreply@example.com} EMAIL_PORT: ${EMAIL_PORT:-587} EMAIL_USERNAME: ${EMAIL_USERNAME:-noreply@example.com} NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000} NEXTAUTH_SESSION_EXPIRES: ${NEXTAUTH_SESSION_EXPIRES:-86400} IMAP_WEBHOOK_SECRET: ${IMAP_WEBHOOK_SECRET:-} volumes: # Persist user-uploaded documents and generated files - files_data:/data/files - type: bind source: ./secrets/db_password_server target: /run/secrets/db_password_server read_only: true - type: bind source: ./secrets/tenants target: /run/secrets/tenants - type: bind source: ./server/migrations target: /app/server/migrations read_only: true - type: bind source: ./server/seeds target: /app/server/seeds read_only: true - type: bind source: ./scripts target: /app/scripts read_only: true entrypoint: ["/bin/sh", "-c", "export DATABASE_URL=postgresql://app_user:$$(cat /run/secrets/db_password_server)@${PGBOUNCER_HOST:-pgbouncer}:${PGBOUNCER_PORT:-6432}/server && /app/entrypoint.sh"] secrets: - postgres_password - db_password_server - db_password_hocuspocus - redis_password - email_password - crypto_key - token_secret_key - nextauth_secret - google_oauth_client_id - google_oauth_client_secret - alga_auth_key networks: - app-network depends_on: setup: condition: service_completed_successfully postgres: condition: service_started pgbouncer: condition: service_started redis: condition: service_started hocuspocus: condition: service_started required: false setup: image: "ghcr.io/nine-minds/alga-psa-ce:${ALGA_IMAGE_TAG:-latest}" platform: linux/amd64 restart: "no" environment: EDITION: community NODE_OPTIONS: --experimental-vm-modules DB_NAME_SERVER: server DB_NAME_HOCUSPOCUS: ${DB_NAME_HOCUSPOCUS:-hocuspocus} DB_USER_SERVER: app_user DB_USER_HOCUSPOCUS: ${DB_USER_HOCUSPOCUS:-hocuspocus_user} HOCUSPOCUS_JWT_SECRET: ${HOCUSPOCUS_JWT_SECRET} DB_USER_ADMIN: ${DB_USER_ADMIN:-postgres} PGBOSS_DATABASE: server VERSION: ${VERSION} APP_NAME: ${APP_NAME} APP_ENV: production NODE_ENV: production HOST: ${HOST} DB_TYPE: ${DB_TYPE:-postgres} DB_HOST: ${DB_HOST:-postgres} DB_PORT: ${DB_PORT:-5432} # Prefer direct Postgres for admin operations even if DB_HOST points to PgBouncer DB_HOST_ADMIN: ${DB_HOST_ADMIN:-postgres} DB_PORT_ADMIN: ${DB_PORT_ADMIN:-5432} LOG_LEVEL: ${LOG_LEVEL:-INFO} LOG_IS_FORMAT_JSON: ${LOG_IS_FORMAT_JSON:-false} LOG_IS_FULL_DETAILS: ${LOG_IS_FULL_DETAILS:-false} EMAIL_ENABLE: ${EMAIL_ENABLE:-false} EMAIL_FROM: ${EMAIL_FROM:-noreply@example.com} EMAIL_PORT: ${EMAIL_PORT:-587} EMAIL_USERNAME: ${EMAIL_USERNAME:-noreply@example.com} NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000} NEXTAUTH_SESSION_EXPIRES: ${NEXTAUTH_SESSION_EXPIRES:-86400} volumes: - type: bind source: ./setup/config.ini target: /app/setup/config.ini read_only: true - type: bind source: ./setup/entrypoint.sh target: /app/setup/entrypoint.sh read_only: true - type: bind source: ./secrets/postgres_password target: /run/secrets/postgres_password read_only: true - type: bind source: ./secrets/db_password_server target: /run/secrets/db_password_server read_only: true - type: bind source: ./server/migrations target: /app/server/migrations read_only: true - type: bind source: ./server/seeds target: /app/server/seeds read_only: true - type: bind source: ./scripts target: /app/scripts read_only: true secrets: - postgres_password - db_password_server - db_password_hocuspocus networks: - app-network depends_on: postgres: condition: service_started entrypoint: ["/app/setup/entrypoint.sh"] workflow-worker: # NOTE: The published CE server image does not currently ship the workflow-worker runtime bundle. # For local prebuilt-stack testing, build the worker image from source. build: context: . dockerfile: services/workflow-worker/Dockerfile platform: linux/amd64 environment: EDITION: community DB_NAME: server PGBOSS_DATABASE: server DB_NAME_SERVER: server DB_USER_SERVER: app_user DB_USER_ADMIN: ${DB_USER_ADMIN:-postgres} VERSION: ${VERSION} APP_NAME: ${APP_NAME} APP_ENV: production NODE_ENV: production HOST: ${HOST} REDIS_HOST: ${REDIS_HOST:-redis} REDIS_PORT: ${REDIS_PORT:-6379} DB_TYPE: ${DB_TYPE:-postgres} DB_HOST: ${PGBOUNCER_HOST:-pgbouncer} DB_PORT: ${PGBOUNCER_PORT:-6432} LOG_LEVEL: ${LOG_LEVEL:-INFO} LOG_IS_FORMAT_JSON: ${LOG_IS_FORMAT_JSON:-false} LOG_IS_FULL_DETAILS: ${LOG_IS_FULL_DETAILS:-false} # Secret provider configuration for workflow-worker (CE edition) SECRET_READ_CHAIN: ${SECRET_READ_CHAIN:-env,filesystem} SECRET_WRITE_PROVIDER: ${SECRET_WRITE_PROVIDER:-filesystem} # Workflow-specific configuration # Run v2 runtime by default (legacy can be re-enabled via WORKFLOW_WORKER_MODE=all|legacy) WORKFLOW_WORKER_MODE: ${WORKFLOW_WORKER_MODE:-v2} WORKFLOW_DISTRIBUTED_MODE: "true" WORKFLOW_REDIS_STREAM_PREFIX: "workflow:events:" WORKFLOW_REDIS_CONSUMER_GROUP: "workflow-workers" WORKFLOW_REDIS_BATCH_SIZE: "10" WORKFLOW_REDIS_IDLE_TIMEOUT_MS: "60000" volumes: - type: bind source: ./secrets/db_password_server target: /run/secrets/db_password_server read_only: true - type: bind source: ./services/workflow-worker/entrypoint.sh target: /app/entrypoint.sh read_only: true entrypoint: ["/app/entrypoint.sh"] secrets: - postgres_password - db_password_server - redis_password - crypto_key - token_secret_key - nextauth_secret networks: - app-network depends_on: postgres: condition: service_started pgbouncer: condition: service_started redis: condition: service_started server: condition: service_started deploy: replicas: ${WORKFLOW_WORKER_REPLICAS:-1} email-service: # NOTE: The published CE server image does not currently ship the email-service runtime bundle. # For local prebuilt-stack testing, build the IMAP service image from source. build: context: . dockerfile: services/email-service/Dockerfile platform: linux/amd64 environment: EDITION: community DB_NAME: server DB_NAME_SERVER: server DB_USER_SERVER: app_user DB_USER_ADMIN: ${DB_USER_ADMIN:-postgres} VERSION: ${VERSION} APP_NAME: ${APP_NAME} APP_ENV: production NODE_ENV: production HOST: ${HOST} REDIS_HOST: ${REDIS_HOST:-redis} REDIS_PORT: ${REDIS_PORT:-6379} DB_TYPE: ${DB_TYPE:-postgres} DB_HOST: ${PGBOUNCER_HOST:-pgbouncer} DB_PORT: ${PGBOUNCER_PORT:-6432} LOG_LEVEL: ${LOG_LEVEL:-INFO} LOG_IS_FORMAT_JSON: ${LOG_IS_FORMAT_JSON:-false} LOG_IS_FULL_DETAILS: ${LOG_IS_FULL_DETAILS:-false} # Secret provider configuration for email-service (CE edition) SECRET_READ_CHAIN: ${SECRET_READ_CHAIN:-env,filesystem} SECRET_WRITE_PROVIDER: ${SECRET_WRITE_PROVIDER:-filesystem} # IMAP service configuration IMAP_PROVIDER_REFRESH_MS: ${IMAP_PROVIDER_REFRESH_MS:-60000} IMAP_POLL_INTERVAL_MS: ${IMAP_POLL_INTERVAL_MS:-30000} IMAP_LEASE_TTL_MS: ${IMAP_LEASE_TTL_MS:-120000} IMAP_MAX_CONNECTIONS_PER_TENANT: ${IMAP_MAX_CONNECTIONS_PER_TENANT:-5} IMAP_MAX_ATTACHMENT_BYTES: ${IMAP_MAX_ATTACHMENT_BYTES:-0} IMAP_FETCH_DELAY_MS: ${IMAP_FETCH_DELAY_MS:-0} IMAP_EVENT_CHANNEL_BY_TENANT: ${IMAP_EVENT_CHANNEL_BY_TENANT:-false} IMAP_OAUTH_AUTH_MECHANISM: ${IMAP_OAUTH_AUTH_MECHANISM:-XOAUTH2} IMAP_WEBHOOK_SECRET: ${IMAP_WEBHOOK_SECRET:-} volumes: - type: bind source: ./secrets/db_password_server target: /run/secrets/db_password_server read_only: true - type: bind source: ./secrets/tenants target: /run/secrets/tenants - type: bind source: ./services/email-service/entrypoint.sh target: /app/entrypoint.sh read_only: true entrypoint: ["/app/entrypoint.sh"] secrets: - postgres_password - db_password_server - redis_password - crypto_key - token_secret_key - nextauth_secret - google_oauth_client_id - google_oauth_client_secret - alga_auth_key networks: - app-network depends_on: postgres: condition: service_started pgbouncer: condition: service_started redis: condition: service_started server: condition: service_started hocuspocus: extends: file: ./hocuspocus/docker-compose.yaml service: hocuspocus build: context: . dockerfile: hocuspocus/Dockerfile environment: VERSION: ${VERSION} APP_NAME: ${APP_NAME} APP_ENV: ${APP_ENV:-development} NODE_ENV: ${APP_ENV:-development} HOST: ${HOST} REDIS_HOST: ${REDIS_HOST:-redis} REDIS_PORT: ${REDIS_PORT:-6379} DB_TYPE: ${DB_TYPE:-postgres} DB_HOST: ${DB_HOST_ADMIN:-postgres} DB_PORT: ${DB_PORT_ADMIN:-5432} DB_NAME_HOCUSPOCUS: ${DB_NAME_HOCUSPOCUS:-hocuspocus} DB_USER_HOCUSPOCUS: ${DB_USER_HOCUSPOCUS:-hocuspocus_user} HOCUSPOCUS_JWT_SECRET: ${HOCUSPOCUS_JWT_SECRET} secrets: - db_password_hocuspocus - redis_password - postgres_password networks: - app-network depends_on: redis: condition: service_started postgres: extends: file: docker-compose.base.yaml service: postgres volumes: # Persist Postgres data across restarts/upgrades - postgres_data:/var/lib/postgresql/data environment: POSTGRES_DB: server POSTGRES_HOST_AUTH_METHOD: trust VERSION: ${VERSION} APP_NAME: ${APP_NAME} APP_ENV: ${APP_ENV:-development} NODE_ENV: ${APP_ENV:-development} HOST: ${HOST} DB_TYPE: postgres DB_HOST: ${DB_HOST:-postgres} DB_PORT: ${DB_PORT:-5432} secrets: - postgres_password redis: extends: file: docker-compose.base.yaml service: redis networks: app-network: driver: bridge volumes: # Named volumes for production-like persistence postgres_data: files_data: