MicProtocol / docs /deploy-all-services.md
Benaah's picture
Upload folder using huggingface_hub
b17baae verified
|
Raw History Blame Contribute Delete
17.9 kB

MicProtocol – Full Deployment Guide (All Services)

This guide covers deploying the complete MicProtocol stack β€” core platform, real-time engine, and all self-hosted integrations β€” on a single server using Docker Compose.

Prerequisites

Requirement Minimum Recommended
OS Ubuntu 22.04 / Debian 12 Ubuntu 24.04 LTS
CPU 8 cores 16+ cores
RAM 16 GB 32+ GB
Disk 80 GB SSD 200+ GB NVMe
Docker 24.0+ 27+
Docker Compose v2.20+ v2.29+
Domain 1 root domain Wildcard DNS (*.yourdomain.com)

Architecture Overview

                    Caddy (TLS + Reverse Proxy)
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  *.yourdomain.com β†’ :3000     β”‚
                    β”‚  api.*           β†’ :4000      β”‚
                    β”‚  realtime.*      β†’ :4001 (WS) β”‚
                    β”‚  admin.*         β†’ :3006      β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚                 β”‚                     β”‚
       β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
       β”‚  Web    β”‚     β”‚   API     β”‚        β”‚   Admin     β”‚
       β”‚  :3000  │────▢│   :4000   │◀───────│   :3006     β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚              β”‚                  β”‚
         β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”
         β”‚ Postgres β”‚   β”‚  Redis  β”‚       β”‚ Meilisearchβ”‚
         β”‚  :5432   β”‚   β”‚  :6379  β”‚       β”‚   :7700    β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚         β”‚         β”‚         β”‚       β”‚       β”‚
β”Œβ”€β”€β”€β–Όβ”€β”€β”€β” β”Œβ”€β”€β–Όβ”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β” β”Œβ”€β”€β–Όβ”€β”€β” β”Œβ”€β”€β–Όβ”€β”€β” β”Œβ”€β”€β–Όβ”€β”€β”
β”‚Cal.comβ”‚ β”‚Chat-β”‚ β”‚Form-   β”‚ β”‚Docu-β”‚ β”‚List-β”‚ β”‚Umamiβ”‚
β”‚ :3002 β”‚ β”‚woot β”‚ β”‚bricks  β”‚ β”‚mensoβ”‚ β”‚monk β”‚ β”‚:3003β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚:3005β”‚ β”‚ :3001  β”‚ β”‚:3004β”‚ β”‚:9000β”‚ β””β”€β”€β”€β”€β”€β”˜
          β””β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”˜

Step 1: Generate Secrets

Run the secrets generator to produce strong random values for every service:

cd /path/to/MicProtocol
bash scripts/generate-secrets.sh > .env.secrets

Review the output, then merge it into your .env file. Never commit .env to git.

Step 2: Configure Environment

Copy the example and fill in your values:

cp .env.example .env

Required Variables

At minimum, set these before the first boot:

