NuFiDocs

Environment variables

Every variable the compose stack reads, where each one comes from, and the ones the template forgets.

deploy/platform/.env is the stack's runtime configuration. bootstrap.sh creates it from .env.example and fills every value that ends in replace-me; nothing else is generated for you. One rule decides whether a variable reaches a service: only the gateway is handed the whole file (env_file), so any ${VAR} referenced from litellm/config.yaml works by being in .env. Every other service gets an explicit list in docker-compose.yml, and a variable missing from that list is invisible to the container however carefully you set it.

Secrets bootstrap generates

VariableGeneratorRead by
LITELLM_MASTER_KEYhex 32the gateway; the app and the console call it with this key
LITELLM_SALT_KEYhex 32the gateway, key hashing
POSTGRES_PASSWORDhex 16Postgres, Langfuse, postgres-exporter
DATABASE_URLderived: postgresql://npuops:<password>@postgres:5432/npuopsthe gateway
LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEYhex 16the gateway, the console, the e2e test
LANGFUSE_NEXTAUTH_SECRET, LANGFUSE_SALTbase64 32Langfuse
LANGFUSE_ENCRYPTION_KEYhex 32Langfuse
LANGFUSE_INIT_USER_PASSWORDhex 12Langfuse, the first admin's password
CLICKHOUSE_PASSWORD, MINIO_ROOT_PASSWORDhex 16ClickHouse, MinIO, Langfuse
JWT_SECRET, JWT_REFRESH_SECREThex 32the app and the console; must be identical in both
CREDS_KEY, CREDS_IVhex 32, hex 16the app, credential encryption
MONGO_INITDB_ROOT_PASSWORDhex 16MongoDB
MONGO_URIderived: mongodb://librechat:<password>@mongodb:27017/LibreChat?authSource=adminthe app
GRAFANA_ADMIN_PASSWORDhex 12Grafana
E2E_USER_PASSWORDhex 16the e2e test

Two consequences of how the fill works. A value you blanked stays blank; only the literal replace-me suffix is replaced. And once Postgres or MongoDB has initialised its volume, its password lives in the volume: a later change in .env breaks the connection instead of rotating the password. grep -c replace-me .env before the first up on a server.

Set by hand

Infrastructure:

VariableDefaultRead by
POSTGRES_USER, POSTGRES_DBnpuops, npuopsPostgres, Langfuse, the exporter
REDIS_HOST, REDIS_PORT, REDIS_PASSWORDredis, 6379, emptythe gateway, redis-exporter
CLICKHOUSE_USER, MINIO_ROOT_USERclickhouse, minioClickHouse, MinIO, Langfuse
LANGFUSE_HOSThttp://langfuse-web:3000the gateway, where it sends traces
LANGFUSE_NEXTAUTH_URLhttp://localhost:3000Langfuse, its own public URL
LANGFUSE_INIT_USER_EMAIL, LANGFUSE_INIT_USER_NAMEadmin@npuops.local, NPUOps AdminLangfuse, the first admin
GRAFANA_ADMIN_USERadminGrafana

The app (librechat service):

VariableDefaultWhat it does
APP_TITLENPUOpsthe name in the UI; set it to NuFi
ALLOW_REGISTRATION, ALLOW_EMAIL_LOGINtrue, trueself sign-up; password sign-in
CUSTOM_FOOTER, HELP_AND_FAQ_URL, PRIVACY_POLICY_URL, TERMS_OF_SERVICE_URL© NPUOps, https://npuops.local/docs, empty, emptyfooter text and links
CONSOLE_URLhttp://localhost:3001the Console entry in the account menu; empty hides it
LIBRECHAT_URLhttp://localhost:3080the app's own public URL; the compose file derives DOMAIN_CLIENT and DOMAIN_SERVER from it, and hands it to the console

Models, read by the gateway:

VariableDefaultWhat it does
GPU_BACKEND_BASE_URL, GPU_BACKEND_API_KEYhttp://host.docker.internal:11434/v1, ollamathe local or GPU model server add-model.sh registers against
NPU_BACKEND_BASE_URL, NPU_BACKEND_API_KEYemptythe NPU server, same shape
GEMINI_API_KEYempty, requiredlitellm/config.yaml ships two Gemini entries and the gateway refuses to start while the key is empty; set it, or remove the entries and rebuild
any other os.environ/NAME in litellm/config.yamladd-model.sh --api-key-env NAME adds the reference; you add the value here

