# Deployment Guide ## Prerequisites - **Node.js** >= 20 (LTS recommended) - **pnpm** (installed via corepack: `corepack enable && corepack prepare pnpm@latest --activate`) ### Ubuntu / Debian `better-sqlite3` requires native compilation tools: ```bash sudo apt-get update sudo apt-get install -y python3 make g++ ``` ### macOS Xcode command line tools are required: ```bash xcode-select --install ``` ## Quick Start (Development) ```bash cp .env.example .env.local pnpm install pnpm dev ``` Open http://localhost:3000. Login with `AUTH_USER` / `AUTH_PASS` from your `.env.local`. ## Production (Direct) ```bash pnpm install --frozen-lockfile pnpm build pnpm start ``` The `pnpm start` script binds to `0.0.0.0:3005`. Override with: ```bash PORT=3000 pnpm start ``` **Important:** The production build bundles platform-specific native binaries. You must run `pnpm install` and `pnpm build` on the same OS and architecture as the target server. A build created on macOS will not work on Linux. ## Production (Standalone) Use this for bare-metal deployments that run Next's standalone server directly. This path is preferred over ad hoc `node .next/standalone/server.js` because it syncs `.next/static` and `public/` into the standalone bundle before launch. ```bash pnpm install --frozen-lockfile pnpm build pnpm start:standalone ``` For a full in-place update on the target host: ```bash BRANCH=fix/refactor PORT=3000 pnpm deploy:standalone ``` What `deploy:standalone` does: - fetches and fast-forwards the requested branch - reinstalls dependencies with the lockfile - rebuilds from a clean `.next/` - stops the old process bound to the target port - starts the standalone server through `scripts/start-standalone.sh` - verifies that the rendered login page references a CSS asset and that the CSS is served as `text/css` ## Production (Docker) ```bash docker build -t mission-control . docker run -p 3000:3000 \ -v mission-control-data:/app/.data \ -e AUTH_USER=admin \ -e AUTH_PASS=your-secure-password \ -e API_KEY=your-api-key \ mission-control ``` The Docker image: - Builds from `node:20-slim` with multi-stage build - Compiles `better-sqlite3` natively inside the container (Linux x64) - Uses Next.js standalone output for minimal image size - Runs as non-root user `nextjs` - Exposes port 3000 (override with `-e PORT=8080`) ### Persistent Data SQLite database is stored in `/app/.data/` inside the container. Mount a volume to persist data across restarts: ```bash docker run -v /path/to/data:/app/.data ... ``` ## Environment Variables See `.env.example` for the full list. Key variables: | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `AUTH_USER` | Yes | `admin` | Admin username (seeded on first run) | | `AUTH_PASS` | Yes | - | Admin password | | `AUTH_PASS_B64` | No | - | Base64-encoded admin password (overrides `AUTH_PASS` if set) | | `API_KEY` | Yes | - | API key for headless access | | `PORT` | No | `3005` (direct) / `3000` (Docker) | Server port | | `OPENCLAW_HOME` | No | - | Path to OpenClaw installation | | `MC_ALLOWED_HOSTS` | No | `localhost,127.0.0.1` | Allowed hosts in production | ## Kubernetes Sidecar Deployment When running Mission Control alongside a gateway as containers in the same pod (sidecar pattern), agents are not discovered via the filesystem. Instead, use the gateway's agent registration API. ### Architecture ``` ┌──────────────── Pod ────────────────┐ │ ┌─────────┐ ┌───────────────┐ │ │ │ MC │◄───►│ Gateway │ │ │ │ :3000 │ │ :18789 │ │ │ └─────────┘ └───────────────┘ │ │ ▲ ▲ │ │ │ localhost │ │ │ └──────────────────┘ │ └─────────────────────────────────────┘ ``` ### Required Configuration **Environment variables** for the MC container: ```bash AUTH_USER=admin AUTH_PASS= API_KEY= OPENCLAW_GATEWAY_HOST=127.0.0.1 NEXT_PUBLIC_GATEWAY_PORT=18789 ``` ### Agent Registration The gateway must register its agents with MC on startup. Include the `agents` array in the gateway registration request: ```bash curl -X POST http://localhost:3000/api/gateways \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "sidecar-gateway", "host": "127.0.0.1", "port": 18789, "is_primary": true, "agents": [ { "name": "developer-1", "role": "developer" }, { "name": "researcher-1", "role": "researcher" } ] }' ``` To update the agent list on reconnect, use `PUT /api/gateways` with the same `agents` field. Alternatively, each agent can register itself via the direct connection endpoint: ```bash curl -X POST http://localhost:3000/api/connect \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "tool_name": "openclaw-gateway", "agent_name": "developer-1", "agent_role": "developer" }' ``` ### Health Checks Agents must send heartbeats to stay visible: ```bash curl http://localhost:3000/api/agents//heartbeat \ -H "Authorization: Bearer " ``` Without heartbeats, agents will be marked offline after 10 minutes (configurable via `general.agent_timeout_minutes` setting). ## Troubleshooting ### "Module not found: better-sqlite3" Native compilation failed. On Ubuntu/Debian: ```bash sudo apt-get install -y python3 make g++ rm -rf node_modules pnpm install ``` ### AUTH_PASS with "#" is not working In dotenv files, `#` starts a comment unless the value is quoted. Use one of these: - `AUTH_PASS="my#password"` - `AUTH_PASS_B64=$(echo -n 'my#password' | base64)` ### "pnpm-lock.yaml not found" during Docker build If your deployment context omits `pnpm-lock.yaml`, Docker build now falls back to `pnpm install --no-frozen-lockfile`. For reproducible builds, include `pnpm-lock.yaml` in the build context. ### "Invalid ELF header" or "Mach-O" errors The native binary was compiled on a different platform. Rebuild: ```bash rm -rf node_modules .next pnpm install pnpm build ``` ### Database locked errors Ensure only one instance is running against the same `.data/` directory. SQLite uses WAL mode but does not support multiple writers. ### "Gateway error: origin not allowed" Your gateway is rejecting the Mission Control browser origin. Add the Control UI origin to your gateway config allowlist, for example: ```json { "gateway": { "controlUi": { "allowedOrigins": ["http://YOUR_HOST:3000"] } } } ``` Then restart the gateway and reconnect from Mission Control. ### "Gateway error: device identity required" Device identity signing uses WebCrypto and requires a secure browser context. Open Mission Control over HTTPS (or localhost), then reconnect. ### "Gateway shows offline on VPS deployment" Browser WebSocket connections to non-standard ports (like 18789/18790) are often blocked by VPS firewall/provider rules. Quick option: ```bash NEXT_PUBLIC_GATEWAY_OPTIONAL=true ``` This runs Mission Control in standalone mode (core features available, live gateway streams unavailable). Production option: reverse-proxy gateway WebSocket over 443. nginx example: ```nginx location /gateway-ws { proxy_pass http://127.0.0.1:18789; 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; } ``` Then point UI to: ```bash NEXT_PUBLIC_GATEWAY_URL=wss://your-domain.com/gateway-ws ``` ## Next Steps Once deployed, set up your agents and orchestration: - **[Quickstart](quickstart.md)** — Register your first agent and complete a task in 5 minutes - **[Agent Setup](agent-setup.md)** — SOUL personalities, heartbeats, config sync, agent sources - **[Orchestration Patterns](orchestration.md)** — Auto-dispatch, quality review, multi-agent workflows - **[CLI Reference](cli-agent-control.md)** — Full CLI command list for headless/scripted usage