# How to Self-Host Multica with a Custom Domain and Reverse Proxy: Complete Setup Guide

> Learn to self-host Multica using a custom domain and reverse proxy. This guide details setting up the service stack, configuring variables, and rebuilding the frontend for a seamless deployment.

- Repository: [multica-ai/multica](https://github.com/multica-ai/multica)
- Tags: how-to-guide
- Published: 2026-04-11

---

**Self-host Multica by deploying its three-core service stack behind an HTTPS-terminating reverse proxy, configuring environment variables for your custom domains, and rebuilding the frontend to bake in the public API URLs.**

Multica is an open-source AI agent platform written in Go and Next.js. When you self-host the `multica-ai/multica` repository, you run PostgreSQL, a Go backend API, and a Next.js frontend on your own infrastructure. Exposing these services through a custom domain requires specific environment configuration and a reverse proxy to handle TLS termination, CORS headers, and WebSocket upgrades.

## Deploy the Core Services

Start by cloning the repository and launching the pre-configured Docker Compose stack. The project ships with **[`docker-compose.selfhost.yml`](https://github.com/multica-ai/multica/blob/main/docker-compose.selfhost.yml)**, which defines services for PostgreSQL 17 (with pgvector), the Go backend, and the Next.js frontend.

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

# Edit .env to set your custom domains (see next section)

docker compose -f docker-compose.selfhost.yml up -d

```

By default, the backend listens on port `8080` and the frontend on port `3000`. These internal ports remain inaccessible to the internet until you place a reverse proxy in front of them.

## Configure Environment Variables for Custom Domains

Before starting the stack, edit the `.env` file to inform the backend and frontend about your public URLs. According to **[`SELF_HOSTING.md`](https://github.com/multica-ai/multica/blob/main/SELF_HOSTING.md)** (lines 66-77 and 45-53), you must set the following variables:

```text

# Public origins for CORS and URL generation

FRONTEND_ORIGIN=https://app.example.com
CORS_ALLOWED_ORIGINS=https://app.example.com

# URLs advertised to the daemon and CLI clients

MULTICA_APP_URL=https://app.example.com
MULTICA_SERVER_URL=wss://api.example.com/ws

# Frontend build-time configuration

NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_WS_URL=wss://api.example.com/ws
REMOTE_API_URL=https://api.example.com

```

**Critical distinction:** `NEXT_PUBLIC_*` variables are embedded during the build process, while `REMOTE_API_URL` is consumed by **`Dockerfile.web`** as a build argument (lines 60-64 of [`docker-compose.selfhost.yml`](https://github.com/multica-ai/multica/blob/main/docker-compose.selfhost.yml)). If you change these after the first build, you must rebuild the frontend image for the changes to take effect.

## Set Up the Reverse Proxy

Place an HTTPS-terminating reverse proxy in front of ports `3000` (frontend) and `8080` (backend). The proxy must forward HTTP traffic and handle WebSocket upgrades on the `/ws` path for daemon connections.

### Option 1: Caddy (Automatic TLS)

Create a `Caddyfile` at `/etc/caddy/Caddyfile`:

```text
app.example.com {
    reverse_proxy localhost:3000
}

api.example.com {
    reverse_proxy localhost:8080
}

```

Caddy automatically provisions TLS certificates via Let's Encrypt. No additional configuration is required for WebSocket support, as Caddy handles the `Upgrade` header transparently.

### Option 2: Nginx (Explicit Control)

For manual certificate management, use this configuration adapted from **[`SELF_HOSTING.md`](https://github.com/multica-ai/multica/blob/main/SELF_HOSTING.md)** (lines 81-140):

```nginx

# Frontend server block

server {
    listen 443 ssl;
    server_name app.example.com;

    ssl_certificate /etc/ssl/certs/your-cert.pem;
    ssl_certificate_key /etc/ssl/private/your-key.key;

    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 block

server {
    listen 443 ssl;
    server_name api.example.com;

    ssl_certificate /etc/ssl/certs/your-cert.pem;
    ssl_certificate_key /etc/ssl/private/your-key.key;

    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 for persistent daemon connections

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

```

The `/ws` location is critical: it enables the Go backend's WebSocket handler (located in **`server/internal/handler`**) to maintain long-lived connections with CLI agents.

## Build and Deploy the Frontend

If you modified `REMOTE_API_URL` or any `NEXT_PUBLIC_*` variables, you must rebuild the frontend image to embed the new API endpoints into the static Next.js bundle:

```bash
docker compose -f docker-compose.selfhost.yml build frontend
docker compose -f docker-compose.selfhost.yml up -d frontend

```

The build process reads `REMOTE_API_URL` from the environment and bakes it into the client-side JavaScript, allowing the browser to reach your custom API domain.

## Verify the Installation

Test your self-hosted Multica instance using these validation steps:

1. **Browser verification:** Navigate to `https://app.example.com`. The Multica UI should load without CORS errors (permitted by `FRONTEND_ORIGIN`).
2. **API health check:** Request `https://api.example.com/health` to receive `{"status":"ok"}`. The Go backend exposes this endpoint in the handler package for load balancer health checks.
3. **CLI configuration:** Configure the Multica CLI to point to your custom domains:

```bash
export MULTICA_APP_URL=https://app.example.com
export MULTICA_SERVER_URL=wss://api.example.com/ws
multica login

```

The `login` command will open your custom domain for authentication, and `daemon start` will establish a WebSocket connection through your reverse proxy to `wss://api.example.com/ws`.

## Upgrading Your Instance

When updating to the latest version, pull the repository changes and rebuild:

```bash
git pull
docker compose -f docker-compose.selfhost.yml up -d --build

```

Database migrations run automatically when the backend container starts, as implemented in the server initialization code referenced in **[`SELF_HOSTING.md`](https://github.com/multica-ai/multica/blob/main/SELF_HOSTING.md)**.

## Summary

- **Clone and configure:** Use [`docker-compose.selfhost.yml`](https://github.com/multica-ai/multica/blob/main/docker-compose.selfhost.yml) from the `multica-ai/multica` repository and copy `.env.example` to `.env`.
- **Set public URLs:** Define `FRONTEND_ORIGIN`, `CORS_ALLOWED_ORIGINS`, `MULTICA_APP_URL`, `MULTICA_SERVER_URL`, and `NEXT_PUBLIC_*` variables to match your custom domains.
- **Proxy traffic:** Deploy Caddy or Nginx to terminate TLS and forward traffic to localhost ports `3000` and `8080`, ensuring the `/ws` path supports WebSocket upgrades.
- **Rebuild frontend:** Run `docker compose build frontend` whenever you change `REMOTE_API_URL` or public API variables.
- **Verify:** Check the `/health` endpoint and authenticate via the CLI to confirm end-to-end connectivity.

## Frequently Asked Questions

### Do I need to rebuild the frontend every time I change the API URL?

**Yes.** The `NEXT_PUBLIC_API_URL` and `REMOTE_API_URL` values are baked into the Next.js static bundle at build time. If you change these environment variables after the initial deployment, you must run `docker compose -f docker-compose.selfhost.yml build frontend` and restart the container for the browser to use the new endpoints.

### Which reverse proxy is recommended for Multica?

**Caddy is simplest** for most users because it handles TLS provisioning and WebSocket upgrades automatically. However, if you require specific security headers, custom access controls, or already run Nginx in your infrastructure, the explicit Nginx configuration provided in **[`SELF_HOSTING.md`](https://github.com/multica-ai/multica/blob/main/SELF_HOSTING.md)** (lines 81-140) gives you finer control over the proxy behavior.

### Why does my daemon fail to connect with a WebSocket error?

**Check your reverse proxy configuration.** The backend's WebSocket handler expects the proxy to forward the `Upgrade` and `Connection` headers on the `/ws` path. In Nginx, you must include `proxy_http_version 1.1` and the `Upgrade` header directives. Caddy handles this automatically. Also verify that `MULTICA_SERVER_URL` uses `wss://` (secure WebSocket) when HTTPS is enabled.

### Can I run Multica without Docker?

**Yes, but it requires manual setup.** You would need to run PostgreSQL 17 with the pgvector extension, build the Go binary from **`Dockerfile`**, and build the Next.js frontend from **`Dockerfile.web`** while injecting the `REMOTE_API_URL` build argument. The Docker Compose stack is the officially supported and tested method for self-hosting.