Skip to content

Docker

Terminal window
docker run -d --name z4j-brain \
-p 8080:7700 \
-e Z4J_DATABASE_URL=postgresql+asyncpg://z4j:pw@db:5432/z4j \
-e Z4J_SECRET=... \
-e Z4J_SESSION_SECRET=... \
-e Z4J_AUDIT_SECRET=... \
-e Z4J_PUBLIC_URL=https://z4j.example.com \
z4jdev/z4j:latest
services:
db:
image: postgres:18-alpine
environment:
POSTGRES_DB: z4j
POSTGRES_USER: z4j
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- z4j-db:/var/lib/postgresql/data
restart: unless-stopped
brain:
image: z4jdev/z4j:latest
depends_on: [db]
environment:
Z4J_DATABASE_URL: postgresql+asyncpg://z4j:${POSTGRES_PASSWORD}@db/z4j
Z4J_SECRET: ${Z4J_SECRET}
Z4J_SESSION_SECRET: ${Z4J_SESSION_SECRET}
Z4J_AUDIT_SECRET: ${Z4J_AUDIT_SECRET}
Z4J_PUBLIC_URL: https://z4j.example.com
ports: ["8080:7700"]
restart: unless-stopped
volumes:
z4j-db:
  • Base: python:3.14-slim-trixie (Debian 13).
  • Size: < 250 MiB.
  • Entry point: z4j-brain serve (FastAPI via Uvicorn).
  • Signal handling: SIGTERM triggers graceful shutdown.

Migrations run automatically on container start (idempotent; safe on every boot). To run them manually:

Terminal window
docker exec -it z4j-brain z4j-brain migrate
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:7700/api/v1/health"]
interval: 30s
timeout: 5s
retries: 3

JSON to stdout. Aggregate with your log shipper.

Watch stderr on first boot to capture the setup URL. With Compose:

Terminal window
docker compose logs -f brain 2>&1 | grep "setup URL"