Deploy the Cronium application stack with Docker Compose. This guide covers the required services, recommended configuration, and the environment variables needed to run Cronium in your own infrastructure.
A production Cronium deployment consists of the Next.js control plane (cronium-app), the secure job orchestrator (cronium-orchestrator), the runtime API used by containerised scripts, and supporting services (PostgreSQL for persistence and Valkey/Redis for caching). Docker Compose offers a simple way to run these services together.
Verify the following before launching the stack.
AUTH_SECRET,ENCRYPTION_KEY, and a sharedINTERNAL_API_KEY between the app and orchestratorYou only need a few long, random strings and a minimal .env file to get started.
curl -O https://raw.githubusercontent.com/addison-moore/cronium/main/docker-compose.example.ymlopenssl is not installed, use a password manager with a 64-character random string:# macOS / Linux / WSL
openssl rand -hex 32 # paste into AUTH_SECRET, ENCRYPTION_KEY, JWT_SECRET
openssl rand -base64 32 # paste into INTERNAL_API_KEY.env file with the secrets you generated and your public domain (use http://localhost:3000 if you are testing locally). Paste these values into the Compose file placeholders or add env_file: ['.env'] to reuse them automatically:AUTH_URL=https://cronium.example.com
PUBLIC_APP_URL=https://cronium.example.com
AUTH_SECRET=<paste value>
ENCRYPTION_KEY=<paste value>
INTERNAL_API_KEY=<paste value>
JWT_SECRET=<paste value>The Compose example below assumes the following images are available locally or in a registry you can pull from:
cronium-app – Next.js control plane UI & APIcronium-orchestrator – Go daemon that executes jobscronium-runtime – Runtime API for container executions (optional if you only use SSH targets)docker build -t cronium-app:latest -f apps/cronium-app/Dockerfile .
docker build -t cronium-orchestrator:latest apps/orchestrator
docker build -t cronium-runtime:latest apps/runtime/cronium-runtimeCopy the following Compose file into docker-compose.yml and adjust environment variables and volume mounts for your environment. The compose file deploys PostgreSQL, Valkey, the Cronium app, the orchestrator, and the runtime service. SMTP is automatically used whenever credentials are provided; missing credentials will disable outbound email and surface warnings in the UI.
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: cronium
POSTGRES_PASSWORD: super-secure-password
POSTGRES_DB: cronium
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U cronium"]
interval: 10s
timeout: 5s
retries: 5
valkey:
image: valkey/valkey:7-alpine
command: valkey-server --appendonly yes
volumes:
- valkey-data:/data
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
cronium-app:
image: ghcr.io/addison-moore/cronium-app:latest
env_file:
- .env
depends_on:
postgres:
condition: service_healthy
valkey:
condition: service_healthy
environment:
AUTO_SEED_ADMIN: "true"
ADMIN_USERNAME: admin
ADMIN_EMAIL: admin@example.com
ADMIN_PASSWORD: admin
SMTP_HOST: smtp.example.com
SMTP_PORT: 587
SMTP_USER: smtp_user
SMTP_PASSWORD: smtp_password
SMTP_FROM_EMAIL: admin@example.com
NODE_ENV: production
AUTH_URL: https://cronium.example.com
PUBLIC_APP_URL: https://cronium.example.com
NEXT_PUBLIC_APP_URL: https://cronium.example.com
AUTH_SECRET: replace-with-random-string
ENCRYPTION_KEY: replace-with-32-byte-key
INTERNAL_API_KEY: replace-with-shared-internal-key
JWT_SECRET: replace-with-runtime-jwt-secret
DATABASE_URL: postgres://cronium:super-secure-password@postgres:5432/cronium
ORCHESTRATOR_URL: http://cronium-orchestrator:8080
VALKEY_URL: valkey://valkey:6379
NEXT_PUBLIC_SOCKET_URL: http://cronium-app:5002
NEXT_PUBLIC_SOCKET_PORT: 5002
ports:
- "3000:3000"
- "5002:5002"
cronium-orchestrator:
image: ghcr.io/addison-moore/cronium-orchestrator:latest
env_file:
- .env
depends_on:
- cronium-app
- valkey
environment:
CRONIUM_API_ENDPOINT: http://cronium-app:3000
CRONIUM_API_TOKEN: replace-with-shared-internal-key
CRONIUM_ORCHESTRATOR_ID: orchestrator-1
CRONIUM_CONTAINER_RUNTIME_JWT_SECRET: replace-with-runtime-jwt-secret
CRONIUM_CONTAINER_RUNTIME_BACKEND_URL: http://cronium-app:3000
CRONIUM_CONTAINER_RUNTIME_VALKEY_URL: valkey://valkey:6379
LOG_LEVEL: info
ports:
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
user: "0:0"
cronium-runtime:
image: ghcr.io/addison-moore/cronium-runtime:latest
env_file:
- .env
depends_on:
- cronium-app
- valkey
environment:
RUNTIME_PORT: 8081
RUNTIME_JWT_SECRET: replace-with-runtime-jwt-secret
RUNTIME_BACKEND_URL: http://cronium-app:3000
RUNTIME_BACKEND_TOKEN: replace-with-shared-internal-key
RUNTIME_VALKEY_URL: valkey://valkey:6379
RUNTIME_LOG_LEVEL: info
ports:
- "8081:8081"
volumes:
postgres-data: {}
valkey-data: {}
Replace the placeholder secrets in .env (or inline them if you prefer). The example already loads .env for each service. Setting AUTO_SEED_ADMIN=true seeds an admin user and default settings on first boot; change theADMIN_* and SMTP values to your desired bootstrap credentials before deploying.
The Cronium app automatically runs database migrations on start. You can disable this behaviour by setting AUTO_MIGRATE=false if you prefer to manage the schema yourself.
Leave the /var/run/docker.sock mount in place if you plan to run container jobs—the orchestrator needs access to the host Docker daemon. Remove it only when you exclusively use SSH runners.
The tables below summarise the key variables per service. Values marked as required must be set for a production deployment.
| Variable | Required | Description |
|---|---|---|
PUBLIC_APP_URL | Yes | Public base URL of the Next.js application (used by links, auth callbacks, emails). |
NEXT_PUBLIC_APP_URL | Optional | Mirror of PUBLIC_APP_URL exposed to the browser; set when serving the UI behind a proxy. |
AUTH_URL | Yes | URL that NextAuth should consider as the canonical origin for authentication. |
AUTH_SECRET | Yes | Random 32-character string used by NextAuth to sign session cookies (e.g. openssl rand -hex 32). |
DATABASE_URL | Yes | PostgreSQL connection string in the format postgres://user:pass@host:5432/db; see the Compose example for defaults. |
ENCRYPTION_KEY | Yes | 32-byte key (Base64 or hex) used to encrypt stored secrets; generate with openssl rand -hex 32 or a password manager. |
INTERNAL_API_KEY | Yes | Shared token that internal services (orchestrator, runtime) must present when calling the app's internal APIs; generate with openssl rand -base64 32. |
JWT_SECRET | Yes | Token used for signing internal service-auth tokens and WebSocket payloads; reuse this for the runtime service (e.g. openssl rand -hex 32). |
AUTO_MIGRATE | Optional | Defaults to true. Leave enabled unless you plan to run migrations yourself; set to falseif you need to manage schema updates manually. |
ORCHESTRATOR_URL | Optional | Base URL for the orchestrator health endpoints. Defaults to http://cronium-orchestrator:8080 inside Docker. |
VALKEY_URL | Optional | Connection string for Valkey (use thevalkey:// scheme). Falls back to in-memory caching if omitted. |
SMTP_* | Optional | Configure SMTP credentials when enabling email notifications. |
| Variable | Required | Description |
|---|---|---|
CRONIUM_API_ENDPOINT | Yes | Base URL of the Cronium app (internal service-to-service address). |
CRONIUM_API_TOKEN | Yes | Must match INTERNAL_API_KEY so the orchestrator can authenticate with the app. |
CRONIUM_ORCHESTRATOR_ID | Yes | Unique identifier for this orchestrator instance (used for logging and job claims). |
CRONIUM_CONTAINER_RUNTIME_JWT_SECRET | Yes* | Shared secret between the orchestrator and the runtime API for container job authentication. Required if you enable the container executor. |
CRONIUM_CONTAINER_RUNTIME_BACKEND_URL | Optional | Internal URL the runtime API should use to call back into the Cronium app (defaults to http://cronium-app:3000). |
CRONIUM_CONTAINER_RUNTIME_VALKEY_URL | Optional | Valkey connection string for coordinating container job state (supports the valkey:// scheme). |
LOG_LEVEL | Optional | Overrides orchestrator logging verbosity. Defaults toinfo. |
*Required when using container-based execution. For SSH-only environments you may omit the runtime service and related secrets.
| Variable | Required | Description |
|---|---|---|
RUNTIME_BACKEND_URL | Yes | Internal URL the runtime service should use to reach the Cronium app (typically http://cronium-app:3000). |
RUNTIME_BACKEND_TOKEN | Yes | Must match INTERNAL_API_KEY to authenticate runtime calls. |
RUNTIME_VALKEY_URL | Yes | Valkey connection string used for caching workflow state. |
RUNTIME_JWT_SECRET | Yes | Same value as CRONIUM_CONTAINER_RUNTIME_JWT_SECRET; used to validate execution tokens. |
RUNTIME_PORT | Optional | Port for the runtime API (defaults to 8081). |
RUNTIME_LOG_LEVEL | Optional | Sets runtime logging verbosity. Defaults to info. |
| Service | Variable | Description |
|---|---|---|
| PostgreSQL | POSTGRES_USER,POSTGRES_PASSWORD,POSTGRES_DB | Standard PostgreSQL variables. Ensure they align with theDATABASE_URL provided to the app. |
| Valkey | - | No special variables required. Persistent volumes are recommended for durability. |
.env and the sample Compose file ready to use.cronium-app, cronium-orchestrator, and cronium-runtime images from GHCR, or build them locally.docker compose up -d and wait for all containers to report healthy states.pnpm install, then execute pnpm --filter @cronium/app db:push. The published app image does not bundle pnpm, so migrations should run outside the container or via your CI pipeline.apps/orchestrator/configs/cronium-orchestrator.yaml from the repo) if you need advanced tuning for metrics, SSH executors, or polling cadence.Confirm each service is reachable before inviting teammates.
https://cronium.example.com (or your configured domain) – you should see the login screen.curl http://localhost:3000/api/health from the host running Docker.curl http://localhost:8080/health; expect a JSON payload with status: "healthy".curl http://localhost:8081/health for a simple heartbeat.docker compose logs cronium-app while triggering a job to verify live log streaming on port 5002.https://cronium.example.com and create the first admin account.Keep your deployment healthy and secure.
Reach out to the Cronium community or open a discussion in the repository for assistance with your self-hosted deployment.