# LIFEBORN: PostgreSQL + one-shot migration + API (+ optional separate worker). # # cp .env.example .env # then edit: at minimum POSTGRES_PASSWORD and LIFEBORN_BOOTSTRAP_ADMIN_KEY # docker compose up -d --build # docker compose run --rm api demo --base-url http://api:8080 # optional: Mira, Luna and Edison # # PostgreSQL is the only stateful dependency: canonical history, memories, durable jobs, the SSE outbox and the # LangGraph checkpoints all live in it. There is deliberately no Redis (see docs/ARCHITECTURE.md). name: lifeborn x-lifeborn-env: &lifeborn-env # Secrets come from the environment / .env (git-ignored) only. Quoted because the messages contain YAML-significant text. LIFEBORN_DATABASE_URL: "postgresql+psycopg://lifeborn:${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}@db:5432/lifeborn" LIFEBORN_ENV: prod LIFEBORN_LOG_JSON: "true" LIFEBORN_BOOTSTRAP_ADMIN_KEY: "${LIFEBORN_BOOTSTRAP_ADMIN_KEY:?set LIFEBORN_BOOTSTRAP_ADMIN_KEY in .env - see .env.example for how to generate one}" LIFEBORN_LLM_PROVIDER: ${LIFEBORN_LLM_PROVIDER:-auto} NEBIUS_API_KEY: ${NEBIUS_API_KEY:-} TAVILY_API_KEY: ${TAVILY_API_KEY:-} # Web research tunables. Compose passes only variables it names, so these defaults must match config.py. LIFEBORN_RESEARCH_ENABLED: ${LIFEBORN_RESEARCH_ENABLED:-true} LIFEBORN_RESEARCH_MAX_PER_WAKE: ${LIFEBORN_RESEARCH_MAX_PER_WAKE:-2} LIFEBORN_RESEARCH_MAX_ROUNDS: ${LIFEBORN_RESEARCH_MAX_ROUNDS:-2} LIFEBORN_RESEARCH_CHARACTER_DAILY: ${LIFEBORN_RESEARCH_CHARACTER_DAILY:-5} LIFEBORN_RESEARCH_WORLD_DAILY: ${LIFEBORN_RESEARCH_WORLD_DAILY:-50} LIFEBORN_RESEARCH_GLOBAL_DAILY: ${LIFEBORN_RESEARCH_GLOBAL_DAILY:-200} LIFEBORN_RESEARCH_REQUESTS_PER_MINUTE: ${LIFEBORN_RESEARCH_REQUESTS_PER_MINUTE:-60} LIFEBORN_RESEARCH_MAX_CONCURRENCY: ${LIFEBORN_RESEARCH_MAX_CONCURRENCY:-4} LIFEBORN_RESEARCH_TIMEOUT_S: ${LIFEBORN_RESEARCH_TIMEOUT_S:-20} LIFEBORN_RESEARCH_MAX_RETRIES: ${LIFEBORN_RESEARCH_MAX_RETRIES:-2} LIFEBORN_LLM_MODEL_DEFAULT: ${LIFEBORN_LLM_MODEL_DEFAULT:-nvidia/Nemotron-3_5-Lightning} LIFEBORN_LLM_MODEL_ESCALATION: ${LIFEBORN_LLM_MODEL_ESCALATION:-nvidia/Nemotron-3-Ultra-550b-a55b} LIFEBORN_LLM_MODEL_FALLBACKS: ${LIFEBORN_LLM_MODEL_FALLBACKS:-[]} LIFEBORN_VOX_BASE_URL: ${LIFEBORN_VOX_BASE_URL:-https://vox.gold} LIFEBORN_VOX_KEY_DIR: /var/lib/lifeborn/vox-keys LIFEBORN_CORS_ORIGINS: ${LIFEBORN_CORS_ORIGINS:-[]} LIFEBORN_OTEL_EXPORTER_OTLP_ENDPOINT: ${LIFEBORN_OTEL_EXPORTER_OTLP_ENDPOINT:-} services: db: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_USER: lifeborn POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env} POSTGRES_DB: lifeborn volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U lifeborn -d lifeborn"] interval: 5s timeout: 4s retries: 20 # Not published to the host by default. Uncomment to reach it from your machine: # ports: ["127.0.0.1:5432:5432"] migrate: build: . image: lifeborn:local command: ["db", "upgrade"] environment: *lifeborn-env depends_on: db: condition: service_healthy restart: "no" api: build: . image: lifeborn:local restart: unless-stopped command: ["serve", "--host", "0.0.0.0", "--port", "8080"] environment: <<: *lifeborn-env # One process serves HTTP and runs the durable-job worker. To scale workers independently set this to # "false" and enable the `worker` service below (profile "workers"). LIFEBORN_EMBEDDED_WORKER: ${LIFEBORN_EMBEDDED_WORKER:-true} FORWARDED_ALLOW_IPS: ${FORWARDED_ALLOW_IPS:-127.0.0.1} ports: - "${LIFEBORN_PORT:-8080}:8080" volumes: - voxkeys:/var/lib/lifeborn/vox-keys depends_on: db: condition: service_healthy migrate: condition: service_completed_successfully stop_grace_period: 40s # the worker drains in-flight jobs on SIGTERM (LIFEBORN_SHUTDOWN_GRACE_SECONDS) worker: build: . image: lifeborn:local profiles: ["workers"] restart: unless-stopped command: ["worker"] environment: *lifeborn-env volumes: - voxkeys:/var/lib/lifeborn/vox-keys depends_on: db: condition: service_healthy migrate: condition: service_completed_successfully stop_grace_period: 40s volumes: pgdata: voxkeys: