* fix(docker): remove COPY for non-existent tsconfig/node_modules The @multica/tsconfig package has zero dependencies, so pnpm install never creates a node_modules directory for it. The COPY --from=deps instruction fails with "not found" during docker compose build. Closes #658 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(docker): add dotenv as explicit dependency for web app next.config.ts imports dotenv to load .env for REMOTE_API_URL, but dotenv was never declared as a dependency. It worked locally as a hoisted transitive dep but fails in Docker's stricter module resolution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: fix daemon setup instructions for local Docker deployments The daemon setup section in SELF_HOSTING.md had production URLs as the active example and local Docker URLs commented out. Since this is a self-hosting guide, local Docker should be the primary example. Key changes: - Make local Docker URLs the default in daemon setup examples - Add explicit warning that CLI defaults to hosted service - Add 'multica config set' instructions for persistent setup - Add link from Quick Start to daemon setup section - Clarify that daemon runs on host machine, not inside Docker - Update CLI_AND_DAEMON.md self-hosted section similarly Closes #660 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.2 KiB
Self-Hosting Guide
This guide walks you through deploying Multica on your own infrastructure.
Architecture Overview
Multica has three components:
| Component | Description | Technology |
|---|---|---|
| Backend | REST API + WebSocket server | Go (single binary) |
| Frontend | Web application | Next.js 16 |
| Database | Primary data store | PostgreSQL 17 with pgvector |
Additionally, each user who wants to run AI agents locally installs the multica CLI and runs the agent daemon on their own machine.
Quick Start (Docker Compose)
Prerequisites: Docker and Docker Compose.
git clone https://github.com/multica-ai/multica.git
cd multica
cp .env.example .env
Edit .env — at minimum, change JWT_SECRET:
JWT_SECRET=$(openssl rand -hex 32)
Then start everything:
docker compose -f docker-compose.selfhost.yml up -d
This builds and starts PostgreSQL, the backend (with auto-migration), and the frontend:
- Frontend: http://localhost:3000
- Backend API: http://localhost:8080
The backend automatically runs database migrations on startup — no manual migration step needed.
To run AI agents, you also need to set up the daemon on your local machine. See Setting Up the Agent Daemon below.
Rebuilding After Updates
git pull
docker compose -f docker-compose.selfhost.yml up -d --build
Migrations run automatically on each backend startup.
Configuration
All configuration is done via environment variables. Copy .env.example as a starting point.
Required Variables
| Variable | Description | Example |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgres://multica:multica@localhost:5432/multica?sslmode=disable |
JWT_SECRET |
Must change from default. Secret key for signing JWT tokens. Use a long random string. | openssl rand -hex 32 |
FRONTEND_ORIGIN |
URL where the frontend is served (used for CORS) | https://app.example.com |
Email (Required for Authentication)
Multica uses email-based magic link authentication via Resend.
| Variable | Description |
|---|---|
RESEND_API_KEY |
Your Resend API key |
RESEND_FROM_EMAIL |
Sender email address (default: noreply@multica.ai) |
Google OAuth (Optional)
| Variable | Description |
|---|---|
GOOGLE_CLIENT_ID |
Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
Google OAuth client secret |
GOOGLE_REDIRECT_URI |
OAuth callback URL (e.g. https://app.example.com/auth/callback) |
File Storage (Optional)
For file uploads and attachments, configure S3 and CloudFront:
| Variable | Description |
|---|---|
S3_BUCKET |
S3 bucket name |
S3_REGION |
AWS region (default: us-west-2) |
CLOUDFRONT_DOMAIN |
CloudFront distribution domain |
CLOUDFRONT_KEY_PAIR_ID |
CloudFront key pair ID for signed URLs |
CLOUDFRONT_PRIVATE_KEY |
CloudFront private key (PEM format) |
COOKIE_DOMAIN |
Domain for CloudFront auth cookies |
Server
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Backend server port |
FRONTEND_PORT |
3000 |
Frontend port |
CORS_ALLOWED_ORIGINS |
Value of FRONTEND_ORIGIN |
Comma-separated list of allowed origins |
LOG_LEVEL |
info |
Log level: debug, info, warn, error |
CLI / Daemon
These are configured on each user's machine, not on the server:
| Variable | Default | Description |
|---|---|---|
MULTICA_SERVER_URL |
ws://localhost:8080/ws |
WebSocket URL for daemon → server connection |
MULTICA_APP_URL |
http://localhost:3000 |
Frontend URL for CLI login flow |
MULTICA_DAEMON_POLL_INTERVAL |
3s |
How often the daemon polls for tasks |
MULTICA_DAEMON_HEARTBEAT_INTERVAL |
15s |
Heartbeat frequency |
Database Setup
Multica requires PostgreSQL 17 with the pgvector extension.
Using Docker Compose (Recommended)
The docker-compose.selfhost.yml includes PostgreSQL. No separate setup needed.
Using Your Own PostgreSQL
If you prefer to use an existing PostgreSQL instance, ensure the pgvector extension is available:
CREATE EXTENSION IF NOT EXISTS vector;
Set DATABASE_URL in your .env and remove the postgres service from the compose file.
Running Migrations Manually
The Docker Compose setup runs migrations automatically. If you need to run them manually:
# Using the built binary
./server/bin/migrate up
# Or from source
cd server && go run ./cmd/migrate up
Manual Setup (Without Docker Compose)
If you prefer to build and run services manually:
Prerequisites: Go 1.26+, Node.js 20+, pnpm 10.28+, PostgreSQL 17 with pgvector.
# Start your PostgreSQL (or use: docker compose up -d postgres)
# Build the backend
make build
# Run database migrations
DATABASE_URL="your-database-url" ./server/bin/migrate up
# Start the backend server
DATABASE_URL="your-database-url" PORT=8080 JWT_SECRET="your-secret" ./server/bin/server
For the frontend:
pnpm install
pnpm build
# Start the frontend (production mode)
cd apps/web
REMOTE_API_URL=http://localhost:8080 pnpm start
Reverse Proxy
In production, put a reverse proxy in front of both the backend and frontend to handle TLS and routing.
Caddy (Recommended)
app.example.com {
reverse_proxy localhost:3000
}
api.example.com {
reverse_proxy localhost:8080
}
Nginx
# Frontend
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Backend API
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket support
location /ws {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}
When using separate domains for frontend and backend, set these environment variables accordingly:
# Backend
FRONTEND_ORIGIN=https://app.example.com
CORS_ALLOWED_ORIGINS=https://app.example.com
# Frontend (set before building the frontend image)
REMOTE_API_URL=https://api.example.com
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_WS_URL=wss://api.example.com/ws
Health Check
The backend exposes a health check endpoint:
GET /health
→ {"status":"ok"}
Use this for load balancer health checks or monitoring.
Setting Up the Agent Daemon
The daemon runs on your local machine (not inside Docker). It detects installed AI agent CLIs, registers them as runtimes with the server, and executes tasks when agents are assigned work.
Each team member who wants to run AI agents locally needs to:
-
Install the CLI
brew tap multica-ai/tap brew install multica-cli -
Install an AI agent CLI — at least one of:
- Claude Code (
claudeon PATH) - Codex (
codexon PATH)
- Claude Code (
-
Point the CLI to your server
The CLI defaults to the hosted Multica service. For self-hosted setups, you must set the server URLs before logging in:
# Local Docker Compose deployment (default ports): export MULTICA_APP_URL=http://localhost:3000 export MULTICA_SERVER_URL=ws://localhost:8080/ws # Production deployment with TLS: # export MULTICA_APP_URL=https://app.example.com # export MULTICA_SERVER_URL=wss://api.example.com/wsNote: Use
http://andws://for local deployments without TLS. Usehttps://andwss://for production deployments behind a TLS-terminating reverse proxy.You can also set these persistently so you don't need to export them each time:
multica config set app_url http://localhost:3000 multica config set server_url ws://localhost:8080/ws -
Authenticate and start
# Login (opens browser to your frontend) multica login # Start the daemon multica daemon startThe login flow opens your browser, authenticates you via the frontend, and stores a personal access token locally. The daemon then uses this token to register with the backend.
To verify the daemon is running:
multica daemon status
Upgrading
git pull
docker compose -f docker-compose.selfhost.yml up -d --build
Migrations run automatically on backend startup. They are idempotent — running them multiple times has no effect.