multica/SELF_HOSTING.md
Junlong 0263ecce9e
Docs: fix self hosting local deploy protocol (#433)
* fix: skip Docker check in ensure-postgres.sh when remote DATABASE_URL is set

When DATABASE_URL points to a non-localhost host, the script now skips
all Docker operations and only verifies remote DB connectivity via
pg_isready directly.

* fix: honor DATABASE_URL for remote postgres preflight

* fix(make): clarify stop output for remote database

* docs: add local deployment protocol guidance to SELF_HOSTING.md

Clarify that local deployments without TLS should use http:// and ws://
instead of https:// and wss://.

---------

Co-authored-by: Junlong Liu <junlong.liu@shopee.com>
2026-04-07 14:08:06 +08:00

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

Prerequisites

  • Docker and Docker Compose (recommended), or:
    • Go 1.26+ (to build from source)
    • Node.js 20+ and pnpm 10.28+ (to build the frontend)
    • PostgreSQL 17 with the pgvector extension

Quick Start (Docker Compose)

git clone https://github.com/multica-ai/multica.git
cd multica
cp .env.example .env

Edit .env with your production values (see Configuration below), then:

# Start PostgreSQL
docker compose up -d

# 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 ./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

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 the Included Docker Compose

docker compose up -d postgres

This starts a pgvector/pgvector:pg17 container on port 5432 with default credentials (multica/multica).

Using Your Own PostgreSQL

Ensure the pgvector extension is available:

CREATE EXTENSION IF NOT EXISTS vector;

Running Migrations

Migrations must be run before starting the server:

# Using the built binary
./server/bin/migrate up

# Or from source
cd server && go run ./cmd/migrate up

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

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. Authenticate and start

    # Point CLI to your server
    #
    # For production deployments with TLS:
    export MULTICA_APP_URL=https://app.example.com
    export MULTICA_SERVER_URL=wss://api.example.com/ws
    #
    # For local deployments without TLS:
    # export MULTICA_APP_URL=http://localhost:3000
    # export MULTICA_SERVER_URL=ws://localhost:8080/ws
    
    # Login (opens browser)
    multica login
    
    # Start the daemon
    multica daemon start
    

    Note: Use https:// and wss:// for production deployments behind a TLS-terminating reverse proxy. For local or development deployments without TLS, use http:// and ws:// instead.

The daemon auto-detects installed agent CLIs and registers itself with the server. When an agent is assigned a task in Multica, the daemon picks it up, creates an isolated workspace, runs the agent, and reports results back.

Upgrading

  1. Pull the latest code or image
  2. Run migrations: ./server/bin/migrate up
  3. Restart the backend and frontend

Migrations are forward-only and safe to run on a live database. They are idempotent — running them multiple times has no effect.