MicProtocol / docker-compose.coolify.yml
Benaah's picture
Upload folder using huggingface_hub
b17baae verified
Raw History Blame Contribute Delete
15.5 kB
# ═══════════════════════════════════════════════════════════
# MicProtocol – Coolify Production Stack
# Deploy via Coolify Docker Compose resource.
# Coolify auto-builds API/Web/Admin from source on git push.
# Coolify proxy handles SSL + domain binding for all services.
# ═══════════════════════════════════════════════════════════
services:
# ─── Database ─────────────────────────────────────────────
postgres:
image: pgvector/pgvector:pg16
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init-databases.sql:/docker-entrypoint-initdb.d/init-databases.sql
environment:
POSTGRES_USER: ${POSTGRES_USER:-micprotocol}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-micprotocol_secret}
POSTGRES_DB: ${POSTGRES_DB:-micprotocol}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-micprotocol}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- micprotocol
# ─── Cache & Queues ───────────────────────────────────────
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD:-redis_secret}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
networks:
- micprotocol
# ─── Search Engine (embedded in API container) ───────────
# Meilisearch runs inside the API container (see apps/api/Dockerfile).
# No separate service needed.
# ─── API (NestJS + tRPC) ─────────────────────────────────
api:
build:
context: .
dockerfile: apps/api/Dockerfile
restart: unless-stopped
environment:
NODE_ENV: production
API_PORT: 4000
DATABASE_URL: postgresql://${POSTGRES_USER:-micprotocol}:${POSTGRES_PASSWORD:-micprotocol_secret}@postgres:5432/${POSTGRES_DB:-micprotocol}?schema=public
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_secret}
MEILISEARCH_HOST: http://localhost:7700
WEB_URL: ${WEB_URL:-https://micprotocol-web.local}
CLERK_SECRET_KEY: ${CLERK_SECRET_KEY}
RESEND_API_KEY: ${RESEND_API_KEY}
R2_ACCESS_KEY_ID: ${R2_ACCESS_KEY_ID}
R2_SECRET_ACCESS_KEY: ${R2_SECRET_ACCESS_KEY}
R2_ENDPOINT: ${R2_ENDPOINT}
R2_BUCKET: ${R2_BUCKET}
LIVEKIT_API_KEY: ${LIVEKIT_API_KEY:-devkey}
LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET:-devsecret}
LIVEKIT_HOST: ${LIVEKIT_HOST:-http://livekit:7880}
LISTMONK_URL: ${LISTMONK_URL:-http://listmonk:9000}
LISTMONK_API_TOKEN: ${LISTMONK_API_TOKEN}
NVIDIA_NIM_API_KEY: ${NVIDIA_NIM_API_KEY}
DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY}
MOONSHOT_API_KEY: ${MOONSHOT_API_KEY}
COOKIE_SECRET: ${COOKIE_SECRET:-cookie_secret_change_in_production}
# Real-time collaboration engine (Erlang) β€” see mic-realtime service below.
# MIC_REALTIME_URL is the in-network base URL the API uses to publish
# events + issue WS tokens. The two secrets MUST be strong random values
# in production (the API fails to boot otherwise β€” RealtimeClient).
MIC_REALTIME_URL: ${MIC_REALTIME_URL:-http://mic-realtime:4001}
MIC_REALTIME_INTERNAL_TOKEN: ${MIC_REALTIME_INTERNAL_TOKEN}
MIC_REALTIME_SHARED_SECRET: ${MIC_REALTIME_SHARED_SECRET}
SENTRY_DSN: ${SENTRY_DSN}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
mic-realtime:
condition: service_started
networks:
- micprotocol
# ─── Background Workers (BullMQ) ──────────────────────────
worker:
build:
context: .
dockerfile: apps/api/Dockerfile
restart: unless-stopped
command: npx bullmq-pro
environment:
NODE_ENV: production
DATABASE_URL: postgresql://${POSTGRES_USER:-micprotocol}:${POSTGRES_PASSWORD:-micprotocol_secret}@postgres:5432/${POSTGRES_DB:-micprotocol}?schema=public
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_secret}
RESEND_API_KEY: ${RESEND_API_KEY}
NVIDIA_NIM_API_KEY: ${NVIDIA_NIM_API_KEY}
DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY}
MOONSHOT_API_KEY: ${MOONSHOT_API_KEY}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- micprotocol
# ─── Erlang/OTP Real-time Collaboration Engine ─────────────
# Owns chat fanout, presence, live domain events. The NestJS API
# publishes into it via /internal/dispatch; the web client opens
# a WebSocket at /ws?token=... to subscribe to conversations.
# Exposed to the internet by Caddy on realtime.<domain> (wss://).
mic-realtime:
build:
context: ./services/mic-realtime
dockerfile: Dockerfile
restart: unless-stopped
environment:
MIC_REALTIME_PORT: 4001
MIC_REALTIME_HOST: 0.0.0.0
# Internal in-network API URL (NOT the public NEXT_PUBLIC_API_URL)
# so realtime→API callbacks stay on the Docker network.
MICPROTOCOL_API_URL: ${API_URL:-http://api:4000}
# Both secrets MUST be strong random values in production
# (the service fails closed at startup otherwise).
MIC_REALTIME_INTERNAL_TOKEN: ${MIC_REALTIME_INTERNAL_TOKEN}
MIC_REALTIME_SHARED_SECRET: ${MIC_REALTIME_SHARED_SECRET}
MIC_REALTIME_PUBLIC_URL: ${MIC_REALTIME_PUBLIC_URL}
MIC_REALTIME_MAX_MSG: 65536
MIC_REALTIME_RATE_LIMIT: 120
MIC_REALTIME_PRESENCE_TTL: 90
depends_on:
- api
networks:
- micprotocol
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:4001/healthz"]
interval: 15s
timeout: 5s
retries: 5
# ─── Web App (Next.js) ───────────────────────────────────
web:
build:
context: .
dockerfile: apps/web/Dockerfile
restart: unless-stopped
depends_on:
- api
environment:
NODE_ENV: production
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}
CLERK_SECRET_KEY: ${CLERK_SECRET_KEY}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${UMAMI_WEBSITE_ID}
NEXT_PUBLIC_UMAMI_URL: ${UMAMI_URL}
# Must be a public wss:// URL in production (e.g. wss://realtime.<domain>)
NEXT_PUBLIC_MIC_REALTIME_URL: ${MIC_REALTIME_PUBLIC_URL}
networks:
- micprotocol
# ─── Admin Panel (Next.js) ───────────────────────────────
admin:
build:
context: .
dockerfile: apps/admin/Dockerfile
restart: unless-stopped
depends_on:
- api
environment:
NODE_ENV: production
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
API_URL: http://api:4000
# Must be a public wss:// URL in production (e.g. wss://realtime.<domain>)
NEXT_PUBLIC_MIC_REALTIME_URL: ${MIC_REALTIME_PUBLIC_URL}
networks:
- micprotocol
# ─── Cal.com (Scheduling) ─────────────────────────────────
calcom:
image: calcom/cal.com:latest
restart: unless-stopped
environment:
DATABASE_URL: postgresql://${POSTGRES_USER:-micprotocol}:${POSTGRES_PASSWORD:-micprotocol_secret}@postgres:5432/calcom?schema=public
DATABASE_DIRECT_URL: postgresql://${POSTGRES_USER:-micprotocol}:${POSTGRES_PASSWORD:-micprotocol_secret}@postgres:5432/calcom?schema=public
NEXTAUTH_SECRET: ${CALCOM_NEXTAUTH_SECRET}
CALENDSO_ENCRYPTION_KEY: ${CALCOM_ENCRYPTION_KEY}
NEXTAUTH_URL: ${CALCOM_URL:-https://calcom.local}
NEXT_PUBLIC_WEBAPP_URL: ${CALCOM_URL:-https://calcom.local}
CALCOM_TELEMETRY_DISABLED: "1"
EMAIL_FROM: ${EMAIL_FROM:-noreply@micprotocol.local}
EMAIL_SERVER_HOST: ${SMTP_HOST}
EMAIL_SERVER_PORT: ${SMTP_PORT:-587}
EMAIL_SERVER_USER: ${SMTP_USER}
EMAIL_SERVER_PASSWORD: ${SMTP_PASSWORD}
REDIS_URL: redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379
NEXT_PUBLIC_SINGLE_ORG_SLUG: ${CALCOM_SINGLE_ORG_SLUG:-micprotocol}
ORGANIZATIONS_ENABLED: "0"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- micprotocol
# ─── Listmonk (Newsletter / Email Marketing) ─────────────
listmonk:
image: listmonk/listmonk:latest
restart: unless-stopped
command: >
sh -c "./listmonk --install --idempotent --yes &&
./listmonk --upgrade --yes &&
./listmonk"
environment:
LISTMONK_app__address: "0.0.0.0:9000"
LISTMONK_db__host: postgres
LISTMONK_db__port: 5432
LISTMONK_db__user: ${POSTGRES_USER:-micprotocol}
LISTMONK_db__password: ${POSTGRES_PASSWORD:-micprotocol_secret}
LISTMONK_db__database: listmonk
LISTMONK_db__ssl_mode: disable
LISTMONK_ADMIN_USER: ${LISTMONK_ADMIN_USER:-admin}
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_ADMIN_PASSWORD:-admin_secret}
volumes:
- listmonk_uploads:/listmonk/uploads:rw
depends_on:
postgres:
condition: service_healthy
networks:
- micprotocol
# ─── Formbricks (Surveys) ─────────────────────────────────
formbricks:
image: ghcr.io/formbricks/formbricks:latest
restart: unless-stopped
environment:
WEBAPP_URL: ${FORMBRICKS_URL:-https://formbricks.local}
NEXTAUTH_URL: ${FORMBRICKS_URL:-https://formbricks.local}
DATABASE_URL: postgresql://${POSTGRES_USER:-micprotocol}:${POSTGRES_PASSWORD:-micprotocol_secret}@postgres:5432/formbricks?schema=public
NEXTAUTH_SECRET: ${FORMBRICKS_NEXTAUTH_SECRET}
ENCRYPTION_KEY: ${FORMBRICKS_ENCRYPTION_KEY}
CRON_SECRET: ${FORMBRICKS_CRON_SECRET}
REDIS_URL: redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379
EMAIL_VERIFICATION_DISABLED: 1
PASSWORD_RESET_DISABLED: 1
MAIL_FROM: ${EMAIL_FROM:-noreply@micprotocol.local}
SMTP_HOST: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT:-587}
TELEMETRY_DISABLED: 1
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- micprotocol
# ─── Documenso (Document Signing) ─────────────────────────
documenso:
image: documenso/documenso:latest
restart: unless-stopped
environment:
NEXTAUTH_SECRET: ${DOCUMENSO_NEXTAUTH_SECRET}
NEXT_PRIVATE_ENCRYPTION_KEY: ${DOCUMENSO_ENCRYPTION_KEY}
NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY: ${DOCUMENSO_ENCRYPTION_SECONDARY_KEY}
NEXT_PUBLIC_WEBAPP_URL: ${DOCUMENSO_URL:-https://documenso.local}
NEXT_PRIVATE_INTERNAL_WEBAPP_URL: http://documenso:3000
NEXT_PRIVATE_DATABASE_URL: postgresql://${POSTGRES_USER:-micprotocol}:${POSTGRES_PASSWORD:-micprotocol_secret}@postgres:5432/documenso?schema=public
NEXT_PRIVATE_DIRECT_DATABASE_URL: postgresql://${POSTGRES_USER:-micprotocol}:${POSTGRES_PASSWORD:-micprotocol_secret}@postgres:5432/documenso?schema=public
NEXT_PUBLIC_UPLOAD_TRANSPORT: r2
NEXT_PRIVATE_SMTP_TRANSPORT: smtp
NEXT_PRIVATE_SMTP_HOST: ${SMTP_HOST}
NEXT_PRIVATE_SMTP_PORT: ${SMTP_PORT:-587}
NEXT_PRIVATE_SMTP_FROM_NAME: MicProtocol
NEXT_PRIVATE_SMTP_FROM_ADDRESS: ${EMAIL_FROM:-noreply@micprotocol.local}
NEXT_PUBLIC_DISABLE_SIGNUP: "false"
NEXT_PUBLIC_DISABLE_SIGNIN: "false"
volumes:
- documenso_cert:/opt/documenso/cert:ro
depends_on:
postgres:
condition: service_healthy
networks:
- micprotocol
# ─── Chatwoot (Live Chat) ─────────────────────────────────
chatwoot:
image: chatwoot/chatwoot:latest
restart: unless-stopped
entrypoint: docker/entrypoints/rails.sh
command: ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
environment:
NODE_ENV: production
RAILS_ENV: production
INSTALLATION_ENV: docker
SECRET_KEY_BASE: ${CHATWOOT_SECRET_KEY_BASE}
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432
POSTGRES_DATABASE: chatwoot
POSTGRES_USERNAME: ${POSTGRES_USER:-micprotocol}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-micprotocol_secret}
REDIS_URL: redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379
REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_secret}
REDIS_SIDEKIQ_URL: redis://:${REDIS_PASSWORD:-redis_secret}@redis:6379
FRONTEND_URL: ${CHATWOOT_URL:-https://chatwoot.local}
SMTP_ADDRESS: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USERNAME: ${SMTP_USER}
SMTP_PASSWORD: ${SMTP_PASSWORD}
MAILER_SENDER_EMAIL: ${EMAIL_FROM:-Chatwoot <support@micprotocol.local>}
volumes:
- chatwoot_storage:/app/storage
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- micprotocol
# ─── Umami (Analytics) ────────────────────────────────────
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
restart: unless-stopped
environment:
DATABASE_URL: postgresql://${POSTGRES_USER:-micprotocol}:${POSTGRES_PASSWORD:-micprotocol_secret}@postgres:5432/umami?schema=public
DATABASE_TYPE: postgresql
APP_SECRET: ${UMAMI_APP_SECRET}
depends_on:
postgres:
condition: service_healthy
networks:
- micprotocol
# ─── LiveKit (WebRTC SFU) ─────────────────────────────────
livekit:
image: livekit/livekit-server:latest
restart: unless-stopped
ports:
- "7880:7880"
- "7881:7881"
- "7882:7882/udp"
command: >
--config-body
"
port: 7880
rtc:
port_range_start: 7882
port_range_end: 7882
use_external_ip: false
keys:
${LIVEKIT_API_KEY:-devkey}: ${LIVEKIT_API_SECRET:-devsecret}
webhook:
urls: [http://api:4000/integrations/webhooks/livekit]
api_key: ${LIVEKIT_API_KEY:-devkey}
"
networks:
- micprotocol
# ─── SMTP Relay ─────────────────────────────────────────────
# Set SMTP_HOST/SMTP_PORT for integrations that send email directly.
# Resend (app-level email) is configured via RESEND_API_KEY.
networks:
micprotocol:
driver: bridge
volumes:
postgres_data:
redis_data:
listmonk_uploads:
chatwoot_storage:
documenso_cert: