Environment variables
Anvil reads its settings from environment variables, plus the file named by ANVIL_ENV_FILE (variables in the environment win). It refuses to start when a required variable is missing or invalid, and logs a warning for each optional feature that is off. Empty values count as unset, except GIT_SSH_HOST.
Core #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
ROLE |
no | all |
all runs the web server and the review worker in one process; web or worker split them. |
|
PORT |
no | 3000 |
Port the server listens on. | |
PUBLIC_URL |
production | http://localhost:3000 |
Anvil's public address, with https. Every link, webhook URL, OAuth redirect, clone URL and the GitHub App's URLs are built from it. | |
DATABASE_URL |
yes | secret | Postgres connection string (Postgres 16 or newer). Migrations run on boot. | |
SESSION_SECRET |
production | secret | Signs sessions and encrypts stored secrets (GitHub App key, hosted Gitea tokens). 32+ characters. Changing it signs everyone out and makes stored secrets unreadable: move it with the database. | |
DB_POOL_MAX |
no | 10 |
Most Postgres connections this process opens. | |
MAX_BODY_MB |
no | 25 |
Largest request body the API accepts, in MB. | |
MAX_UPLOAD_MB |
no | 256 |
Largest upload (git push over HTTPS, release assets), in MB. | |
ANVIL_ENV_FILE |
no | A KEY=value file read before the environment (variables set in the environment win). The self-host bundle points it at the file the first-run setup writes (Gitea tokens, OAuth client). | ||
NODE_ENV |
no | production in the image. Turns on the production checks (SESSION_SECRET, PUBLIC_URL) and hides DEV_LOGIN. |
Instance #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
INSTANCE_NAME |
no | Anvil |
Name used in the email sender and the GitHub App Anvil creates. | |
SERVER_NAME |
no | your server |
How pages and docs refer to the machine Anvil runs on ("runs on <SERVER_NAME>"). | |
SERVER_LOCATION |
no | (empty) | Where that machine is, for the privacy answers on the landing page and in the docs, e.g. Germany. Empty leaves it out. |
|
CONTACT_EMAIL |
no | Address for privacy, security and legal questions, shown in the docs. | ||
CANONICAL_REDIRECT_HOSTS |
no | (empty) | Old hostnames of this install (comma-separated). Pages on them redirect to PUBLIC_URL; webhooks and /mcp keep answering. Off when empty. |
Git server #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
GITEA_URL |
yes | Gitea as Anvil reaches it, for the API and git. Can be internal, e.g. http://gitea:3000. |
||
GITEA_PUBLIC_URL |
no | Gitea as browsers reach it ("Continue with Gitea", links). Defaults to GITEA_URL. | ||
GITEA_TOKEN |
yes | secret | Token of the bot user (scopes read:repository, write:repository, write:issue, read:user). Written by the first-run setup. | |
GITEA_WEBHOOK_SECRET |
yes | secret | Signs Gitea's system webhook to <PUBLIC_URL>/api/webhooks/gitea. 16+ characters. |
|
BOT_USERNAME |
no | anvil |
The bot's Gitea username; people mention it as @<name> in pull requests. |
|
BOT_ALIASES |
no | reviewer |
Other @names the bot answers to (comma-separated), e.g. a name it had before. | |
GITEA_OAUTH_CLIENT_ID |
no | OAuth2 application behind "Continue with Gitea". Redirect URI <PUBLIC_URL>/auth/callback. Written by the first-run setup. |
||
GITEA_OAUTH_CLIENT_SECRET |
no | secret | Secret of that OAuth2 application. | |
GITEA_ADMIN_TOKEN |
no | secret | Gitea admin token (write:admin, write:repository, write:issue, write:user, write:organization, read:misc). Creates the Gitea user behind each Anvil account. Without it hosted repositories only work for people who sign in with Gitea. Its name must not start with anvil-. |
Sign-in and accounts #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
GITHUB_CLIENT_ID |
no | OAuth client for "Continue with GitHub" when no GitHub App was created in Settings → GitHub (the App's own client wins). Redirect URI <PUBLIC_URL>/auth/github/callback. |
||
GITHUB_CLIENT_SECRET |
no | secret | Secret of that OAuth client. | |
DASHBOARD_USERS |
no | (empty) | Account logins (comma-separated) let in without an invitation. | |
ADMIN_EMAILS |
no | (empty) | Email addresses (comma-separated) whose accounts are admins once they sign in with a verified address. Admin stays on the account. | |
ADMIN_RECOVERY_EMAIL |
no | (empty) | An admin's email address. At start Anvil logs a one-time sign-in link for that admin (valid 15 minutes), for getting back in without mail. Remove it once you're in. | |
ADMIN_GITHUB_LOGINS |
no | (empty) | GitHub logins (comma-separated) that are admins when they sign in with GitHub. |
AI #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
ANTHROPIC_API_KEY |
yes | secret | Anthropic API key for reviews, questions, CI diagnosis and agents. | |
REVIEW_MODEL |
no | claude-opus-5-5 |
Claude model for reviews and agents. | |
REVIEW_EFFORT |
no | high |
Reasoning effort for reviews. | |
CHAT_EFFORT |
no | medium |
Reasoning effort for questions in comments and the Ask panel. | |
REVIEW_ROUTING |
no | auto |
auto picks a model per review by size and risk (light / standard / deep, see docs/REVIEW-COSTS.md); off sends every review to REVIEW_MODEL. |
|
REVIEW_MODEL_STANDARD |
no | claude-sonnet-5-5 |
Model for small, low-risk changes (the standard tier). | |
REVIEW_MODEL_LIGHT |
no | claude-haiku-5-5 |
Model for docs-only, formatting-only and bot pull requests (the light tier). | |
CHAT_MODEL |
no | claude-sonnet-5-5 |
Model for replies to comments and review threads. | |
REVIEW_MAX_DIFF_CHARS |
no | 160000 |
Most diff characters sent with the review prompt; the agent reads the rest of the change itself. | |
MAX_BUDGET_USD |
no | 3 |
Hard cost ceiling per agent run, in US dollars; .anvil.yaml can lower it, not raise it. |
|
MAX_TURNS |
no | 80 |
Most agent turns per run. | |
REVIEW_PACKED_TURNS |
no | 24 |
Turn limit for deep reviews that start with a context pack; a review that runs out is retried once at MAX_TURNS. | |
REVIEW_PREP |
no | on |
on prepares each review without the model first: a context pack of the changed code's callers, callees, types and tests, collapsed renames and moves, static checks, and a review memo (docs/REVIEW-COSTS.md). off reviews the plain diff. |
|
REVIEW_OSV |
no | on |
on looks up new and changed dependencies in the OSV vulnerability database (network); off only lists them. |
|
SEMGREP_RULES |
no | Semgrep rules to run on changed files before the model (a registry config or a path); unset skips Semgrep. | ||
SEMGREP_BIN |
no | semgrep |
Path of the semgrep binary, when Semgrep is installed in the image. | |
REVIEW_CACHE_TTL |
no | 5m |
Prompt cache lifetime for the stable part of the review prompt: 5m, or 1h (cache writes cost twice as much, reads are reused across reviews of the same repository for longer). |
|
AGENT_TIMEOUT_MS |
no | 1200000 |
An agent run is stopped after this many milliseconds. | |
WORK_DIR |
no | /tmp/anvil-work |
Where checkouts for reviews and agents are made (deleted after each job). | |
WORKER_CONCURRENCY |
no | 2 |
Jobs (reviews, agents, CI diagnosis) run at the same time. | |
AGENT_MEMORY_MB |
no | 1024 |
Memory one review or agent run is assumed to need; with the container's memory limit it caps how many run at once. | |
CLAUDE_CODE_PATH |
no | Path to the claude executable, when the bundled one can't be found. |
||
AGENTS_COMMANDS |
no | off |
local lets code-changing agents run the repository's agents.command (tests, lint) inside the worker. Repository code can then do anything the worker can; off by default. |
Mail #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
SMTP_URL |
no | secret | smtps://user:pass@host:465 for invitations and sign-in links. Without it invitation links are shown to copy and email sign-in only works in development. |
|
EMAIL_FROM |
no | Sender of Anvil's emails. Defaults to <INSTANCE_NAME> <noreply@<PUBLIC_URL host>>. |
||
ADMIN_NOTIFY_EMAIL |
no | Where new access requests are announced. |
Runners and CI #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
RUNNERS_SECRET |
no | secret | Shared secret between Anvil and the runner manager (runners/manager.mjs). 32+ characters. Without it Anvil runners (runs-on: anvil-*) are off. |
|
RUNNER_MAX_MINUTES_PER_DAY |
no | 600 |
Runner minutes per repository owner per day. | |
CI_LOG_RETENTION_DAYS |
no | 30 |
Days job logs and runner metrics are kept. | |
CI_LOG_MAX_BYTES |
no | 5242880 |
The most of one job log that is kept, in bytes. | |
CI_TEST_RETENTION_DAYS |
no | 90 |
Days test results are kept. | |
COMPUTE_RAW_HOURS |
no | 48 |
Hours raw CPU/memory samples are kept before they are rolled into one-minute averages. | |
RUNNER_POOL_CPUS |
no | CPU cores of the runner pool shown on the compute pages; by default the pool host protection reports, else the runner host's cores. | ||
RUNNER_POOL_MEMORY_GB |
no | Memory of the runner pool shown on the compute pages, in GB; same fallback as RUNNER_POOL_CPUS. | ||
CI_ESTIMATE_CPUS |
no | 2 |
CPUs assumed for jobs without samples (Gitea's runner, GitHub-hosted) when estimating compute. | |
CI_ESTIMATE_MEMORY_GB |
no | 7 |
Memory assumed for jobs without samples when estimating compute, in GB. | |
PUBLIC_STATS |
no | on |
off turns off GET /api/public/compute, the public aggregate CI numbers (counts and totals only, no names). |
Git over SSH #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
GIT_SSH_HOST |
no | Host in SSH clone URLs; defaults to PUBLIC_URL's host. An empty value or off hides SSH in the UI. |
||
GIT_SSH_PORT |
no | 2222 |
Port of Gitea's SSH server in clone URLs. | |
GIT_SSH_USER |
no | git |
User in SSH clone URLs. | |
GIT_SSH_FINGERPRINTS |
no | (empty) | Host key fingerprints shown in the docs, comma-separated, e.g. ED25519 SHA256:…,RSA SHA256:…. Get them with ssh-keyscan -p 2222 <host> | ssh-keygen -lf -. |
Development #
| Variable | Required | Default | Secret | What it does |
|---|---|---|---|---|
DEV_LOGIN |
no | Local development only: /auth/login signs in as this user. Ignored when NODE_ENV=production. |