Skip to content

Environment variables

This page is generated from the .env.example files in the repository, so it lists every variable they document. set means the example sets the variable; optional means it is commented out, and the value shown is its default or an example.

These live in nautelle/.env, next to the compose file. install.sh writes the file and fills both passwords; a re-run keeps what is there. Source: deploy/compose/.env.example.

Variable Value in the example Description
NAUTELLE_URL http://localhost:8000 set The address people open, without a trailing slash. API_URL, WEB_URL, APP_URL and PUBLIC_URL derive from it; set one here to override it. PUBLIC_URL= (empty) keeps the GitHub App’s webhook off: install.sh writes it for an http:// URL, which GitHub cannot reach.
COMPOSE_PROFILES local set The exposure mode: local, tunnel or tls (exactly one).
NAUTELLE_BIND
NAUTELLE_PORT
127.0.0.1
8000
set local: where Caddy listens on the host. 0.0.0.0 opens it to the network.
TUNNEL_TOKEN empty set tunnel: the token of a remote-managed Cloudflare tunnel whose public hostname points at http://proxy:80. NAUTELLE_URL is that hostname’s https URL.
NAUTELLE_DOMAIN empty set tls: the domain Caddy gets a certificate for. Its DNS points at this host and ports 80 and 443 are open. NAUTELLE_URL is https://<domain>.
NAUTELLE_VERSION latest set Image tag: latest, or a release such as 0.15.0.
NAUTELLE_REGISTRY empty set Where the images come from: empty for ghcr.io/andreybegma, or docker.io/abegma for the same images on Docker Hub (the same digests and signatures).
APP_ENV production set production marks cookies Secure. install.sh writes development for an http:// NAUTELLE_URL, where a browser would drop them.
POSTGRES_PASSWORD
REDIS_PASSWORD
empty
empty
set Database and Redis passwords. Required: there is no default.

In a compose install these go in nautelle/api.env, one KEY=value per line, and docker compose up -d applies them. The install already sets the URLs, APP_ENV, TRUST_PROXY, DATABASE_URL and REDIS_URL; the variables only the test suite reads matter to contributors alone. Source: apps/api/.env.example.

