Skip to content

Deploy

$ deploy · 5 min read · updated 2026-05-12

capsol is one Node 22 process plus a filesystem volume. No database, queue, or worker is required.

VariableDefaultNotes
CAPSOL_SECRET_KEYephemeral in devRequired for hosted deployments. Keep it outside the data directory.
CAPSOL_SECRET_KEY_FILEunsetLoad the signing/session secret from an external secret file.
CAPSOL_ADMIN_KEYgenerated on first runBootstrap dashboard unlock key. Set only if you need deterministic first setup.
CAPSOL_DATA_DIR./registry-dataCapsule storage. Mount this as a volume.
PORT4000 from source, 8080 in DockerHTTP port for dashboard, REST, and MCP.
PUBLIC_URLrequest originOverrides generated MCP connection URLs behind proxies.
CAPSOL_TRUST_PROXY1Number of proxy hops Express should trust.
CAPSOL_SCHEMESbuilt-ins onlyJSON array of custom URI scheme definitions.
CAPSOL_RATELIMIT_ENABLEDunset / offSet true to enable built-in token buckets. The dashboard Settings page can also toggle this at runtime.
CAPSOL_RATELIMIT_DISABLEDunsetSet true to force limits off even if enabled elsewhere.
NODE_ENVdevelopmentUse production behind HTTPS for secure cookies.
Terminal window
docker run -d --name capsol \
--restart unless-stopped \
-p 4000:8080 \
-v capsol-data:/data \
-e CAPSOL_SECRET_KEY="$(openssl rand -hex 32)" \
-e CAPSOL_DATA_DIR=/data \
-e NODE_ENV=production \
ghcr.io/arcadeai-labs/capsol-v2:latest

Pre-provision the bootstrap admin key only when you need deterministic first setup:

Terminal window
docker run -d --name capsol \
--restart unless-stopped \
-p 4000:8080 \
-v capsol-data:/data \
-e CAPSOL_SECRET_KEY="$(openssl rand -hex 32)" \
-e CAPSOL_DATA_DIR=/data \
-e NODE_ENV=production \
-e CAPSOL_ADMIN_KEY="$(openssl rand -base64 32)" \
ghcr.io/arcadeai-labs/capsol-v2:latest
docker-compose.yml
services:
capsol:
image: ghcr.io/arcadeai-labs/capsol-v2:latest
restart: unless-stopped
ports: ["4000:8080"]
environment:
CAPSOL_SECRET_KEY: ${CAPSOL_SECRET_KEY}
CAPSOL_ADMIN_KEY: ${CAPSOL_ADMIN_KEY}
CAPSOL_DATA_DIR: /data
NODE_ENV: production
volumes: ["capsol-data:/data"]
volumes:
capsol-data:

The repo ships a fly.toml and Dockerfile. The container listens on internal port 8080; Fly exposes 80/443.

Terminal window
fly launch --copy-config --no-deploy
fly secrets set CAPSOL_SECRET_KEY="$(openssl rand -hex 32)"
fly volumes create capsol_data --size 1 --region sjc
fly deploy

Verify:

Terminal window
curl https://your-app.fly.dev/health
Terminal window
git clone https://github.com/arcadeai-labs/capsol-v2.git /opt/capsol
cd /opt/capsol
npm ci
npm --prefix registry ci
npm run build
/etc/systemd/system/capsol.service
[Unit]
Description=capsol registry
After=network.target
[Service]
Type=simple
User=capsol
WorkingDirectory=/opt/capsol
Environment=NODE_ENV=production
Environment=PORT=4000
Environment=CAPSOL_DATA_DIR=/var/lib/capsol
EnvironmentFile=/etc/capsol/env
ExecStart=/usr/bin/node dist/registry/server.js
Restart=on-failure
RestartSec=5
ProtectSystem=strict
ReadWritePaths=/var/lib/capsol
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target

/etc/capsol/env:

CAPSOL_SECRET_KEY=<paste-generated-secret-here>
CAPSOL_ADMIN_KEY=<paste-generated-key-here>
PUBLIC_URL=https://capsol.example.com

capsol does not terminate TLS. Put nginx, Caddy, Cloudflare, Fly, or another TLS terminator in front.

server {
listen 443 ssl;
server_name capsol.example.com;
location / {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Set PUBLIC_URL=https://capsol.example.com or save the same value in Settings → Hosted URLs so dashboard/API responses return public MCP URLs.

For OAuth MCP clients, the public URL must match the tunnel URL exactly. Start ngrok and copy its HTTPS forwarding URL:

Terminal window
ngrok http 4000

Then either paste that URL into Settings → Hosted URLs → Public URL, or start capsol with PUBLIC_URL=https://your-subdomain.ngrok-free.app. The dashboard setting takes effect immediately for OAuth discovery and generated MCP URLs.

Verify discovery before adding the MCP server to Cursor or ChatGPT:

Terminal window
curl -s https://your-subdomain.ngrok-free.app/.well-known/oauth-authorization-server | jq .issuer
curl -s https://your-subdomain.ngrok-free.app/.well-known/oauth-protected-resource | jq .resource

Both values must use the https://...ngrok... origin, never 127.0.0.1. If you already connected a client while PUBLIC_URL was local, remove and re-add that MCP server so it performs DCR again.

Everything durable is under CAPSOL_DATA_DIR.

Terminal window
tar -C /var/lib/capsol -czf /backups/capsol-$(date +%F).tar.gz .

Restore by stopping capsol, extracting the archive back into CAPSOL_DATA_DIR, and starting capsol again.

EndpointAuthPurpose
GET /healthnoneLiveness probe.
GET /api/agentsadmin cookie/keyActive MCP clients snapshot.
GET /api/agents/streamadmin cookie/keyDashboard activity SSE.
/admin cookie/key after loginDashboard.
  • registry/server.ts — Express 5 registry wrapper.
  • registry/auth.ts — admin cookie and owner-token auth.
  • registry/store.ts — boxes, shares, and activity persistence.
  • Access — credential model.
  • Architecture — server factory and storage layout.