Environment variables

The .env keys a self-hosted Future AGI install reads most often: generated secrets, LLM provider keys, public URLs, email, sign-in, telemetry and image tags.

In this page

Standalone and Distributed read their settings from one .env file in the repository root. This page covers the keys you are most likely to set, grouped by what you want to do. The complete list, with every key’s default, the setups that read it and what goes wrong when it is wrong, is the Configuration reference.

📝
TL;DR

./bin/install creates .env from .env.example and generates this install’s secrets, so nothing in it is required for a local install. Change a key, then run docker compose up -d to apply it. Helm reads the chart’s values, not .env: see Configure.

What to set, by situation

You want toSet
Try it on your laptopNothing. Run ./bin/install.
Run LLM evals, the prompt playground and agent featuresOne provider key. See LLM providers.
Open the UI from another machine or a domainVITE_HOST_API, BASE_URL, FRONTEND_URL, APP_URL, MINIO_URL. See Public URLs.
Let anyone else reach Distributed’s API (port 8000)DEBUG=false first. Distributed runs Django with its debug pages on while ENV_TYPE is local, the default. Standalone sets DEBUG=false itself.
Send traces from SDKs on other machinesA reverse proxy in front of port 4318, and FI_COLLECTOR_PUBLIC_URL. See Public URLs.
Send invites and password resets by emailMAILGUN_API_KEY, MAILGUN_SENDER_DOMAIN, DEFAULT_FROM_EMAIL, optionally DEFAULT_REPLY_TO_EMAIL. See Email.
Stop sending deployment telemetryFUTURE_AGI_TELEMETRY_DISABLED=true, or install with ./bin/install --no-telemetry. See Telemetry.
Isolate code evals from each other on StandaloneCOMPOSE_PROFILES=sandbox. See Profiles.
Use a model server on your network, such as Ollama or vLLM, added in the UIAGENTCC_ALLOW_PRIVATE_PROVIDER_URLS=true. See LLM providers.
Pin image versionsFUTURE_AGI_VERSION, and on Distributed the other image tags. See Image versions.
Start the stack with plain docker compose, without the installerEvery key in Secrets the installer generates
Go to productionENV_TYPE=production, which turns off the local-mode fallbacks and Django’s debug pages, and the rest of the production checklist

Warning

On an install that already holds data, ENV_TYPE=production also sends new uploads to another bucket, fi-content instead of fi-content-dev. Files uploaded before stay in the old bucket, and features that look them up in the current one, such as knowledge bases, no longer find them. Set UPLOAD_BUCKET_NAME=fi-content-dev in .env before you switch. Links built from an APP_URL without a scheme also change to https:// for any host other than localhost or a loopback address.

How .env is read

Docker Compose reads .env from the directory you run it in, in two ways:

  • To fill the ${VAR} references in the compose files. A variable exported in your shell wins over .env here.
  • As an env_file for the application containers: app and serving in Standalone; backend, every worker*, serving and agentcc-gateway in Distributed. That is how an application key the compose files never mention, such as OPENROUTER_API_KEY, still reaches the app.

