All services in the Docker Compose stack read one shared .env. Variables marked Required have no safe default; everything else ships with the default shown and can be left alone for a trial.
Two secrets must match everywhere
JWT_SECRET and ENCRYPTION_KEY must be identical for the core proxy and the control plane (and any additional instances of either). The portal JWT is minted by the proxy and validated against JWT_SECRET; secrets encrypted by the control plane (provider keys, OAuth tokens) are decrypted by the proxy with ENCRYPTION_KEY. A mismatch produces HMAC failures and 401s everywhere. Likewise SKILL_RUNNER_JWT_SECRET must match between the core proxy and the skill runner.
Variable
Example
Required
What it does
BRUTOR_LICENSE_KEY
BRUTOR-V1-...
Required
License key; the core proxy refuses to start without a valid one.
JWT_SECRET
output of openssl rand -hex 32
Required
HMAC (HS256) signing secret for gateway and portal JWTs. Shared by core + control plane.
ENCRYPTION_KEY
output of python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Required
Fernet key encrypting stored secrets (provider API keys, OAuth tokens). Shared by core + control plane.
QDRANT_JWT_SIGNING_SECRET
openssl rand -hex 32
Required
Signs the short-lived (300 s) tenant-scoped JWTs used on every Qdrant call.
SKILL_RUNNER_JWT_SECRET
openssl rand -hex 32
Required
Authenticates core-proxy → skill-runner calls; must match on both services.
PROXY_HEALTH_CHECK_API_KEY
sk_brutor_api_<43 url-safe chars>
Required
API key the stack uses for internal health probes of the proxy.
SYSTEM_ADMIN_USERNAME
sysadmin
Optional
Seeded system-admin username (cross-tenant).
SYSTEM_ADMIN_PASSWORD
Admin123!
Change it
Seeded system-admin password.
DEFAULT_TENANT_ADMIN_USERNAME
admin
Optional
Seeded tenant-admin username for the default tenant.
DEFAULT_TENANT_ADMIN_PASSWORD
Admin123!
Change it
Seeded tenant-admin password.
Variable
Example
Required
What it does
BRUTOR_VERSION
0.9.68
Optional
Image tag applied to every ghcr.io/brutor-ai/* service. Pin it — see upgrades .
PROXY_PORT
8100
Optional
Host port of the core proxy (runtime + Portal API).
GATEWAY_PORT
5050
Optional
Host port of the control plane (admin API).
ADMIN_UI_PORT
3002
Optional
Host port of the Admin Console.
PORTAL_PORT
3001
Optional
Host port of the User Portal.
DB_PORT
5432
Optional
Host port of PostgreSQL.
REDIS_PORT
6379
Optional
Host port of Redis.
QDRANT_REST_PORT / QDRANT_GRPC_PORT
6333 / 6334
Optional
Host ports of Qdrant (REST / gRPC).
MINIO_API_PORT / MINIO_CONSOLE_PORT
9000 / 9001
Optional
Host ports of MinIO (S3 API / web console).
Variable
Example
Required
What it does
DB_HOST
postgres
Required
PostgreSQL host (service name inside the compose network).
DB_PORT
5432
Optional
PostgreSQL port.
DB_USER
brutor
Required
Database user.
DB_PASSWORD
brutor123
Required — change it
Database password.
DB_NAME
ai_gateway
Required
Database name — shared by both planes.
DB_POOL_SIZE
20
Optional
Connection-pool size per service.
DB_SSL_MODE
prefer
Optional
One of disable, allow, prefer, require, verify-ca, verify-full.
Variable
Example
Required
What it does
REDIS_URL
redis://redis:6379/0
Optional
Redis connection URL — rate-limit/quota counters and cache state.
REDIS_DISABLED
false
Optional
true switches to an in-memory fallback. Only correct for a single proxy instance — counters don’t propagate across pods without Redis.
Variable
Example
Required
What it does
SEMANTIC_CACHE_ENABLED
true
Optional
Master switch for the LLM semantic cache.
EMBEDDING_MODEL
text-embedding-3-small
Optional
Model used to embed prompts for similarity lookup.
SIMILARITY_THRESHOLD
0.95
Optional
Cosine-similarity cutoff for a semantic hit.
SEMANTIC_CACHE_EXACT_TTL_SECS
3600
Optional
TTL for exact-match entries.
SEMANTIC_CACHE_SEMANTIC_TTL_SECS
86400
Optional
TTL for semantic-match entries.
SEMANTIC_CACHE_TIME_BUCKET
daily
Optional
Time-bucketing of cache keys, so “today”-sensitive answers age out.
Variable
Example
Required
What it does
DEFAULT_EMBEDDING_MODEL
text-embedding-3-small
Optional
Embedding model for KB ingestion.
KB_POLL_INTERVAL_SECS
5
Optional
How often the uploader polls for new ingestion jobs.
KB_MAX_CONCURRENT_JOBS
4
Optional
Parallel ingestion jobs.
KB_CHUNK_SIZE_TOKENS
500
Optional
Chunk size for document splitting.
KB_CHUNK_OVERLAP_TOKENS
50
Optional
Overlap between adjacent chunks.
KB_EMBEDDING_BATCH_SIZE
50
Optional
Chunks embedded per provider call.
KB_EMBEDDING_CONCURRENCY
4
Optional
Parallel embedding batches.
Variable
Example
Required
What it does
MEDIA_STORAGE_BACKEND
s3
Optional
Storage backend for generated/uploaded media.
MEDIA_BUCKET
brutor-media
Optional
Bucket name.
MEDIA_S3_ENDPOINT
http://minio:9000
Optional
S3 endpoint — MinIO locally, AWS S3 in production.
MEDIA_S3_ACCESS_KEY_ID
minioadmin
Change it
S3 access key.
MEDIA_S3_SECRET_ACCESS_KEY
minioadmin
Change it
S3 secret key.
MEDIA_S3_FORCE_PATH_STYLE
true
Optional
Path-style addressing (required for MinIO).
MEDIA_SIGNED_URL_TTL_SECS
900
Optional
Lifetime of signed media URLs.
MEDIA_GC_INTERVAL_SECS
21600
Optional
Garbage-collection interval for expired media.
MinIO stopped publishing public images in September 2026 (quay.io/minio/* now
needs a login and docker.io/minio/* was removed). From the next release the
bundle runs Docker’s hardened MinIO image (dhi.io/minio), mirrored
unmodified to ghcr.io/brutor-ai/minio and pinned by digest, so it pulls
without a login like every other Brutor image. The media bucket is created by
the official amazon/aws-cli image.
Variable
Example
Required
What it does
MINIO_IMAGE
dhi.io/minio:<tag>
Optional
Replaces the mirrored image. To pull Docker’s copy directly, run docker login dhi.io (a free Docker account) first.
Any other S3-compatible store works too (AWS S3, Cloudflare R2, Ceph): set the
MEDIA_S3_* variables above and run the stack without the minio service.
Media storage holds only generated images, speech audio and video. Knowledge
bases, skills and chat do not use it, and the Portal shows media controls only
when an enabled image, speech or transcription model exists.
Variable
Example
Required
What it does
SKILL_RUNNER_URL
http://skill-runner:8210
Optional
Where the core proxy reaches the sandbox. Keep it internal — never expose :8210.
SKILL_RUNNER_JWT_TTL_SECS
300
Optional
Lifetime of per-execution JWTs the proxy mints for the runner.
SANDBOX_TIMEOUT_SECS
30
Optional
Wall-clock limit for sandboxed skill executions.
TRUSTED_TIMEOUT_SECS
120
Optional
Wall-clock limit for trusted skill executions.
MAX_REQUEST_BYTES
16777216
Optional
Maximum request size accepted by the runner (16 MiB).
Variable
Example
Required
What it does
KEY_PROVIDER
local
Optional
local (Fernet key from ENCRYPTION_KEY), aws_kms, or vault for envelope encryption with an external KMS.
KMS_KEY_ID
arn:aws:kms:eu-west-1:...:key/...
With aws_kms
KMS key to wrap data keys with.
AWS_REGION
eu-west-1
With aws_kms
AWS region for KMS calls.
AWS_KMS_ENDPOINT_URL
http://localstack:4566
Optional
Endpoint override — used with the kms compose profile (LocalStack).
VAULT_ADDR
http://vault:8200
With vault
Vault server address.
VAULT_TOKEN
hvs....
With vault
Vault auth token.
VAULT_TRANSIT_KEY
brutor
With vault
Transit key name.
VAULT_TRANSIT_MOUNT
transit
With vault
Transit engine mount path.
Variable
Example
Required
What it does
JWT_EXPIRES_IN
3600
Optional
JWT lifetime in seconds.
JWT_ALGORITHM
HS256
Optional
JWT signing algorithm.
CORS_ALLOW_ALL
false
Optional
true allows any origin — never in production .
CORS_ORIGINS
http://localhost:3001,http://localhost:3002
Optional
Comma-separated allowed origins (your portal + admin URLs). The core proxy reads it too: open the portal at an address that is not listed and its calls are blocked by the browser.
Variable
Example
Required
What it does
LOG_LEVEL
INFO
Optional
Control-plane (Python) log level.
USE_JSON_LOGGING
false
Optional
Structured JSON logs from the control plane.
RUST_LOG
brutor_gateway_core=info,tower_http=info
Optional
Core-proxy (Rust) log filter, standard env_logger syntax.
Variable
Example
Required
What it does
BRUTOR_DEPLOYMENT_MODE
self_hosted
Optional
self_hosted (default — includes sample seeds), saas, or development. Gates which seed data makes sense for the environment.
MCP_DEPLOYMENT_DRIVER
docker
Optional
How the control plane deploys containerized MCP servers: docker (drives the host Docker socket — fine for a single-user trial, not for multi-tenant setups), k8s, ecs, or none (refuse all container deployments).
BRUTOR_SHARED_MCP_SERVERS
[{"name": "app-map", ...}]
Optional
JSON array of MCP servers auto-registered at seed time (how the demo map server appears out of the box).