Skip to content

Compose reference

The release bundle ships deploy/compose.yml and deploy/.env.example. As shipped, compose.yml is the evaluation stack: it includes three evaluation fixtures and publishes the database and api ports next to the UI. A production install layers an override file on top that publishes app-ui only (see Overrides).

Service Image Depends on Health check Restart
db postgres:16-alpine none pg_isready unless-stopped
migrator bookend/migrator db healthy none; exits 0 when every script is applied no
api bookend/api migrator completed successfully; inference healthy (and, as shipped, the three evaluation fixtures healthy) GET /readyz (database and schema) unless-stopped
inference bookend/inference none GET /healthz unless-stopped
app-ui bookend/app-ui api healthy GET /nginx-health unless-stopped
jxchange-mock, metering-mock bookend/jxchange-mock none GET /healthz unless-stopped
mailhog mailhog/mailhog:v1.0.1 none MailHog API unless-stopped

jxchange-mock (a stand-in core), metering-mock (an in-stack heartbeat receiver) and mailhog (an SMTP server with a web mailbox) exist for evaluation. A production install keeps them from starting with the override below. Image tags come from BOOKEND_VERSION.

Variable Meaning
DB__PROVIDER postgres (default) or sqlserver
POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD bundled database name and owner principal (the migrator runs as it); the password is required
DB_APP_USER, DB_APP_PASSWORD least-privilege application principal, created by the migrator with exactly this password; the password is required
DB__CONNECTION, DB__MIGRATOR_CONNECTION connection strings for an external database. With the bundled database compose.yml builds both itself (host db); an external database needs the override below
BOOKEND_MASTER_KEY base64 of 32 random bytes; encrypts secrets in settings. Keep it in your vault; never use the example value
BOOKEND_VERSION image tag: the release version you loaded (defaults to dev)
BOOKEND_SKIP_SCRIPTS 908_seed_demo_users.sql on a production install, so the two evaluation user accounts are never created
BOOKEND_UI_PORT (8081), BOOKEND_API_PORT (8080), BOOKEND_DB_PORT (5432), BOOKEND_MAILHOG_PORT (8025) host ports; only the UI port is published in production
BOOKEND_LOGIN_ATTEMPTS_PER_MINUTE per-address login limit as compose.yml sets it (60 for automated test runs); the production override sets 10
Proxy__TrustedNetworks comma-separated networks whose X-Forwarded-For / X-Forwarded-Proto the api honors (default: the private ranges and loopback)

A value a compose file sets under a service’s environment: wins over the same name in .env, which is why connection strings and the login limit are changed in override files rather than in .env.

Everything else (SMTP, core adapter, tolerances, reason codes, retention, metering) lives in the settings table and is configured through the wizard or Settings.

dbdata (bundled Postgres), documents (content-addressed files, rendered pages), exports (boarding files, wire requests), watch (watch-folder intake), modelcache (inference scratch). Compose prefixes them with the project name: bookend_documents, and so on.

The Installation Guide gives both files in full.

  • compose.production.yml: withdraws the db and api host ports (ports: !reset []), moves jxchange-mock, metering-mock and mailhog into an evaluation profile and drops them from the api’s start conditions (depends_on: !override), sets RateLimiting__LoginAttemptsPerMinute to 10, and mounts the license public key for Licensing__PublicKeyPath. Requires Compose v2.24.4 or later.
  • compose.external-db.yml: moves db into a profile so it does not start, removes the migrator’s dependency on it, and passes DB__CONNECTION / DB__MIGRATOR_CONNECTION from .env to the migrator and api. For SQL Server also set DB__PROVIDER=sqlserver.

Air-gapped operation needs no override: it is a setting (metering.air_gapped), see Air-gapped operations.

Terminal window
DC="docker compose -f deploy/compose.yml -f deploy/compose.production.yml"
$DC up -d --wait # start everything, wait for healthy
$DC ps -a # state per service (the migrator shows Exited (0))
$DC logs --since 30m api # structured logs
$DC cp validation-pack/golden-packages/BK-DEMO-001 api:/data/watch/ # watch-folder intake from the bundle root,
$DC exec -u root api chown -R app:app /data/watch # then hand the copy to the api's user
$DC down # stop; volumes are kept (never add -v on a live install)