Skip to content

This guide covers how to run the Z.E.N. server locally, with Docker, and in production. For VPS deployment with automatic HTTPS, see the Cloud Deployment Guide.

Quick links: Local Development | Docker with Remote DB | Docker with Local PostgreSQL | Production


Local Development

Local development with SQLite is the recommended default. No database setup is needed.

Prerequisites

  • Bun 1.0+
  • At least one provider configured (Claude Code or Codex)
  • A GitHub token for repository cloning (GH_TOKEN / GITHUB_TOKEN)

Setup

bash
# 1. Clone and install
git clone https://github.com/Cadence-Intelligence/zen
cd zen
bun install

# 2. Configure environment
cp .env.example .env
nano .env  # Add your provider tokens (Claude or Codex)

# 3. Start server + Web UI (SQLite auto-detected, no database setup needed)
bun run dev

# 4. Open Web UI
# http://localhost:5173

In development mode, two servers run simultaneously:

ServiceURLPurpose
Web UIhttp://localhost:5173React frontend (Vite dev server)
API Serverhttp://localhost:3090Backend API + SSE streaming

Optional: Use PostgreSQL Instead of SQLite

If you prefer PostgreSQL for local development:

bash
docker compose --profile with-db up -d postgres
# Set DATABASE_URL=postgresql://postgres:postgres@localhost:5432/zen in .env

Note: The database schema is created automatically on first container startup via the mounted migration file. No manual psql step is needed for fresh installs.

Production Build (Local)

bash
bun run build    # Build the frontend
bun run start    # Server serves both API and Web UI on port 3090

Verify It Works

bash
curl http://localhost:3090/health
# Expected: {"status":"ok"}

Docker with Remote PostgreSQL

Use this option when your database is hosted externally (Supabase, Neon, AWS RDS, etc.). This starts only the app container.

Prerequisites

  • Docker & Docker Compose
  • A remote PostgreSQL database with DATABASE_URL set in .env
  • provider tokens configured in .env

Setup

The app container runs without any profile when using an external database. There is no external-db profile; the base app service always starts.

bash
# 1. Get the deployment files
mkdir zen && cd zen
curl -fsSL https://raw.githubusercontent.com/Cadence-Intelligence/zen/main/deploy/docker-compose.yml -o docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/Cadence-Intelligence/zen/main/deploy/.env.example -o .env

# 2. Configure (edit .env with your tokens and DATABASE_URL)
nano .env

# 3. Start app container (no profile needed for external DB)
docker compose up -d

# 4. View logs
docker compose logs -f app

# 5. Verify
curl http://localhost:3090/api/health

INFO

Both Docker and local dev default to port 3090 (set via PORT in .env). The health endpoint is /api/health (Docker) and /health (local dev mode also works).

Database Migration (First Time)

For fresh installations, run the combined migration:

bash
psql $DATABASE_URL < migrations/000_combined.sql

Stop

bash
docker compose down

Docker with Local PostgreSQL

Use this option to run both the app and PostgreSQL in Docker containers. The database schema is created automatically on first startup.

Setup

bash
# 1. Configure .env
# Set: DATABASE_URL=postgresql://postgres:postgres@postgres:5432/zen

# 2. Start both containers
docker compose --profile with-db up -d --build

# 3. Wait for startup (watch logs)
docker compose logs -f app

# 4. Verify
curl http://localhost:3090/api/health

Note: Database tables are created automatically via the init script on first startup. No manual migration step is needed.

Updating an Existing Installation

When new migrations are added, apply them manually:

bash
# Connect to the running postgres container
docker compose exec postgres psql -U postgres -d zen

# For a fresh install, run the combined migration (idempotent, creates all 7 tables):
\i /migrations/000_combined.sql

# Or apply individual migrations you haven't applied yet.
# Check the migrations/ directory for the full list (currently 001 through 019).
\q

Stop

bash
docker compose --profile with-db down

Production Deployment

For deploying to a VPS (DigitalOcean, Linode, AWS EC2, etc.) with automatic HTTPS via Caddy, see the Cloud Deployment Guide.


Database Options Summary

OptionSetupBest For
SQLite (default)Zero config, just omit DATABASE_URLSingle-user, CLI usage, local development
Remote PostgreSQLSet DATABASE_URL to hosted DBCloud deployments, shared access
Local PostgreSQLDocker --profile with-dbSelf-hosted, Docker-based setups

SQLite stores data at ~/.zen/zen.db (or /.zen/zen.db in Docker). It is auto-initialized on first run.


Port Configuration

ContextDefault PortNotes
Local dev (bun run dev)3090Default server port
Docker3090Set via PORT in .env
Worktrees3190-4089Auto-allocated, hash-based on path
OverrideAnySet PORT=4000 bun dev

TIP

Both contexts default to port 3090. Override with the PORT environment variable in either.


Health Endpoints

ContextEndpointNotes
Docker / production/api/healthUsed by Docker healthcheck
Local dev/healthConvenience alias (also supports /api/health)
bash
# Docker
curl http://localhost:3090/api/health

# Local dev
curl http://localhost:3090/health

# Additional checks (both contexts)
curl http://localhost:3090/health/db           # Database connectivity
curl http://localhost:3090/health/concurrency  # Concurrency status

Troubleshooting

Container Won't Start

bash
# Check logs
docker compose logs app          # default (SQLite or external DB)
docker compose logs app          # --profile with-db

# Verify environment
docker compose config

# Rebuild without cache
docker compose build --no-cache
docker compose up -d

Port Conflicts

bash
# Check if port is in use
lsof -i :3090        # macOS/Linux
netstat -ano | findstr :3090  # Windows

AI that follows a recipe, not a conversation.