Environment variables
This page is for operators who run their own OttoTester instance. On a hosted OttoTester, configuration is managed for you — you can skip this.
.env.example at the repo root is the source of truth. It lists every variable with a documented default and a comment explaining it. Copy it to .env and fill in real values. The groups below are an overview, not a replacement for that file.
LLM provider
Section titled “LLM provider”OttoTester’s agents call an LLM. You set the provider and model:
OPENAI_API_KEY/ANTHROPIC_API_KEY/GOOGLE_GENERATIVE_AI_API_KEY— the provider key. Set the one your model needs.DEFAULT_MODEL— the global model, as aprovider:modelspec (for exampleanthropic:claude-3-5-sonnet).<AGENT>_MODEL— per-agent models (PLANNER_MODEL,GENERATOR_MODEL,HEALER_MODEL, and so on). Setup fills in each agent’s line with the model suggested for that agent, when your key offers it. Most agents without a line useDEFAULT_MODEL..env.examplenotes the two that borrow another agent’s model instead. Running setup again with a new key rewrites these lines.
Each day, OttoTester asks every provider you set a key for which models that key can use. With an OttoCloud account, it asks OttoCloud too. You can see these as the “model list” jobs on the Scheduled Jobs screen, and run any of them now. A job for a supplier you have no key for reads Off there, names the key it waits for, and has no Run now. It turns on by itself once the key is set and the server restarts. The price list jobs work the same way. On a server where each organization has its own OttoCloud account, the OttoCloud price list job waits for the server’s own OttoCloud key. That key only reads the model list and prices. Each organization’s AI calls are still billed to that organization. A job fails, and keeps the last list it had, when it can’t reach its provider. For OpenRouter, the list is your account’s own, so models your OpenRouter settings block are not offered. A model that appears on a list before OttoTester has a price for it shows on the Pricing page as unpriced, and you can pick it once it has a price.
The model picker under Global Admin > AI Models > Assignments offers only models from those lists. A supplier with no key offers none, and neither does one that turned down its key. The “Where models come from” card on Global Admin > AI Models > Model catalog lists the suppliers this install is set up for and says if one turned down its key. With “Hide models you can’t use” turned off, the catalog shows those models too, each with the reason it can’t be used, such as “Needs an Anthropic key”. Point at the reason to see the setting the key goes in. A line under the toggle counts how many models are waiting on each missing key. An agent still set to a model that’s no longer available shows “No longer available”, and a run stops before calling it, with a message naming the agent.
Database and auth
Section titled “Database and auth”MONGODB_URI— the MongoDB connection string.MASTER_KEY— encrypts stored credentials. The server won’t start without it. Back it up — if it’s lost, every encrypted credential is unrecoverable.BETTER_AUTH_SECRET/BETTER_AUTH_URL— session signing, and the URL the auth flow returns users to.TERMS_URL/PRIVACY_URL— your own terms and privacy pages. When either is set, signup asks people to agree to it before they can create an account. Leave both blank to ask nothing. Each must be a full http or https address, or the server won’t start.
EMAIL_PROVIDER— how email leaves your copy:none,smtp(your own mail server) orses. A new install ships withnone.EMAIL_FROM— who email appears to come from. Signup shows this sender, so people know which address to look for.
With EMAIL_PROVIDER=none, OttoTester still works without a mail server:
- New users don’t have to verify their address. After signing up they go straight in.
- Inviting someone gives you a link to send them yourself. Pending invitations have a Copy link button.
- Email options are hidden from notification settings, and the Email PDF buttons are hidden from reports. Saved email choices are kept for when email is turned on.
- The forgot-password screen tells people to ask whoever runs OttoTester.
To let someone back in, run the reset command where OttoTester runs. It prints a reset link that works once and expires in 1 hour, and records it in the audit log. It works whether email is on or off.
If you started OttoTester with pnpm dev, run this from the OttoTester folder while it is running:
pnpm reset-password-link person@example.comIf you run the production image, run this inside its backend container:
node dist/scripts/resetPasswordLink.js person@example.comFrontend and docs
Section titled “Frontend and docs”VITE_API_URL/VITE_WS_URL— where the frontend reaches the backend. Optional, and normally left unset: the frontend then uses its own address (/apiand/ws). Set them only to point the frontend at a backend running somewhere else.VITE_DOCS_BASE_URL— the docs origin the in-app help panel fetches from. Leave it unset to use the build-mode default.
Browser
Section titled “Browser”VITE_BROWSER_TYPE(chromium/firefox/webkit),VITE_BROWSER_HEADLESS, and theVITE_BROWSER_VIEWPORT_*size variables — the default browser configuration for Playwright runs.
Agent budgets
Section titled “Agent budgets”MAX_PLANNER_PER_TEST_ITERATIONS/MAX_GENERATOR_PER_TEST_ITERATIONS/MAX_HEALER_PER_ATTEMPT_ITERATIONS— caps on how long each agent loops before it stops. They protect a run from consuming unbounded LLM cost. The planner and generator count steps per test, so a run that asks for 3 tests gets 3 times the planner number, or twice that when its Create Tests template plans prerequisite tests automatically. The healer counts steps for each attempt to fix a test.
Tracing
Section titled “Tracing”Each agent run can send a trace to Arize Phoenix or Langfuse, one at a time. Traces in Phoenix or Langfuse explains each setting.
Every tracing setting can also be set on Global Admin → Observability, with no restart. A value saved there wins over the one in the settings file, and clearing it there brings the file’s value back.
TRACING_TOOL—phoenix,langfuseoroff. Only the named tool’s settings are read. Empty means Phoenix whenPHOENIX_ENDPOINTis set, otherwise off.PHOENIX_ENDPOINT(needed for Phoenix),PHOENIX_API_KEY,PHOENIX_PROJECT,PHOENIX_PUBLIC_URL— where Phoenix is, its key, its project, and the address a browser opens for its links.PHOENIX_HEADERS— extra headers sent to Phoenix, for a proxy or a hosted Phoenix, written like{"X-Team": "qa"}. Leave outAuthorization; the key sets it.LANGFUSE_BASE_URL,LANGFUSE_PUBLIC_KEY,LANGFUSE_SECRET_KEY(all needed for Langfuse),LANGFUSE_PUBLIC_URL— where Langfuse is, its project’s keys, and the address a browser opens for its links when it differs.
Updates
Section titled “Updates”Once a day, an install running a release reads GitHub’s public list of OttoTester releases. It sends nothing about your install. When a newer release is out, each Global Admin gets one notice, and the Build panel on the Global Admin page links to the release notes for it and shows the command that installs it. The panel’s Check for updates button checks at once.
RELEASE_CHECK_ENABLED— on unless you turn it off. Set it tofalseto make no request at all, from the very first start. Unset ortruemeans on, and any other value means off. Score sharing (TELEMETRY_ENABLED) does not affect it.
Related
Section titled “Related”- Reference → Settings, API tokens, Traces in Phoenix or Langfuse