Guardrails and tests:

VariableDefaultWhat it does
SCANNER_MODEL_ID, SCANNER_MODEL_REVISIONprotectai/deberta-v3-base-prompt-injection-v2, a pinned committhe injection classifier the scanner downloads
HF_TOKENemptyonly if the model needs an authenticated download
E2E_USER_EMAIL, E2E_USER_NAME, E2E_MODEL, E2E_EXPECTED_HARDWARE_ID, E2E_ENDPOINT_NAMEe2e@npuops.local, E2E Bot, qwen2.5-3b, mac-local, NPUOpsthe end-to-end test, which does not pass today; see Troubleshooting
LAKERA_API_KEYemptyread by nothing; left over from an earlier control

Read by the stack, missing from the template

Add these to .env yourself.

VariableFallbackRead by
NUFI_CONSOLE_TAGmainthe console image tag
DEFAULT_USER_BUDGET, DEFAULT_BUDGET_DURATION, DEFAULT_TPM_LIMIT, DEFAULT_RPM_LIMIT10, 30d, 10000, 60the console, for a newly provisioned user

Two more are honoured by the app but never reach it on this stack, because the librechat service's environment list does not include them. Until the two lines are added to docker-compose.yml, setting them in .env does nothing:

VariableWhat it would do
AGENTS_URLlibrechat.yaml uses it for the Agents entry that leads to NuFi Studio and NuFi Works; without it the entry is hidden
COOKIE_DOMAIN, COOKIE_SAMESITEshare the session across subdomains; see SSO and reverse proxy

Two console variables are in the same position, read by the console but not passed by the compose file: KEY_DEFAULT_DURATION (code default 90d) and AGENTS_ALLOWED_ORIGINS (unset, which disables the connect endpoint for the agent products). Add them to the console service's environment: when you need them.

Two notes on values. REDIS_PASSWORD is empty because the Redis container starts without requirepass; setting the variable alone makes the clients send a password Redis does not expect. LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY keep their pk-lf- and sk-lf- prefixes; bootstrap fills only the random part.

Production values

On a public deployment the URLs above become the real ones: LIBRECHAT_URL and CONSOLE_URL to the hostnames users open, LANGFUSE_NEXTAUTH_URL to Langfuse's, and ALLOW_REGISTRATION=false once the admin account exists. Pin NUFI_CONSOLE_TAG to a version.

The wrapper stack

deploy/railway/.env.example is a different, shorter file for the app on its own:

VariableDefaultWhat it does
IMAGE_TAGmainthe app image tag
DOMAIN_CLIENT, DOMAIN_SERVERhttp://localhost:3081the app's public URL
APP_TITLE, ALLOW_REGISTRATION, ALLOW_EMAIL_LOGINNuFi Chat, true, trueas above; set the title to NuFi
COOKIE_DOMAIN, COOKIE_SAMESITEempty, strictpassed through to the app here
JWT_SECRET, JWT_REFRESH_SECRET, CREDS_KEY, CREDS_IVgenerated by its bootstrap.sh
BACKEND_BASE_URL, BACKEND_API_KEYthe gateway (https://api.codechi.me/v1 for NuFi's own) and a key for it
CONSOLE_URLemptythe Console entry
RAG_GOOGLE_API_KEY, EMBEDDINGS_PROVIDER, EMBEDDINGS_MODEL, RAG_PORT, google_genai, gemini-embedding-001, 8000the file-upload RAG service
SHARED_DOCKER_NETWORKcommentedjoin the platform stack's network on the same host
LITELLM_SYNC_ENABLED, LITELLM_BASE_URL, LITELLM_MASTER_KEYfalse, ,mirror admin-created endpoints into a central gateway; off by default

AGENTS_URL is referenced by that stack's librechat.yaml too, and not passed by its compose file either. On Railway the service variables reach the container directly, which is why the entry shows there.

Everything else

The console, the admin panel, NuFi Studio and NuFi Works are configured on their own pages: Work on the console lists every variable the console reads, Work on the admin panel the admin panel's, Installing NuFi Studio, Installing NuFi Works and Single sign-on for the agent apps the rest.