A few rules follow from that:

  • A key that a compose file sets in its environment: block wins over .env, so changing it in .env does nothing. Internal keys lists them.
  • ${KEY:-default} in a compose file treats an empty value (KEY=) as unset, and a key the compose files do not mention reaches the app as an empty string. Keep optional keys commented out (# KEY=) until you give them a value.
  • docker compose up -d applies a change. Compose recreates every container whose configuration changed and keeps the data volumes. A .env change therefore recreates every container listed above that reads .env: on Distributed, the API and all the workers too.
  • The compose files set the Postgres and ClickHouse hosts themselves, so values in .env do not reach them. Besides PG_HOST, PGBOUNCER_HOST and CH_HOST, the collector’s connection URLs name postgres and clickhouse, and on Distributed so do Temporal and PeerDB’s setup job. To use a managed database, override every key that docker compose config shows pointing at postgres or clickhouse in a docker-compose.override.yml, as in Move to managed data stores. On Distributed, also list the override in COMPOSE_FILE: see Choices the installer records.

Secrets the installer generates

On a fresh install (no volumes of this Compose project yet), ./bin/install fills every key below that is empty or still a CHANGEME- placeholder with a random value and writes it to .env. On any install it also fills INTEGRATION_ENCRYPTION_KEY, AGENTCC_WEBHOOK_SECRET and, on Standalone, REDIS_PASSWORD while they are unset. The installer never changes a value that is already set.

KeyGenerated onProtectsIf you change it
SECRET_KEYFresh installLogins, tokens and password-reset linksEveryone is signed out
PG_PASSWORDFresh installPostgresWritten into the Postgres volume on the first start. A later change gives password authentication failed until you restore the old value or wipe the volume.
MINIO_ROOT_PASSWORDFresh installObject storage: datasets, exports, media. Compose derives S3_SECRET_KEY from it.Takes effect on the next docker compose up -d
AGENTCC_INTERNAL_API_KEYFresh installCalls from the app to the LLM gateway, and through it your provider keysTakes effect on the next docker compose up -d
AGENTCC_ADMIN_TOKENFresh installThe gateway’s admin API. The gateway also sends it when it loads keys from the app.Takes effect on the next docker compose up -d
CH_PASSWORDFresh installClickHouse’s default user. Empty means no password, and code evals can then read and change every trace. No <, > or &.See the warning below
INTEGRATION_ENCRYPTION_KEYAny install, when unsetStored integration credentials (a Fernet key)Credentials saved with the old key cannot be decrypted, and those integrations must be connected again
AGENTCC_WEBHOOK_SECRETAny install, when unsetThe request logs the gateway sends to the app. Left empty on Distributed, the app refuses them and the gateway’s logs and analytics stay empty.Takes effect on the next docker compose up -d
REDIS_PASSWORDAny Standalone install, when unsetThe Redis inside the app container, which code evals can reach. Only letters, digits and - _ . ~: anything else stops the container at start.Takes effect on the next docker compose up -d

Warning

Set PG_PASSWORD and CH_PASSWORD before the first start and keep them. Postgres stores its password in its volume on the first start. On an existing install, ./bin/install stops whenever CH_PASSWORD (in .env or exported in your shell) differs from the password ClickHouse runs with, which docker compose exec clickhouse printenv CLICKHOUSE_PASSWORD prints: set it back, or re-run with --force once you mean the change. On Distributed a new CH_PASSWORD also stops the Postgres to ClickHouse sync until PeerDB’s ch_dest peer carries it, and removing a ClickHouse password is not supported. Keep INTEGRATION_ENCRYPTION_KEY as well: without the key they were saved with, stored integration credentials cannot be decrypted.

PG_USER, PG_DB and MINIO_ROOT_USER (each futureagi by default) also belong in .env before the first start. Changing PG_USER or PG_DB later points the app at a database that does not exist.

Installs made before Standalone became the default were given at most a generated MINIO_ROOT_PASSWORD. They may still run the defaults published in the repository for SECRET_KEY, PG_PASSWORD, AGENTCC_INTERNAL_API_KEY and AGENTCC_ADMIN_TOKEN, and a ClickHouse without a password. The installer keeps those values and warns about them, and ENV_TYPE=production does not refuse them either.

Replace the published SECRET_KEY, AGENTCC_INTERNAL_API_KEY and AGENTCC_ADMIN_TOKEN before anyone else can reach ports 8000 and 8090: SECRET_KEY signs every login token, and AGENTCC_ADMIN_TOKEN opens the gateway’s admin API, which lists and creates keys. Generate each with openssl rand -hex 32, set it in .env, and run docker compose up -d. Everyone is signed out once. PG_PASSWORD cannot be changed this way: see the table above.

To give such an install a ClickHouse password, set CH_PASSWORD and run docker compose up -d: the bootstrap re-creates the ClickHouse dictionaries with it. On Distributed, leave it empty unless you also update the ch_dest peer in PeerDB. The production checklist covers the rest.

To set the secrets yourself, for example before a first docker compose up without the installer, generate each with openssl rand -hex 32, and INTEGRATION_ENCRYPTION_KEY with:

python3 -c "import base64, os; print(base64.urlsafe_b64encode(os.urandom(32)).decode())"

Choices the installer records

KeyDefaultWhat it does
COMPOSE_FILEUnset: Standalone./bin/install --distributed writes docker-compose.distributed.yml, so plain docker compose commands use Distributed. It also turns off Compose’s automatic loading of docker-compose.override.yml, so list an override here too: docker-compose.distributed.yml:docker-compose.override.yml.
COMPOSE_PROJECT_NAMEfutureagiPrefixes container, volume and network names. Changing it on an existing install points it at new, empty volumes. For an isolated second copy, use a second checkout: there, ./bin/install --new-instance writes the next free name (futureagi-2, futureagi-3, …) when it finds the first copy’s volumes. In a checkout that has already run the installer, the flag does nothing.
FUTURE_AGI_VERSIONEmpty: latest./bin/install --from-source writes local, and on Distributed also FRONTEND_VERSION, AGENTCC_GATEWAY_VERSION and FI_COLLECTOR_VERSION. See Image versions.

Warning

Keep the COMPOSE_FILE line on a Distributed install. Without it, every plain docker compose command, such as docker compose up -d after a .env change or an upgrade, uses Standalone’s docker-compose.yml against the Distributed data. Only ./bin/install recognises a Distributed install by its volumes and writes the line back, so if the line is gone, run ./bin/install or add COMPOSE_FILE=docker-compose.distributed.yml to .env before any other docker compose command. Upgrading older installs describes what goes wrong and how to recover. The only time to delete the line is a switch to Standalone after wiping the data: see Switch setups.

COMPOSE_PROFILES is yours to set; the installer only reads it. It turns on optional services: ml and sandbox on Standalone; all, workers, observability and peerdb on Distributed. See Profiles.

LLM providers

Server-wide provider keys power the built-in evals, the prompt playground and the other platform features that call an LLM. One provider is enough to start, and workspaces can also add their own keys in the UI.

KeyDefaultProvider
OPENAI_API_KEYEmptyOpenAI, for the app and the gateway’s default route
ANTHROPIC_API_KEYEmptyAnthropic
GOOGLE_API_KEYEmptyGemini (Google AI Studio). The gateway receives it as GEMINI_API_KEY.
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEYEmptyAWS Bedrock
AWS_REGIONus-east-1Region for Bedrock calls

A wrong key stops nothing from starting: the calls that use it fail with the provider’s authentication error. The gateway’s bundled config routes OpenAI only. To route other providers through it, see LLM gateway. Keys for further providers, such as OpenRouter, Hugging Face and the speech providers, are in Other model and voice providers.

Two more keys control how the gateway reaches providers:

KeyDefaultWhat it does
AGENTCC_ALLOW_PRIVATE_PROVIDER_URLSfalsetrue lets providers added in the UI or the org API use base URLs on private networks (LAN addresses, Docker service names, host.docker.internal), such as an Ollama or vLLM server. One .env line reaches both the API and the gateway. Loopback, link-local and cloud metadata addresses stay refused. Providers listed in the gateway’s config.yaml need no setting.
AGENTCC_SYNC_INTERVAL60sHow often the gateway re-reads keys and organization settings from the app, as a Go duration (60s, 5m). See Keys and org settings sync.

Warning

With AGENTCC_ALLOW_PRIVATE_PROVIDER_URLS=true, anyone who can add a provider can point the gateway and the API at the databases and every other service on those networks, including a ClickHouse without a password on installs made before Standalone became the default. Turn it on only where everyone who can add a provider is trusted: see Private and local provider URLs.

Public URLs

Set these as soon as anyone opens the UI from anything other than http://localhost:3000: a server IP, a domain or a reverse proxy.

KeyDefaultWhat it sets
VITE_HOST_APIEmpty: http://localhost:8000 (follows BACKEND_PORT)The URL the browser uses to reach the API. Wrong or unset on a remote host, the UI loads but every API call fails, often reported as a CORS error.
BASE_URLhttp://localhost:8000The API’s own public URL: SSO and OAuth callbacks, the MCP and SDK endpoints shown in the UI, trusted CSRF origins
FRONTEND_URLUnset: links use APP_URLThe UI’s URL, for links in digest, annotation and rule-run emails and for MCP OAuth consent redirects
APP_URLlocalhost:3000 (follows FRONTEND_PORT)The UI’s address, as host[:port] or a full URL. Invite, verification and password-reset links are built from it. Without a scheme, links use http:// while ENV_TYPE is local (the default), so write https://app.example.com for a UI behind TLS.
MINIO_URLhttp://localhost:9005 (follows MINIO_API_PORT)Base URL of every stored-file link the browser opens: uploads, exports, audio and image previews
FI_COLLECTOR_PUBLIC_URLhttp://localhost:4318The OTLP/HTTP URL that SDKs on other machines send traces to, shown as FI_BASE_URL in the in-app SDK snippet and the setup screen
ALLOWED_HOSTS*Host names the API answers to, comma-separated. Standalone adds 127.0.0.1,localhost to a list without *. On Distributed, include backend,localhost,127.0.0.1 yourself: the workers call the API as backend.
CORS_ALLOWED_ORIGINSEmpty: any originBrowser origins allowed to call the API, such as https://app.example.com. Setting it turns off allow-all.

For example, for a UI, API and file storage on three HTTPS host names:

VITE_HOST_API=https://api.example.com
BASE_URL=https://api.example.com
FRONTEND_URL=https://app.example.com
APP_URL=https://app.example.com
MINIO_URL=https://files.example.com

VITE_HOST_API is written into the UI’s /config.js each time its container starts, so a change needs docker compose up -d, not a rebuild. That recreates app in Standalone, and in Distributed frontend along with every service that reads .env. The installer prints the exact VITE_HOST_API line for your LAN address, and when it moves BACKEND_PORT it rewrites a VITE_HOST_API that names localhost. One that names another host is yours to update.

In both Compose setups, object storage (port 9005) and the trace collector (port 4318) listen on 127.0.0.1 only. A browser on another machine needs a route to object storage: a reverse proxy on the host in front of 127.0.0.1:9005 that passes the path through unchanged, or, for a single user, an SSH tunnel (ssh -L 9005:127.0.0.1:9005 your-host). Set MINIO_URL to the URL the browser uses. SDKs on other machines need the same for 127.0.0.1:4318, with FI_COLLECTOR_PUBLIC_URL set to the proxy’s URL. Security & TLS covers the reverse proxy.

Email

Email is optional. Without it, emails are written to the app log instead of being sent; Email in Users & sign-in covers invites and password resets without it. Mailgun is the supported provider; SMTP settings are not read from the environment.

MAILGUN_API_KEY=YOUR_API_KEY
MAILGUN_SENDER_DOMAIN=mg.example.com
DEFAULT_FROM_EMAIL=Future AGI <noreply@mg.example.com>
# Optional: where replies go. Unset, emails carry no Reply-To header.
DEFAULT_REPLY_TO_EMAIL=support@example.com

DEFAULT_FROM_EMAIL must be on the sending domain, because Mailgun rejects senders on other domains. Left empty, it is Future AGI <noreply@<MAILGUN_SENDER_DOMAIN>>. Set APP_URL too, since the links in emails are built from it. Then run docker compose up -d.

Sign-in

KeyDefaultWhat it does
OSS_RETURN_PASSWORD_RESET_LINKfalsetrue returns the password-reset link in the browser, to anyone who asks, instead of emailing it. Read the warning in Reset a password before you turn it on.
RECAPTCHA_ENABLEDfalseKeep it false. reCAPTCHA also needs RECAPTCHA_SECRET_KEY and a UI image built with a site key. The published images have none, so turning it on rejects every sign-up and login.

Google, GitHub and Microsoft sign-in need an Enterprise Edition license (EE_LICENSE_KEY). Their keys and redirect URIs are in Single sign-on.

An install made before Standalone became the default may still have OSS_RETURN_PASSWORD_RESET_LINK=true in .env, from the .env.example of its release. Delete that line from .env and run docker compose up -d. Creating accounts and recovering them from the host are covered in Users & sign-in.

Telemetry

Deployment telemetry is on by default. Set FUTURE_AGI_TELEMETRY_DISABLED=true in .env and run docker compose up -d to turn it off, or install with ./bin/install --no-telemetry, which writes that line before anything starts. Telemetry and outbound connections covers what is sent, what the opt-out still sends, and every other outbound connection.

Image versions

KeyDefaultImage
FUTURE_AGI_VERSIONEmpty: latestThe Future AGI images: futureagi/standalone in Standalone, futureagi/future-agi (backend and workers) in Distributed. A release tag such as vX.Y.Z pins it; local means images built from the checkout by ./bin/install --from-source.
FRONTEND_VERSION, AGENTCC_GATEWAY_VERSIONlatestDistributed: the UI and the LLM gateway
FI_COLLECTOR_VERSIONlocal: built from ./fi-collectorDistributed: the trace collector
SERVING_VERSIONlatestThe embedding model server (Standalone’s ml profile, and Distributed). The image runs PyTorch on the CPU. On a host with an NVIDIA GPU, vX.Y.Z-gpu (or latest-gpu) selects the CUDA build, linux/amd64 only, and the container also needs the GPU: see ml: model serving. On Distributed, list that override in COMPOSE_FILE.
CODE_EXECUTOR_VERSIONlatestThe code executor (Standalone’s sandbox profile, and Distributed)
SIMULATION_RUNNER_VERSIONlatestDistributed: the simulation runner

Pin a release on any install others rely on, never latest or local. To upgrade or roll back, change the tag, then run docker compose pull and docker compose up -d. See Upgrades & rollback. On an install made before Standalone became the default, run ./bin/install after git pull instead, or first make sure .env holds COMPOSE_FILE=docker-compose.distributed.yml: see Upgrading older installs.

Ports

Every port the stack publishes has a key: FRONTEND_PORT (3000), BACKEND_PORT (8000), AGENTCC_GATEWAY_PORT (8090), FI_COLLECTOR_OTLP_PORT (4317), FI_COLLECTOR_OTLP_HTTP_PORT (4318) and MINIO_API_PORT (9005) in both setups, plus the ports only Distributed publishes. ./bin/install checks every port the chosen setup publishes, offers a free one when a default is taken, and writes it to .env. Requirements lists every port, its default and the interface it listens on.

Sizing

KeyDefaultWhat it does
GRANIAN_THREADS2API server threads, in both setups
REDIS_MAXMEMORY128mbStandalone: memory cap of the Redis inside app, which holds only cache and locks. At the cap it evicts the least recently used keys.

On Distributed, GRANIAN_WORKERS (default 1) sets the number of API server processes. The Temporal worker slots of each setup are in Temporal workers.

About 230 bounded application limits, such as page sizes, timeouts and batch sizes, can also be set by name in .env. futureagi/tfc/settings/runtime_setting_specs.py in the repository declares each with its default, minimum and maximum, and a value outside its bounds stops the app at start with a message naming the key.

Dive deeper

Was this page helpful?

Questions & Discussion