Skip to main content

Environment variables

Important: Poindexter runs virtually ALL configuration out of the app_settings Postgres table, not environment variables. Env vars and ~/.poindexter/bootstrap.toml exist only to bootstrap the stack (enough config to connect to the database and start Docker). Once the worker connects, everything — model thresholds, prompt templates, QA weights, auto-publish rules, algorithm windows, SEO pinging endpoints — is changed with SQL against app_settings (hundreds of keys — see docs/reference/app-settings.md for the full, nightly-regenerated catalog), no redeploy needed. Preferred path: run poindexter setup once on first install. It writes ~/.poindexter/bootstrap.toml with 5 bootstrap values (database URL + 4 generated passwords). Then bash scripts/start-stack.sh reads bootstrap.toml and exports them for Docker. No .env file needed. See reference/app-settings.md for the full list of DB-backed settings.
This document enumerates the environment variables consumed by the stock docker-compose.local.yml deployment. If you’re running a custom configuration, start from this list.

Required

These variables are generated by poindexter setup and stored in ~/.poindexter/bootstrap.toml. The scripts/start-stack.sh wrapper exports them automatically for Docker. Missing any causes the stack to fail fast with a clear error — no silent fallbacks.
VariablePurposeExample
LOCAL_POSTGRES_PASSWORDPassword for the local Postgres superuserrandom 32-char hex
GRAFANA_PASSWORDGrafana admin passwordrandom 32-char hex
PGADMIN_PASSWORDpgAdmin admin passwordrandom 32-char hex
All three are generated automatically by poindexter setup on first run and written to ~/.poindexter/bootstrap.toml (safe permissions, outside the repo). No .env file is needed. Worker authentication uses OAuth 2.1 client credentials (Phase 3 finalised in Glad-Labs/poindexter#249). poindexter setup provisions an initial CLI client and stores the credentials encrypted in app_settings.cli_oauth_client_id / cli_oauth_client_secret. There is no API_TOKEN env var.

Optional (with sensible defaults)

VariableDefaultPurpose
API_BASE_URLhttp://localhost:8002Base URL clients use to reach the worker
COMPANY_NAMEGlad LabsPublisher name used in generated content’s <author> tag
DEFAULT_OLLAMA_MODELautoOllama model the router picks when no override
DEVELOPMENT_MODEtrueEnables dev bypasses (token, CORS). Never set in prod.
ENVIRONMENTproductionLog tag — also tagged on GlitchTip events. Sentry SDK is back via self-hosted GlitchTip; main.py initialises it when app_settings.sentry_dsn is set (provisioned 2026-05-09).
GRAFANA_USERadminGrafana admin username
LOCAL_POSTGRES_USERpoindexterLocal Postgres user
LOCAL_POSTGRES_DBpoindexter_brainLocal Postgres database name
OLLAMA_BASE_URLhttp://host.docker.internal:11434Where OllamaClient reaches Ollama (pipeline generation)
OLLAMA_URLhttp://host.docker.internal:11434Where MemoryClient / auto-embed.py reaches Ollama (embeddings). Must be set to the same value as OLLAMA_BASE_URL.
PORT8002Worker HTTP port
SITE_DOMAINgladlabs.ioThe operator’s public site domain
SITE_NAMEGlad LabsSite name in feeds + metadata
SITE_URLhttps://www.gladlabs.ioCanonical site URL

Optional (no default — feature off unless set)

VariableFeature gated on it
PEXELS_API_KEYFallback stock-photo search when image-gen is unavailable
TELEGRAM_BOT_TOKENBrain daemon alerts via Telegram
TELEGRAM_CHAT_IDDestination chat for Telegram alerts
OPENCLAW_GATEWAY_URLDiscord + Telegram bridge via OpenClaw
OPENCLAW_GATEWAY_TOKENBearer token for OpenClaw Tools Invoke API
POINDEXTER_MCP_HTTP_TOOL_ALLOWLISTComma-separated tool names exposed by the remote MCP HTTP transport. Unset = expose all 25+ tools. Set to the recommended voice/mobile read-only list when registering a Custom Connector to a phone (see below)
Local Ollama only in the core stack. Paid-API providers (OpenAI, Anthropic, Google) live in community plugins, never the core. If you install one of those plugins, it reads its own keys from app_settings at runtime via the settings API — not from env vars.

.env.local template

A fully-populated .env.local for a standard local install looks like this (the bootstrap script writes something equivalent):

MCP HTTP tool allowlist (POINDEXTER_MCP_HTTP_TOOL_ALLOWLIST)

The remote MCP HTTP server (mcp-server/http_server.py) exposes the same tool registry as the stdio transport — by default that’s all 25+ tools, including write-capable ones like publish_post, approve_post, reject_post, set_setting, and create_post. When you register the HTTP server as a Custom Connector for the mobile or voice Claude app, write tools become a real risk: a misheard query plus an LLM that “follows through” can publish or reject content from the road, exactly when you’re least able to course-correct quickly. Set POINDEXTER_MCP_HTTP_TOOL_ALLOWLIST to the comma-separated list below to expose only the 13 read-only tools — covers “what’s the system state?”, “what did I decide about X?”, and “what’s in the queue?” without giving voice mode the keys to the publishing button:
The same list lives as the DEFAULT_VOICE_MOBILE_ALLOWLIST constant in mcp-server/http_server.py for in-code reference. Behaviour notes:
  • Unset ⇒ all registered tools exposed (pre-#239 default).
  • Empty string ("") ⇒ explicit “expose nothing” — useful if you want the route mounted but locked down without unmounting it.
  • Whitespace-toleranta, b ,c parses as {a, b, c}.
  • Unknown names silently ignored — copying the recommended list verbatim never crashes startup, even after a tool is renamed upstream.
  • Stdio transport unaffected — running python mcp-server/server.py always exposes the full registry, since the stdio path doesn’t go through http_server.build_app.
The filter runs before FastMCP builds its streamable_http_app(), so both tools/list and tools/call reflect the trimmed registry — unlisted tools surface as Unknown tool: <name> to the client.

How the worker actually reads these

The worker resolves configuration in this order:
  1. Environment variable — for bootstrap-only values (DATABASE_URL, passwords). Read once at startup. Worker API auth uses OAuth JWTs (no env var); see poindexter auth migrate-cli.
  2. app_settings table — for runtime-mutable values (model thresholds, prompt overrides, cost guards, feature flags). Consulted on every call via site_config.get(key, default).
  3. Hardcoded fallback — the _DEFAULTS dict in services/site_config.py. Used only when the DB value is absent AND no env var provides a value.
Never edit _DEFAULTS to change runtime behavior — that requires a rebuild. Use the settings API or SQL against app_settings instead.

Where to go next