Configuration reference
Every Bud setting in .env.example, its default and what it changes.
Bud reads its runtime configuration from environment variables. With Docker Compose they come from the .env file next to the compose file. On Cloudron, set them with cloudron env set. The repository’s .env.example carries the full key list and default values.
The install requires only a subset of these settings. Change the remaining defaults only to harden or tune a deployment.
Required keys
Section titled “Required keys”These are the keys the install sets. Replace every replace-with-... value.
| Setting | Default | What it does |
|---|---|---|
BUD_ENV |
production |
In production, Bud rejects placeholder secrets and the admin@example.com placeholder, requires an administrator password of at least 16 characters, sends auth cookies only over HTTPS, and writes JSON logs unless LOG_JSON says otherwise. |
BUD_VERSION |
stable |
Image tag to run. In production, pin an immutable version (such as 1.2.0) or use stable, the newest stable release. See Images and versions. |
DB_USER |
bud |
PostgreSQL user. |
DB_PASSWORD |
none | PostgreSQL password. Set a strong value. |
DB_NAME |
buddb |
PostgreSQL database name. |
DB_HOST |
localhost |
Database host. The bundled Compose file points DATABASE_URL at the postgres service. Set this for an external database instead. |
DB_PORT |
5432 |
Database port, for an external database. |
SECRET_KEY |
none | Application secret. A different SECRET_KEY invalidates sessions and runner tokens, so runners must re-register. Keep it in your secret store. |
ADMIN_EMAIL |
admin@example.com |
First administrator’s email. Production refuses this placeholder. |
ADMIN_PASSWORD |
none | First administrator’s password. At least 16 characters in production. |
ADMIN_FULL_NAME |
Bud Admin |
First administrator’s name. |
AUTO_SEED_ADMIN |
false |
true creates the administrator from the three values above at startup. false leaves account creation to /setup; the values still have to pass startup validation. |
APP_BASE_URL |
http://localhost:8001 |
Public URL of this Bud instance. Used in the links Bud emails (invites, email verification, password resets) and in report links. |
BUD_APP_URL |
http://localhost:8001 |
Public URL of Bud. Set it in the .env of both Bud and Bloom so each app links to the other. |
Operational settings
Section titled “Operational settings”# Leave the interactive API docs off in production: they expose the full# schema and every endpoint to anyone who can reach the host.ENABLE_DOCS=false
# One-off consistency pass at startup. Off by default; turn on only when a# release note asks for it.RUN_STARTUP_DATA_REPAIR=false
LOG_LEVEL=INFOLOG_JSON=trueENABLE_METRICS=true
# Browser origins allowed to call the API (JSON array). Set your public URL(s) in production.CORS_ORIGINS=["https://bud.example.com"]
# Access and refresh token lifetimes; auth cookies are HTTPS-only in production.ACCESS_TOKEN_EXPIRE_MINUTES=30REFRESH_TOKEN_EXPIRE_DAYS=30AUTH_COOKIE_SECURE=true| Setting | Default | What it does |
|---|---|---|
ENABLE_DOCS |
false |
Interactive API documentation. Leave it off in production: it exposes the full schema and every endpoint to anyone who can reach the host. Enable it only in a trusted development environment. |
RUN_STARTUP_DATA_REPAIR |
false |
One-off consistency pass at startup. Turn it on only when a release note asks for it. Database schema changes are applied only through Alembic. |
LOG_LEVEL |
INFO |
Root log level (DEBUG, INFO, WARNING, …). |
LOG_JSON |
true |
true writes one JSON object per log line, false writes text. Unset, Bud writes JSON in production and text otherwise. |
ENABLE_METRICS |
true |
Serves Prometheus metrics at /api/metrics. |
CORS_ORIGINS |
["https://bud.example.com"] |
Browser origins allowed to call the API, as a JSON array. Set your public URL or URLs in production. |
Bud writes logs to stdout, so any container log driver or shipper picks them up. Every response carries an X-Request-ID header. Incoming X-Request-ID values are propagated, so you can trace a request across a reverse proxy, Bud, and your log aggregator.
/api/metrics reports http_requests_total (request counts by method, route template, and status) and http_request_duration_seconds (a latency histogram), plus standard Python process metrics. The endpoint is unauthenticated, as is the Prometheus convention. Keep it reachable only from your monitoring network, or set ENABLE_METRICS=false.
Sessions and tokens
Section titled “Sessions and tokens”| Setting | Default | What it does |
|---|---|---|
ACCESS_TOKEN_EXPIRE_MINUTES |
30 |
Lifetime of the access token a browser receives at sign-in. |
REFRESH_TOKEN_EXPIRE_DAYS |
30 |
Lifetime of the refresh session. Users sign in again once both the access and refresh sessions expire. |
AUTH_COOKIE_SECURE |
true |
Sends auth cookies only over HTTPS. Defaults to true in production. |
RUNNER_TOKEN_EXPIRE_HOURS |
2160 |
Lifetime of the runner token issued at registration: 90 days. Separate from user sessions. |
RUNNER_HEARTBEAT_TIMEOUT |
120 |
Runner heartbeat timeout, in seconds. |
SMTP is optional for a single pre-seeded administrator, but required for invitations, password resets, and approved email changes. See Email and invites.
| Setting | Default | What it does |
|---|---|---|
SMTP_ENABLED |
false |
Turns outgoing email on. |
SMTP_HOST |
empty | SMTP server host, supplied by the operator or hosting platform. |
SMTP_PORT |
587 |
SMTP submission port. Change it only when the mail provider uses another port. |
SMTP_USERNAME |
none | SMTP user name. |
SMTP_PASSWORD |
none | SMTP password. |
SMTP_FROM_EMAIL |
none | Sender address, required when SMTP is enabled. |
SMTP_REPLY_TO |
none | Optional Reply-To address. |
SMTP_STARTTLS |
true |
Use STARTTLS. |
SMTP_SSL |
false |
Use SSL. |
SMTP_TIMEOUT_SECONDS |
30 |
SMTP timeout, in seconds. |
INVITE_TOKEN_TTL_HOURS |
72 |
Lifetime of an invitation link, in hours. |
EMAIL_VERIFICATION_TOKEN_TTL_HOURS |
24 |
Lifetime of an email verification link, in hours. |
PASSWORD_RESET_TOKEN_TTL_HOURS |
2 |
Lifetime of a password reset link, in hours. |
Bloom integration
Section titled “Bloom integration”| Setting | Default | What it does |
|---|---|---|
BLOOM_APP_URL |
empty | Optional sidebar link to Bloom. The result-sync destination itself is saved by an administrator in Bud Settings. |
BUD_INTEGRATION_ENCRYPTION_KEY |
empty | Fernet key that encrypts the saved Bloom credential. Required only when Bud to Bloom result sync is configured. A different key makes the stored credential unreadable, so it must be rotated. |
Set this secret only when connecting Bud to Bloom. See Connecting to Bloom for the integration setup.
Artifact storage
Section titled “Artifact storage”Bud writes run artifacts to a volume. The byte limits are the guardrails that stop one runner filling the disk; ARTIFACT_RETENTION_DAYS is what actually reclaims it.
BUD_UPLOAD_DIR=/app/uploads # Cloudron sets this to /app/data/uploadsMAX_UPLOAD_SIZE_BYTES=26214400 # 25 MiB: single fileMAX_RUN_UPLOAD_BYTES=262144000 # 250 MiB: one run's totalMIN_UPLOAD_FREE_BYTES=1073741824 # 1 GiB: refuse uploads below thisMAX_CONCURRENT_UPLOADS_PER_PRINCIPAL=1UPLOADS_PER_15_MINUTES=10 # per-principal rate limitUPLOAD_STREAM_CHUNK_BYTES=1048576 # 1 MiBARTIFACT_RETENTION_DAYS=30| Setting | Default | What it does |
|---|---|---|
BUD_UPLOAD_DIR |
/app/uploads |
Artifact directory. UPLOAD_DIR is accepted as an unprefixed alias. Cloudron sets it to /app/data/uploads. |
MAX_UPLOAD_SIZE_BYTES |
26214400 |
Cap for a single file: 25 MiB. Operators may raise it to at most 100 MiB for trace-heavy runners. |
MAX_UPLOAD_SIZE_HARD_LIMIT_BYTES |
104857600 |
Hard ceiling MAX_UPLOAD_SIZE_BYTES may not exceed: 100 MiB. |
MAX_RUN_UPLOAD_BYTES |
262144000 |
Cap for the combined committed and reserved bytes of one run: 250 MiB. |
MIN_UPLOAD_FREE_BYTES |
1073741824 |
Free space Bud preserves: uploads are refused below 1 GiB. |
UPLOADS_PER_15_MINUTES |
10 |
Upload-start rate limit per authenticated runner or user. |
MAX_CONCURRENT_UPLOADS_PER_PRINCIPAL |
1 |
Active uploads allowed at once per authenticated runner or user. |
UPLOAD_STREAM_CHUNK_BYTES |
1048576 |
Upload stream chunk size: 1 MiB. |
ARTIFACT_RETENTION_DAYS |
30 |
Retention for the daily retention pass, which also removes orphaned files and expired reservations. |
ALLOWED_UPLOAD_MIME_TYPES |
unset | Advanced: JSON array of accepted upload MIME types. Unset uses the built-in allowlist. |
The run, not the suite name, is the boundary for MAX_RUN_UPLOAD_BYTES, so separate executions of the same named suite receive separate run budgets. Bud reserves run capacity before an upload. Different runners have independent request allowances, and the request controls do not shrink the per-run byte budget. Bud writes each upload through a temporary file and promotes only complete uploads.
S3-compatible storage
Section titled “S3-compatible storage”Bud can keep run artifacts and generated reports in an S3-compatible bucket under a per-instance prefix. The local directory stays as a mirror that reads fall back to when the bucket does not answer. Local storage remains the default. python -m app.storage migrate --to s3 copies existing files and checks their sizes; /api/ready reports the storage.
BUD_STORAGE_BACKEND=s3BUD_STORAGE_LOCAL_MIRROR=trueBUD_S3_BUCKET=BUD_S3_ENDPOINT_URL=BUD_S3_REGION=BUD_S3_PREFIX=budBUD_S3_ACCESS_KEY_ID=BUD_S3_SECRET_ACCESS_KEY=| Setting | Default | What it does |
|---|---|---|
BUD_STORAGE_BACKEND |
local |
local keeps artifacts in BUD_UPLOAD_DIR. s3 keeps them in a bucket (AWS S3, MinIO, Hetzner, Ceph). |
BUD_STORAGE_LOCAL_MIRROR |
true |
Also keeps every file in BUD_UPLOAD_DIR. Reads come from the bucket and fall back to the mirror when the bucket does not answer; the log then names the key as a warning. |
BUD_S3_BUCKET |
empty | The bucket. It must exist. |
BUD_S3_ENDPOINT_URL |
empty | Endpoint for any store that is not AWS. |
BUD_S3_REGION |
empty | Region, where the store needs one. |
BUD_S3_PREFIX |
bud |
Prefix that keeps several instances apart in one bucket. |
BUD_S3_ACCESS_KEY_ID |
empty | Access key. Set both keys, or neither to use the ambient AWS credentials (an instance role). |
BUD_S3_SECRET_ACCESS_KEY |
empty | Secret key. |
The migration keeps each file’s storage name under the prefix, checks sizes after the copy, and skips files already in the bucket, so it can be run again. The local files stay where they are.
/api/ready reports storage as local, s3, or s3-unreachable when the bucket did not answer. It stays 200 in that case: the database decides readiness, and reads can still come from the mirror. The free-space reserve applies to the local directory; quotas in the bucket are the operator’s. On Cloudron, keep the mirror: it lives under /app/data and is part of Cloudron’s backup.