Variable Value in the example Description
PORT 8180 set —
APP_ENV
API_URL
WEB_URL
development
http://localhost:8180
http://localhost:3517
set development and test send the session and CSRF cookies over plain http; any other value, or none, marks them Secure. Set production when deployed.
TRUST_PROXY loopback set Express trust proxy: whose X-Forwarded-For to believe for the client IP (per-IP login throttle, and the audited IP of /terminal and /runner sockets). loopback trusts a proxy on the same host — the web app’s /api rewrite. Behind another reverse proxy, set its address or hop count. true trusts every hop and a client can forge X-Forwarded-For: the API refuses to boot with it when APP_ENV=production. Count the proxies between the client and the API, then pick the value for your topology: dev, or single host (browser -> Next /api rewrite -> API) loopback nginx in front of Next (browser -> nginx -> Next -> API) 2 staging (Cloudflare -> cloudflared -> Caddy -> API) uniquelocal Never true in production.
DATABASE_URL "postgresql://postgres:@localhost:5421/agentdock?schema=public" set The database and Redis URLs ship without a password: bun run db:setup writes the passwords it generates into docker/.env into every URL that has none.
TEST_DATABASE_URL "postgresql://postgres:@localhost:5421/agentdock_test?schema=public" set Database the e2e tests (bun run test) wipe and migrate on every run. Its name must end in test; it is created if it does not exist. It is the template of one database per Jest worker on the same server (agentdock_1_test, …), recreated on every run. In a board card’s session (NAUTELLE_CARD, set by the runner) the suite uses the card’s own database instead — agentdock_card<id>_test — so card worktrees never wipe each other’s (#389) — with a bounded pool (connection_limit=5&pool_timeout=10, #487). docs/guides/e2e-test-database.md.
REDIS_URL "redis://:@localhost:6421/0" set Redis for BullMQ (cron schedules, ADR-0015). Required unless SCHEDULER_ENABLED=false; the API refuses to boot without it. The password is REDIS_PASSWORD of docker/.env.
TEST_REDIS_URL "redis://:@localhost:6421/1" set Redis the e2e tests use. Each run keeps to its own key prefix and removes it on teardown.
QUEUE_PREFIX agentdock optional Key prefix of every queue. Default: agentdock. The e2e suite sets a per-run one.
BUN_BIN — optional The runner e2e test starts the real runner daemon (apps/runner) with Bun. Defaults to bun on PATH.
NAUTELLE_DATA_DIR ./data optional Directory of the API’s own state (C-366). At start-up each of APP_ENCRYPTION_KEY, AUDIT_HMAC_KEY and LLM_SECRETS_KEY that is not set here is read from <dir>/secrets.json, or generated (32 random bytes, base64) and written there with mode 600 — a variable set here always wins and a key in the file is never replaced. A key is missing and the directory is not writable: the API refuses to start. Back the file up — data sealed with a key is lost without it — but not next to the database dump: either alone is useless. Default: /data in the image (NODE_ENV=production), else ./data.
AUDIT_HMAC_KEY — optional Key of the audit hash chain (C-174): every new audit record is hashed with HMAC-SHA256 under it, so someone who can write the database but lacks the key cannot rewrite a record and recompute the chain after it. At least 32 random bytes (openssl rand -base64 32); keep it out of the database and its backups. Records written before it was set keep their plain sha256 and still verify. Unset: the API takes it from, or generates it into, NAUTELLE_DATA_DIR’s secrets.json (below). Never change or drop it once set: verification then fails at the first keyed record.
AUDIT_VERIFY_CRON "0 3 * * *" optional Cron schedule of the audit chain verification (server time). Default: nightly at 03:00.
USAGE_RECOMPUTE_BATCH 1000 optional Requests re-priced per transaction by a price recompute (spec 13 D7). Default: 1000.
APP_ENCRYPTION_KEY — optional Key that seals secrets stored by Nautelle — the Telegram bot token (spec 22 D8), Slack webhook URLs (ADR-0109) and two-factor sign-in secrets (ADR-0081): 32 random bytes, base64 (openssl rand -base64 32). Without it the Telegram and Slack integrations refuse to store a token or URL and two-factor sign-in cannot be set up or used. Keep it out of backups of the database: either alone is useless, both together are the secret. Unset: taken from, or generated into, NAUTELLE_DATA_DIR’s secrets.json.
APP_URL http://localhost:3517 optional Public URL of the web app, used in Telegram message links (spec 22 D10), in email links (C-404), in Slack message links (ADR-0109), in emailed password reset links (C-402: without it Forgot password is off) and as the Telegram Mini App’s address when it is https (#580).
TELEGRAM_POLL_TIMEOUT_S 30 optional Telegram long-poll timeout, seconds (spec 22 D7). Default: 30.
TELEGRAM_API_BASE https://api.telegram.org optional Telegram Bot API base URL. Default: https://api.telegram.org — tests point it at a mock.
TELEGRAM_INIT_DATA_MAX_AGE_S 3600 optional How old, in seconds, a Telegram Mini App’s initData may be when it signs in (#580): 60 to 86400. Default: 3600. The Mini App needs an https APP_URL; with any other the bot sends plain links instead of Mini App buttons.
SMTP_URL smtps://user:password@smtp.example.com:465 optional Email notifications (C-404, ADR-0108). Unset: no email is sent, the Email column stays hidden and Forgot password tells people to ask an admin (C-402). smtp:// upgrades with STARTTLS when the server offers it; smtps:// is TLS from the start (usually port 465). Percent-encode an @ or : in the user or password. The credentials are never shown or logged.
SMTP_FROM Nautelle <nautelle@example.com> optional Sender of every email. Default: “Nautelle <user>” when the SMTP user is an address, else “Nautelle <nautelle@localhost>”.
ACTIVITY_POLL_MS 2000 optional Activity feed and run history projector poll interval, in ms (spec 21 D2). 0 turns the poll loop off. Default: 2000. It never runs under APP_ENV=test.
ACTIVITY_GAP_GRACE_MS 10000 optional How long a hole in events.id is waited on before it is treated as a rolled-back id, in ms (spec 21 notes). Default: 10000.
ACTIVITY_RETENTION_DAYS 180 optional Days an activity item is kept; a daily job deletes older ones (spec 21 D6). Default: 180.
SCHEDULER_ENABLED true optional Cron schedules (spec 25, 123): false runs no schedules worker on this instance and needs no REDIS_URL (maintenance). Default: true. It never runs under APP_ENV=test.
TERMINAL_IDLE_TIMEOUT_SEC
TERMINAL_MAX_DURATION_SEC
900
14400
optional Interactive terminal attach (spec 29 D7), in seconds. An attach with no input (read-write) or no traffic at all (read-only) for the idle timeout closes with idle; any attach closes with max_duration at the maximum. Defaults: 900, 14400.
APP_TIMEZONE Europe/Berlin optional Default time zone (IANA) a new budget’s day, week or month is cut in (spec 28 D1). Default: UTC; an unknown zone falls back to UTC.
WEBHOOKS_WORKER_ENABLED true optional Webhooks (spec 26). false keeps the outbound dispatcher and delivery worker off on this instance (inbound triggers still answer). Default: true. They never run under APP_ENV=test. Trigger and webhook secrets need APP_ENCRYPTION_KEY.
PUBLIC_URL https://dock.example.com optional GitHub App (spec 27). Public HTTPS base GitHub can reach for POST /hooks/github (docs/architecture/github-app.md). Unset: the App’s hook is created inactive and every project keeps 60 s polling. The App’s secrets need APP_ENCRYPTION_KEY; the manifest redirect uses APP_URL (else WEB_URL).
GITHUB_API_BASE https://api.github.com optional GitHub REST API base. Default: https://api.github.com — tests point it at a mock.
ATTACHMENTS_DIR ./data/attachments optional Board card attachments (#309, ADR-0031). Where the API stores the bytes (<projectId>/<attachmentId>, uploads in tmp/); back it up with the database. Default: ./data/attachments, relative to the API’s working directory.
ATTACHMENTS_PROJECT_QUOTA_BYTES 1073741824 optional Bytes one project may store, pending uploads included. Default: 1073741824 (1 GiB).
ATTACHMENTS_USER_PENDING_BYTES 268435456 optional Bytes of uploads not yet claimed by a card (and not expired) one user may hold; above it an upload is refused with 413 pending_exceeded (#156). Default: 268435456 (256 MiB).
NAUTELLE_UPDATE_CHECK true optional Update check (C-374, ADR-0091). Once a day the API asks GitHub for the latest Nautelle release (api.github.com, no data about the install) and admins see a notice when a newer one is out. false turns it off: GitHub is never asked.
DEEPL_API_KEY empty set DeepL API key (#324, ADR-0033). Set: the free text people save is translated to English by DeepL inside the save (the text leaves this host for DeepL); a free key ends in :fx. Empty: the runner’s haiku translates it later (ADR-0027). Server-side only — never logged, returned or sent to a runner.
SPEECH_TO_TEXT_URL
SPEECH_TO_TEXT_API_KEY
SPEECH_TO_TEXT_MODEL
https://api.openai.com/v1
—
whisper-1
optional Voice input of the Telegram /new intake (#594). The base URL of a Whisper-compatible speech-to-text API — the bot posts voice messages to <base>/audio/transcriptions (OpenAI-style multipart) and uses the text. The audio leaves this host for that service and is never stored. Unset: the bot answers that voice input is off. The key is sent as a bearer token (optional for a local server); server-side only. Model default: whisper-1.
LLM_SECRETS_KEY — optional Master key of the LLM gateway (ADR-0059, #598): seals the AI provider keys set under Admin › AI providers with AES-256-GCM. 32 random bytes, base64 (openssl rand -base64 32). Unset: taken from, or generated into, NAUTELLE_DATA_DIR’s secrets.json. Malformed: the gateway does not start — no AI feature runs and no provider key can be stored — and the rest of the API runs on. Separate from APP_ENCRYPTION_KEY; keep it out of database backups: either alone is useless, both together are the keys.
ADMIN_EMAIL
ADMIN_PASSWORD
ADMIN_NAME
—
—
—
optional The first admin without the browser (C-367): set both, and the API creates that admin on boot while no user exists — else it logs a one-time <WEB_URL>/setup?token=… link. bun run admin:create reads them too, to skip its prompts. Remove them once the admin exists.
DEMO_LOGIN_EMAIL
DEMO_LOGIN_PASSWORD
—
—
optional Demo recordings (C-383, ADR-0090): scripts/demo-dev.sh runs the API on a database of its own (agentdock_demo_<card>) and creates its admin from these; bun run demo:db seed gives that admin the demo project. The runner sets them from the project’s Demo env — never put a real account here.
DEMO_API_PORT — optional The demo API’s port; default the web app’s PORT + 1000.

The published web image is built for any host and needs none of these. They matter when you build the web app yourself or run it from source. Source: apps/web/.env.example.

Variable Value in the example Description
API_URL http://localhost:8180 set Target of the same-origin /api rewrite (next.config.ts).
NEXT_PUBLIC_LIVE_URL ws://localhost:8180/live set WebSocket URL of the API’s /live endpoint (live updates). Development default shown; in production use wss://<origin>/api/live through the reverse proxy.