5.3 KiB
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:
sudo apt-get update
sudo apt-get install -y python3 make g++
macOS
Xcode command line tools are required:
xcode-select --install
Quick Start (Development)
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)
pnpm install --frozen-lockfile
pnpm build
pnpm start
The pnpm start script binds to 0.0.0.0:3005. Override with:
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.
pnpm install --frozen-lockfile
pnpm build
pnpm start:standalone
For a full in-place update on the target host:
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)
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-slimwith multi-stage build - Compiles
better-sqlite3natively 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:
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 |
Troubleshooting
"Module not found: better-sqlite3"
Native compilation failed. On Ubuntu/Debian:
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:
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:
{
"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:
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:
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:
NEXT_PUBLIC_GATEWAY_URL=wss://your-domain.com/gateway-ws