Docker Deployment
Self-host CMDOP with the open-source deploy/compose.oss.yml Docker Compose file. It brings up the full stack on a clean box: PostgreSQL 16, Redis 7, a one-shot migrate step, the api_server (REST control plane) on :8000, the grpc_server (relay) on :50051, and a background worker. Configure two required secrets in .env, then terminate TLS at a reverse proxy (Caddy / Traefik / Nginx) in front. This is the recommended self-host path.
Docker Compose is the recommended way to run CMDOP yourself. The open-source server is a multi-process Python stack, and the shipped Compose file wires it all together.
How do I get started with Docker?
# Clone the open-source server
git clone https://github.com/cmdop/cmdop-server
cd cmdop-server
# Copy the env template and set the two required secrets
cp .env.example .env
# Bring the stack up
docker compose -f deploy/compose.oss.yml up --buildThis starts: postgres, redis, migrate (one-shot, runs alembic upgrade head), api on :8000, grpc on :50051, and worker.
What does the Compose stack contain?
The shipped deploy/compose.oss.yml is intentionally minimal — no Traefik, no observability stack. Operators front it with their own gateway for TLS and metrics scraping. Its services:
services:
postgres: # postgres:16-alpine — shared state, RLS-enforced
redis: # redis:7-alpine — token cache, arq queue, output buffers
migrate: # one-shot: alembic upgrade head, then exits
api: # api_server — REST control plane on :8000
grpc: # grpc_server — the relay (agent bidi stream) on :50051
worker: # arq worker — cleanup crons + schedule executor
# optional: jarvis (server-side AI), coturn (NAT traversal for tunnels)api, grpc, and worker all share the same environment block and wait for migrate to complete successfully before starting.
What environment variables are needed?
Set these in .env. Only two are strictly required:
# Required — pick strong values
POSTGRES_PASSWORD=$(openssl rand -hex 16)
INTERNAL_SECRET=$(openssl rand -hex 32)
# Optional — override published host ports (defaults shown)
API_PORT=8000
GRPC_PORT=50051POSTGRES_PASSWORD and INTERNAL_SECRET are docker-compose substitutions — Compose refuses to start without them. The server processes themselves read CMDOP_* variables (already set in the Compose env block): CMDOP_DATABASE_URL, CMDOP_ADMIN_DATABASE_URL, CMDOP_REDIS_URL, CMDOP_DB_MODE=standalone, CMDOP_INTERNAL_SECRET, CMDOP_ENVIRONMENT=oss. See the self-hosted guide and the server’s docs/configuration.md for the full set.
How do I create the first user?
Once api_server.bind appears in the logs:
docker compose -f deploy/compose.oss.yml exec api \
cmdop-admin user create --email [email protected]It prints a bootstrap API key, shown only once. Save it.
How do I set up TLS with Traefik?
The OSS Compose ships plain HTTP / h2c by design. Put Traefik in front to terminate TLS — api_server over HTTP and grpc_server over h2c on the upstream:
http:
routers:
api:
rule: "Host(`api.example.com`)"
service: api
tls: {certResolver: letsencrypt}
grpc:
rule: "Host(`grpc.example.com`)"
service: grpc
tls: {certResolver: letsencrypt}
services:
api:
loadBalancer:
servers:
- url: "http://api:8000"
grpc:
loadBalancer:
servers:
- url: "h2c://grpc:50051"
passHostHeader: truegRPC needs h2c:// (HTTP/2 cleartext) on the upstream; Traefik handles the public-side TLS. Don’t expose the unauthenticated /metrics route on :8000 to the open internet.
How do I view logs?
# All services
docker compose -f deploy/compose.oss.yml logs -f
# A specific service
docker compose -f deploy/compose.oss.yml logs -f grpcConfigure log rotation on the daemon or per-service logging: options as needed.
How do I check health?
# REST liveness / readiness
curl http://localhost:8000/healthz/live
curl http://localhost:8000/healthz/ready
# → {"ok":true,"db":true,"redis":true}
# gRPC reflection is enabled; health protocol is registered
grpcurl -plaintext localhost:50051 grpc.health.v1.Health/CheckHow do I back up the database?
# Dump
docker compose -f deploy/compose.oss.yml exec postgres \
pg_dump -U cmdop_admin cmdop > backup.sql
# Restore
cat backup.sql | docker compose -f deploy/compose.oss.yml exec -T postgres \
psql -U cmdop_admin cmdopHow do I upgrade CMDOP?
# Pull / rebuild images
docker compose -f deploy/compose.oss.yml pull
docker compose -f deploy/compose.oss.yml build
# Apply migrations (the migrate sidecar runs on every up)
docker compose -f deploy/compose.oss.yml up -d migrate
docker compose -f deploy/compose.oss.yml up -d
# Verify RLS coverage
just audit-rlsHow do I troubleshoot Docker issues?
What if a container won’t start?
# Status + logs
docker compose -f deploy/compose.oss.yml ps
docker compose -f deploy/compose.oss.yml logs api
# Validate the merged config
docker compose -f deploy/compose.oss.yml configHow do I debug database connection issues?
# Postgres readiness from inside the network
docker compose -f deploy/compose.oss.yml exec postgres \
pg_isready -U cmdop_admin -d cmdopWhat should I read next?
- Self-Hosted — the open-source stack, config, and TLS in depth
- Kubernetes — cluster deployment for scale