Self-hosting
Configuration reference
Every environment variable of the API and the web app.
AgentDock is configured with environment variables: the API reads its own set, and the web app takes two, partly at build time. This page lists every one of them with its default. The runner has its own config file, covered in the runner reference.
In a source checkout the files are apps/api/.env, apps/web/.env and docker/.env, each with a commented .env.example next to it. In the Docker Compose stack, the API reads env/api.env plus DATABASE_URL and REDIS_URL from the compose file.
API: core
| Variable | Default | Meaning |
|---|---|---|
PORT | 8180 | Port the API listens on. REST and the /live, /runner and /terminal WebSockets share it. |
APP_ENV | development | production marks the session and CSRF cookies Secure, so serve over HTTPS. test keeps every background worker off (used by the test suites). |
API_URL | http://localhost:8180 | The API origin runners pair with. The UI uses it to build the pairing command. Behind the reference proxy: https://<host>/api. |
WEB_URL | http://localhost:3517 | The web app's origin. It is the only CORS origin and the only Origin the /live and /terminal WebSockets accept. |
APP_URL | unset | Public URL of the web app, used for links in Telegram messages and as the GitHub App manifest redirect. The GitHub App falls back to WEB_URL; Telegram messages have no link without it. |
TRUST_PROXY | loopback | Express trust proxy: whose X-Forwarded-For to believe for the client IP (per-IP login throttle). Accepts true, false, a hop count, or addresses and names such as uniquelocal. See TRUST_PROXY. |
DATABASE_URL | none | PostgreSQL connection string. Required. Example: postgresql://postgres:<password>@postgres:5432/agentdock?schema=public. |
REDIS_URL | none | Redis for BullMQ (cron schedules), redis:// or rediss://. Required unless SCHEDULER_ENABLED=false; the API refuses to start without it. Example: redis://:<password>@redis:6379/0. |
QUEUE_PREFIX | agentdock | Key prefix of every BullMQ queue. Change it only to share one Redis between instances. |
APP_ENCRYPTION_KEY | unset | 32 random bytes, base64 (openssl rand -base64 32). Seals stored secrets with AES-256-GCM. Required for Telegram, inbound triggers, outbound webhooks and the GitHub App. Without a valid key the API still starts, but those features refuse to store secrets; a malformed key is logged once at start-up. |
APP_ENCRYPTION_KEY and a database dump are useless alone and the secret together. Store the key outside your database backups, and keep a copy: losing it means re-entering every stored secret.
API: features and workers
| Variable | Default | Meaning |
|---|---|---|
SCHEDULER_ENABLED | true | false runs no schedules worker on this instance, and then REDIS_URL is not needed. Useful for maintenance. |
WEBHOOKS_WORKER_ENABLED | true | false keeps the outbound webhook dispatcher and delivery worker off. Inbound triggers still answer. |
PUBLIC_URL | unset | Public HTTPS base GitHub can reach, for POST <PUBLIC_URL>/hooks/github. Set it before creating the GitHub App. Unset: the App's webhook is created inactive and runners keep polling every 60 seconds. Behind the reference proxy: https://<host>/api. |
GITHUB_API_BASE | https://api.github.com | GitHub REST API base. Tests point it at a mock. |
TELEGRAM_API_BASE | https://api.telegram.org | Telegram Bot API base. Tests point it at a mock. |
TELEGRAM_POLL_TIMEOUT_S | 30 | Long-poll timeout for getUpdates, in seconds, from 1 to 50. |
APP_TIMEZONE | UTC | Default IANA time zone a new budget's day, week or month is cut in, for example Europe/Berlin. An unknown zone falls back to UTC. |
TERMINAL_IDLE_TIMEOUT_SEC | 900 | A terminal attach with no input (read-write) or no traffic (read-only) for this long closes with idle. |
TERMINAL_MAX_DURATION_SEC | 14400 | Any terminal attach closes with max_duration after this long (4 hours). |
AUDIT_VERIFY_CRON | 0 3 * * * | Cron schedule (server time) of the nightly audit hash-chain verification. |
USAGE_RECOMPUTE_BATCH | 1000 | Requests re-priced per transaction when an admin re-prices a date range. |
ACTIVITY_POLL_MS | 2000 | Poll interval of the activity feed and run history projector, in milliseconds. 0 turns the loop off. |
ACTIVITY_GAP_GRACE_MS | 10000 | How long a gap in event ids is waited on before it is treated as a rolled-back id, in milliseconds. |
ACTIVITY_RETENTION_DAYS | 180 | Days an activity feed item is kept. A daily job deletes older ones. |
API: first admin
| Variable | Default | Meaning |
|---|---|---|
ADMIN_EMAIL | unset | With ADMIN_PASSWORD, makes admin:create run without prompts. |
ADMIN_PASSWORD | unset | The admin's password. Remove both after use. |
ADMIN_NAME | unset | Optional display name. |
API: development and tests
These matter only when you work on AgentDock itself.
| Variable | Default | Meaning |
|---|---|---|
TEST_DATABASE_URL | none | Database the end-to-end tests wipe and migrate on every run. Its name must end in _test. |
TEST_REDIS_URL | none | Redis the end-to-end tests use, for example database 1. |
BUN_BIN | bun on PATH | Bun binary the runner end-to-end test starts the real runner with. |
Web app
| Variable | Default | When | Meaning |
|---|---|---|---|
API_URL | http://localhost:8180 | build and run | Target of the same-origin /api rewrite (fixed at build time) and the address server components call at runtime. In Compose: http://api:8180, as both a build argument and a container variable. |
NEXT_PUBLIC_LIVE_URL | ws://localhost:8180/live | build | WebSocket URL of the live-updates endpoint, baked into the browser bundle. In production: wss://<host>/api/live. The web image refuses to build without it. |
The web image also sets PORT=3517, HOSTNAME=0.0.0.0 (the standalone server binds to loopback otherwise), NODE_ENV=production and NEXT_TELEMETRY_DISABLED=1. A plain next dev from source does not disable Next.js telemetry; set NEXT_TELEMETRY_DISABLED=1 yourself if you care.
Changing NEXT_PUBLIC_LIVE_URL or the build-time API_URL means rebuilding the web image. Restarting the container is not enough.
Local database (docker/.env)
Used only by the development compose file in docker/, which bun run db:setup starts.
| Variable | Example | Meaning |
|---|---|---|
POSTGRES_PASSWORD | change-me | Password of the postgres user. Must match DATABASE_URL in apps/api/.env. |
REDIS_PASSWORD | change-me | Redis password. Must match REDIS_URL (and TEST_REDIS_URL) in apps/api/.env. |
The local stack publishes PostgreSQL on port 5421 and Redis on port 6421.
Ports
| Port | What |
|---|---|
8180 | API (PORT) |
3517 | Web app |
5421 | PostgreSQL, local development compose only |
6421 | Redis, local development compose only |
127.0.0.1:4318 | The runner's OTLP receiver on each agent machine (loopback only) |
A production example
PORT=8180
APP_ENV=production
API_URL=https://agentdock.example.com/api
WEB_URL=https://agentdock.example.com
APP_URL=https://agentdock.example.com
PUBLIC_URL=https://agentdock.example.com/api
TRUST_PROXY=uniquelocal
APP_TIMEZONE=Europe/Berlin
APP_ENCRYPTION_KEY=<output of openssl rand -base64 32>