Skip to main content

Headless install: Docker, CI, postinstall

As of v0.37, modusbrain init --pglite in a non-TTY context (Docker RUN, CI step, postinstall hook) exits 1 when no embedding-provider API key is present in the environment. This is a deliberate fail-loud — the alternative was the v0.36 silent-broken-state class where init succeeded with a default that didn’t match any real key. Two patterns work for headless installs. Pick whichever fits your image lifecycle.

Pattern 1: Provider key available at image build time

If your CI / Docker pipeline can inject the API key as a build-time env var, set it before modusbrain init:
Init writes ~/.modusbrain/config.json with the resolved embedding_model + embedding_dimensions. Subsequent runs (in the same image / runner) read from that config and don’t re-resolve.

Pattern 2: Provider key only at runtime (deferred-setup)

If the API key is a runtime secret (Kubernetes secret, runtime env injection, end-user-supplied), use --no-embedding at build time and configure the provider when the container actually runs:
The modusbrain init --no-embedding opt-in writes embedding_disabled: true to config. Every embed callsite (modusbrain import, modusbrain embed, the runEmbedCore library entry point) checks this and refuses cleanly with a modusbrain config set embedding_model <id> hint rather than proceeding with a silent default. The runtime modusbrain init --force re-runs the init flow against the now-populated env, which:
  • Removes embedding_disabled from config.
  • Resolves the provider via env detection.
  • Re-templates the PGLite schema if dim differs from the build-time default.

What WON’T work

If you upgrade from a pre-v0.37 image that used this pattern, modusbrain doctor will surface the mismatch on first run after upgrade and print a paste-ready repair command (modusbrain init --force --embedding-model … for empty brains, modusbrain retrieval-upgrade --reindex for non-empty).

Verifying a headless install

After init, run modusbrain doctor --json to verify state:
The embedding_provider check returns status: 'ok' when:
  • Config has a persisted embedding_model.
  • Config has a persisted embedding_dimensions.
  • Live provider probe returns the configured dim.
  • DB column width matches.
If you used Pattern 2’s deferred-setup path, the check shows Skipped (no provider credentials) until the runtime config is populated. That’s expected.