Skip to content
GitHub
documentation

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.

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.
# 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=INFO
LOG_JSON=true
ENABLE_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=30
REFRESH_TOKEN_EXPIRE_DAYS=30
AUTH_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.

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.
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.

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/uploads
MAX_UPLOAD_SIZE_BYTES=26214400 # 25 MiB: single file
MAX_RUN_UPLOAD_BYTES=262144000 # 250 MiB: one run's total
MIN_UPLOAD_FREE_BYTES=1073741824 # 1 GiB: refuse uploads below this
MAX_CONCURRENT_UPLOADS_PER_PRINCIPAL=1
UPLOADS_PER_15_MINUTES=10 # per-principal rate limit
UPLOAD_STREAM_CHUNK_BYTES=1048576 # 1 MiB
ARTIFACT_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.

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=s3
BUD_STORAGE_LOCAL_MIRROR=true
BUD_S3_BUCKET=
BUD_S3_ENDPOINT_URL=
BUD_S3_REGION=
BUD_S3_PREFIX=bud
BUD_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.