Variable Where to get it
POSTGRES_PASSWORD Generated by generate-secrets.sh
REDIS_PASSWORD Generated by generate-secrets.sh
COOKIE_SECRET Generated by generate-secrets.sh
CLERK_SECRET_KEY Clerk Dashboard β†’ API Keys
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY Clerk Dashboard β†’ API Keys
WEB_URL Your public web app URL (e.g. https://yourdomain.com)
API_URL Your public API URL (e.g. https://api.yourdomain.com)
RESEND_API_KEY Resend Dashboard (or your SMTP relay)
R2_ACCESS_KEY_ID Cloudflare R2 dashboard
R2_SECRET_ACCESS_KEY Cloudflare R2 dashboard
R2_ENDPOINT Cloudflare R2 endpoint
R2_BUCKET R2 bucket name

Self-Hosted Integration Variables

Each integration needs its own secrets (all generated by generate-secrets.sh):

Integration Key Variables
Cal.com CALCOM_NEXTAUTH_SECRET, CALCOM_ENCRYPTION_KEY, CALCOM_WEBHOOK_SECRET, CALCOM_API_KEY
Chatwoot CHATWOOT_SECRET_KEY_BASE, CHATWOOT_WEBHOOK_SECRET, CHATWOOT_API_KEY
Formbricks FORMBRICKS_NEXTAUTH_SECRET, FORMBRICKS_ENCRYPTION_KEY, FORMBRICKS_CRON_SECRET, FORMBRICKS_HUB_API_KEY, FORMBRICKS_CUBE_API_SECRET, FORMBRICKS_WEBHOOK_SECRET
Documenso DOCUMENSO_NEXTAUTH_SECRET, DOCUMENSO_ENCRYPTION_KEY, DOCUMENSO_ENCRYPTION_SECONDARY_KEY, DOCUMENSO_SIGNING_PASSPHRASE
Listmonk LISTMONK_ADMIN_PASSWORD, LISTMONK_API_TOKEN, LISTMONK_WEBHOOK_SECRET
Umami UMAMI_APP_SECRET
LiveKit LIVEKIT_API_KEY, LIVEKIT_API_SECRET
mic-realtime MIC_REALTIME_INTERNAL_TOKEN, MIC_REALTIME_SHARED_SECRET, MIC_REALTIME_PUBLIC_URL

Production URLs

Replace all http://localhost:* URLs with your actual domain:

# Public-facing
WEB_URL=https://yourdomain.com
API_URL=https://api.yourdomain.com
NEXT_PUBLIC_API_URL=https://api.yourdomain.com

# Self-hosted integration public URLs
CALCOM_URL=https://cal.yourdomain.com
CHATWOOT_URL=https://support.yourdomain.com
DOCUMENSO_URL=https://sign.yourdomain.com
FORMBRICKS_URL=https://surveys.yourdomain.com
LISTMONK_URL=https://mail.yourdomain.com
NEXT_PUBLIC_UMAMI_URL=https://analytics.yourdomain.com

# Real-time (MUST be wss:// in production)
MIC_REALTIME_PUBLIC_URL=wss://realtime.yourdomain.com
NEXT_PUBLIC_MIC_REALTIME_URL=wss://realtime.yourdomain.com

Step 3: Configure DNS

For wildcard TLS to work, you need a DNS-01 challenge. Add these DNS records:

Record Type Value
@ A Your server IP
* A Your server IP

Or use CNAME records if your DNS provider supports wildcard CNAMEs.

Step 4: Generate Documenso Signing Certificate

Documenso requires a PKCS#12 signing certificate:

mkdir -p scripts/documenso-cert

# Generate self-signed cert (replace with your details)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 3650 -nodes \
  -subj "/CN=Documenso Signing/O=MicProtocol"

# Convert to PKCS#12
openssl pkcs12 -export -out cert.p12 -inkey key.pem -in cert.pem \
  -passout pass:${DOCUMENSO_SIGNING_PASSPHRASE}

# Move to the expected location
mv cert.p12 scripts/documenso-cert/
rm -f key.pem cert.pem

Step 5: Start Core Infrastructure

Start the shared infrastructure first β€” these must be healthy before anything else:

# Start Postgres, Redis, Meilisearch, MinIO
docker compose up -d postgres redis meilisearch minio minio-createbuckets

# Wait for health checks to pass
docker compose ps  # All should show "healthy"

Verify Postgres is ready:

docker exec -it micprotocol-postgres pg_isready -U micprotocol

The init-databases.sql script runs automatically on first boot, creating databases for: chatwoot, calcom, documenso, formbricks, listmonk, umami.

Step 6: Start the API and Worker

docker compose up -d api worker

The API will:

  1. Run Prisma migrations against the micprotocol database
  2. Start the NestJS server on :4000
  3. Expose tRPC, REST, and webhook endpoints

Verify:

curl -s http://localhost:4000/health

Step 7: Start the Real-Time Engine

docker compose up -d mic-realtime

Verify:

curl -s http://localhost:4001/healthz

Important: MIC_REALTIME_INTERNAL_TOKEN and MIC_REALTIME_SHARED_SECRET must be strong random strings. The service fails closed at startup if they are missing or set to defaults.

Step 8: Start Web and Admin

docker compose up -d web admin

Verify:

curl -s -o /dev/null -w "%{http_code}" http://localhost:3000  # Web
curl -s -o /dev/null -w "%{http_code}" http://localhost:3006  # Admin

Step 9: Start Self-Hosted Integrations

Start all integrations (or selectively enable ones you need):

# Start everything at once
docker compose up -d

# OR start individual services:
docker compose up -d calcom              # Scheduling
docker compose up -d chatwoot-web chatwoot-worker  # Live chat
docker compose up -d formbricks-migrate formbricks-hub-migrate formbricks formbricks-hub formbricks-cube  # Surveys
docker compose up -d documenso           # E-signatures
docker compose up -d listmonk            # Email marketing
docker compose up -d umami               # Analytics
docker compose up -d livekit             # WebRTC recording

Integration Startup Order

Some services have dependencies that Docker Compose handles automatically:

Postgres (healthy)
  β”œβ”€β”€ formbricks-migrate (runs once)
  β”‚     └── formbricks-hub-migrate (runs once)
  β”‚           └── formbricks-cube (healthy)
  β”‚                 └── formbricks-hub (healthy)
  β”‚                       └── formbricks (healthy)
  β”œβ”€β”€ calcom (depends on Postgres + Redis)
  β”œβ”€β”€ chatwoot-web + chatwoot-worker (depends on Postgres + Redis)
  β”œβ”€β”€ documenso (depends on Postgres)
  β”œβ”€β”€ listmonk (depends on Postgres)
  └── umami (depends on Postgres)

Step 10: Start Dev SMTP (Optional)

For local development, Mailhog captures all outbound email:

docker compose up -d mailhog
  • SMTP server: localhost:1025
  • Web UI: http://localhost:8025

In production, replace with your transactional SMTP relay (Resend, SES, Postmark, etc.) by setting SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD.

Step 11: Start CLIP Scorer (Optional)

The CLIP scoring engine is CPU/GPU intensive. Start it only if you need AI-powered clip scoring:

docker compose up -d clip-scorer

Resource requirements:

  • CPU: 2 cores minimum
  • Memory: 2 GB minimum
  • Optional GPU: Cuts inference time by 5-10x

Step 12: Configure Caddy (TLS + Reverse Proxy)

The Caddyfile handles TLS termination and routing. Set the ROOT_DOMAIN and CLOUDFLARE_API_TOKEN environment variables:

export ROOT_DOMAIN=yourdomain.com
export CLOUDFLARE_API_TOKEN=your_cloudflare_api_token

Start Caddy:

docker compose up -d caddy

Or install Caddy on the host and run it with the provided Caddyfile:

# Install Caddy
sudo apt install -y caddy

# Copy the Caddyfile
sudo cp Caddyfile /etc/caddy/Caddyfile

# Set environment
export ROOT_DOMAIN=yourdomain.com
export CLOUDFLARE_API_TOKEN=your_token

# Start
sudo systemctl restart caddy

Caddy Routing Table

Host Upstream Protocol
yourdomain.com / *.yourdomain.com web:3000 HTTPS
api.yourdomain.com api:4000 HTTPS
realtime.yourdomain.com mic-realtime:4001 WSS
admin.yourdomain.com admin:3006 HTTPS

Step 13: Run Database Migrations

If this is a fresh deployment, run Prisma migrations:

docker exec -it micprotocol-api npx prisma migrate deploy

Or generate the Prisma client and push the schema:

docker exec -it micprotocol-api npx prisma generate
docker exec -it micprotocol-api npx prisma db push

Step 14: Post-Deployment Setup

Cal.com

  1. Open https://cal.yourdomain.com
  2. Create admin account
  3. Go to Settings β†’ API Keys β†’ generate a key
  4. Set CALCOM_API_KEY in .env
  5. Restart the API: docker compose restart api

Chatwoot

  1. Open https://support.yourdomain.com
  2. Complete the setup wizard
  3. Go to Settings β†’ Agents β†’ create an agent
  4. Go to Settings β†’ Inboxes β†’ create a website inbox
  5. Copy the inbox ID to CHATWOOT_INBOX_ID
  6. Copy the website token to NEXT_PUBLIC_CHATWOOT_WEBSITE_TOKEN
  7. Restart the API

Formbricks

  1. Open https://surveys.yourdomain.com
  2. Create admin account
  3. Go to Settings β†’ Environments β†’ copy the environment ID
  4. Set NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID
  5. Go to Settings β†’ API Keys β†’ create a key
  6. Set FORMBRICKS_API_KEY
  7. Restart the API

Documenso

  1. Open https://sign.yourdomain.com
  2. Create admin account
  3. Go to Settings β†’ API β†’ create a key
  4. Set DOCUMENSO_API_KEY
  5. Restart the API

Listmonk

  1. Open https://mail.yourdomain.com
  2. Log in with LISTMONK_ADMIN_USER / LISTMONK_ADMIN_PASSWORD
  3. Go to Settings β†’ API β†’ generate a token
  4. Set LISTMONK_API_TOKEN
  5. Create a mailing list and copy its UUID to NEXT_PUBLIC_LISTMONK_LIST_UUID
  6. Configure webhooks: POST to http://api:4000/integrations/webhooks/listmonk with header x-listmonk-secret: <LISTMONK_WEBHOOK_SECRET>
  7. Restart the API

Umami

  1. Open https://analytics.yourdomain.com
  2. Log in with default credentials (admin/umami)
  3. Change the admin password
  4. Add a website and copy the website ID to NEXT_PUBLIC_UMAMI_WEBSITE_ID
  5. Restart the web app

LiveKit

  1. The recording studio connects to LiveKit on port 7880
  2. For internet-facing deployments, ensure UDP port 7882 is open
  3. If behind a firewall, configure a TURN server or set rtc.use_external_ip: true

Health Checks

Verify all services are running:

# Core
curl -s http://localhost:4000/health          # API
curl -s http://localhost:4001/healthz          # mic-realtime
curl -s http://localhost:3000                  # Web
curl -s http://localhost:3006                  # Admin

# Integrations
curl -s http://localhost:3002/api/health       # Cal.com
curl -s http://localhost:3005/health           # Chatwoot
curl -s http://localhost:3001/api/health       # Formbricks
curl -s http://localhost:3004/api/health       # Documenso
curl -s http://localhost:9000/health           # Listmonk
curl -s http://localhost:3003/api/health       # Umami
curl -s http://localhost:7880/health           # LiveKit

# Consolidated check
curl -s http://localhost:4000/integrations/health | jq

Port Reference

Port Service Protocol
3000 Web (Next.js) HTTP
3001 Formbricks HTTP
3002 Cal.com HTTP
3003 Umami HTTP
3004 Documenso HTTP
3005 Chatwoot HTTP
3006 Admin HTTP
4000 API (NestJS) HTTP
4001 mic-realtime HTTP/WS
5432 PostgreSQL TCP
6379 Redis TCP
7700 Meilisearch HTTP
7880 LiveKit HTTP
7881 LiveKit TCP
7882 LiveKit UDP
8025 Mailhog UI HTTP
9000 Listmonk HTTP
9001 MinIO API HTTP
9090 MinIO Console HTTP
1025 Mailhog SMTP TCP

Resource Limits

Default limits from docker-compose.yml:

Service CPUs Memory
API 2 2 GB
Worker 1 1 GB
CLIP Scorer 2 2 GB
mic-realtime 1.5 1 GB

Other services have no explicit limits. Tune based on your hardware.

Common Operations

Stop everything

docker compose down            # Keep data
docker compose down -v         # Wipe all data (destructive)

Update a single service

docker compose pull <service>
docker compose up -d --no-deps <service>

Update Formbricks (requires migration re-run)

docker compose up formbricks-migrate
docker compose up formbricks-hub-migrate
docker compose up -d --no-deps formbricks formbricks-hub formbricks-cube

View logs

docker compose logs -f api           # API logs
docker compose logs -f mic-realtime  # Real-time logs
docker compose logs -f chatwoot-web  # Chatwoot logs

Restart a service

docker compose restart api

Enter a container

docker exec -it micprotocol-api sh
docker exec -it micprotocol-postgres psql -U micprotocol

Troubleshooting

Service won't start

  1. Check logs: docker compose logs <service>
  2. Verify env vars are set in .env
  3. Check that dependent services are healthy: docker compose ps

Database connection refused

  1. Ensure Postgres is healthy: docker exec micprotocol-postgres pg_isready
  2. Verify DATABASE_URL uses the container hostname (postgres), not localhost

Real-time WebSocket not connecting

  1. Ensure MIC_REALTIME_PUBLIC_URL is a wss:// URL in production
  2. Check that MIC_REALTIME_INTERNAL_TOKEN and MIC_REALTIME_SHARED_SECRET are set
  3. Verify Caddy is routing realtime.* correctly

Formbricks won't start

  1. Ensure all 5 Formbricks containers start in order
  2. Check that SKIP_STARTUP_MIGRATION=true is set on the web container
  3. Verify Cube.js is healthy before Formbricks web starts

Outbound email not working

  1. Check SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD in .env
  2. In dev, ensure Mailhog is running: docker compose up -d mailhog
  3. Check Mailhog UI at http://localhost:8025

Disabling Services

Any integration can be removed by deleting its block from docker-compose.yml and omitting its env vars from .env. The API degrades gracefully β€” missing integrations return false from health checks and log warnings rather than crashing.

Security Checklist

  • All default passwords changed
  • .env not committed to git
  • Firewall configured (only ports 80, 443, and SSH open)
  • TLS working via Caddy
  • MIC_REALTIME_INTERNAL_TOKEN and MIC_REALTIME_SHARED_SECRET are strong random values
  • Encryption keys (*_ENCRYPTION_KEY) generated and backed up (never rotate after first use)
  • Webhook secrets configured on both sides
  • SMTP relay configured (not Mailhog) in production
  • DEV_AUTH_BYPASS_ENABLED=false in production