Files
multica/SELF_HOSTING.md
Jiayuan Zhang 2d4b959407 fix(docker): remove COPY for non-existent tsconfig/node_modules (#661)
* 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>
2026-04-10 20:58:32 +08:00

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:

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.

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.

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:

  1. Install the CLI

    brew tap multica-ai/tap
    brew install multica-cli
    
  2. Install an AI agent CLI — at least one of:

  3. 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/ws
    

    Note: Use http:// and ws:// for local deployments without TLS. Use https:// and wss:// 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
    
  4. Authenticate and start

    # Login (opens browser to your frontend)
    multica login
    
    # Start the daemon
    multica daemon start
    

    The 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.