Skip to content
GitHub
documentation

Configuration reference

Every Bloom setting in .env.example, its default and what it changes.

Bloom reads its configuration from environment variables. Its .env.example carries the full key list and default values; an empty default means the value is not set. The install requires only a subset of these settings. Change the remaining defaults only to harden or tune a deployment.

The keys every deployment sets. See Install.

Setting Default What it does
BLOOM_VERSION stable Image tag to run. In production, pin an immutable version or use stable, the newest stable release. See Image tags.
BLOOM_ENV production Production mode. Startup rejects a missing or weak SECRET_KEY, requires a real ADMIN_EMAIL and an administrator password of at least 16 characters, logs JSON when LOG_JSON is unset, and sends auth cookies only over HTTPS by default.
POSTGRES_USER bloom Database user.
POSTGRES_PASSWORD empty Database password. Required.
POSTGRES_DB bloom Database name.
SECRET_KEY empty Signing key. Required. A different key invalidates sessions and pending invite and reset links.
ADMIN_EMAIL empty First administrator’s email. Production requires a real address.
ADMIN_PASSWORD empty First administrator’s password, at least 16 characters in production.
ADMIN_FULL_NAME Bloom Admin First administrator’s name.
AUTO_SEED_ADMIN false true creates the first administrator from the ADMIN_* values on a new database. With false, those values satisfy startup validation but create no account; create the administrator at /setup. Cloudron disables seeding.
APP_BASE_URL http://localhost:8000 Public URL of this Bloom instance. Used in the links Bloom emails (invites, email verification, password resets) and in campaign links.
BLOOM_APP_URL http://localhost:8000 Public Bloom URL. Set it in both the Bloom and the Bud .env. See Connecting to Bud.
# 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://bloom.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 docs. Enable them only in a trusted development environment.
RUN_STARTUP_DATA_REPAIR false One-off consistency pass at startup. Turn on only when a release note asks for it.
LOG_LEVEL INFO Root log level (DEBUG, INFO, WARNING, …). Logs go to stdout.
LOG_JSON true One JSON object per log line. Unset: JSON when BLOOM_ENV=production, text otherwise.
ENABLE_METRICS true Prometheus metrics at /api/metrics. The endpoint is unauthenticated: keep it reachable only from your monitoring network, or set false.
CORS_ORIGINS ["https://bloom.example.com"] Browser origins allowed to call the API, as a JSON array. Set your public URL in production.
ACCESS_TOKEN_EXPIRE_MINUTES 30 Access token lifetime.
REFRESH_TOKEN_EXPIRE_DAYS 30 Refresh token lifetime.
AUTH_COOKIE_SECURE true Send auth cookies only over HTTPS. Defaults to true in production.
BLOOM_DOTENV_DISABLED 1 Bloom reads configuration from the environment, not a .env file, in the container.

Every response carries an X-Request-ID header, and an incoming X-Request-ID is propagated. Pass it from your reverse proxy to trace a request across the proxy, Bud and your log aggregator. In nginx:

proxy_set_header X-Request-ID $request_id;

The metrics endpoint serves http_requests_total{method,path,status}, the http_request_duration_seconds{method,path} latency histogram, and the standard Python process and GC metrics.

Setting Default
SMTP_ENABLED false
SMTP_HOST smtp.example.com
SMTP_PORT 587
SMTP_USERNAME replace-with-smtp-username
SMTP_PASSWORD replace-with-smtp-password
SMTP_FROM_EMAIL noreply@example.com
SMTP_REPLY_TO support@example.com
SMTP_STARTTLS true
SMTP_SSL false
SMTP_TIMEOUT_SECONDS 30
INVITE_TOKEN_TTL_HOURS 72
EMAIL_VERIFICATION_TOKEN_TTL_HOURS 24
PASSWORD_RESET_TOKEN_TTL_HOURS 2

SMTP is optional for a single administrator, but required for invitations, password resets, email verification, and approved email changes. See Email and invites.

Setting Default What it does
SERVICE_TOKEN_PEPPER empty Peppers the service tokens Bloom issues to Bud. Required only for the optional Bud result sync. Use an independent random value of at least 32 characters. A different value invalidates Bud result-sync credentials.
BUD_APP_URL empty Bud’s public URL. Bloom navigation and execution links open Bud.

See Connecting to Bud.

INTEGRATION_ENCRYPTION_KEY is separate from the SERVICE_TOKEN_PEPPER used for Bud sync: it encrypts GitHub, GitLab, and Jira tokens and webhook secrets at rest. Bloom starts without it: only tracker credential operations fail closed. Generate it with:

python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Setting Default What it does
INTEGRATION_ENCRYPTION_KEY empty Fernet key for tracker credentials and webhook secrets. Only needed when GitHub, GitLab, or Jira issue synchronization is configured. A different key makes stored tracker secrets unreadable until they are rotated.

See Issue tracker sync.

# Only needed when GitHub, GitLab, or Jira issue synchronization is configured
INTEGRATION_ENCRYPTION_KEY=
BLOOM_ATTACHMENT_DIR=/app/attachments # Cloudron sets this to /app/data/attachments
BLOOM_MAX_ATTACHMENT_SIZE=26214400 # 25 MiB
BLOOM_MAX_DOCUMENT_ATTACHMENT_BYTES=262144000 # 250 MiB per document
BLOOM_MIN_ATTACHMENT_FREE_BYTES=1073741824 # 1 GiB
BLOOM_MAX_CONCURRENT_ATTACHMENT_UPLOADS_PER_USER=1
BLOOM_ATTACHMENT_UPLOADS_PER_15_MINUTES=10 # per-user rate limit
# Bloom reads configuration from the environment, not a .env file, in container
BLOOM_DOTENV_DISABLED=1
Setting Default What it does
BLOOM_ATTACHMENT_DIR /app/attachments Attachment directory. Mount durable storage here. Cloudron sets it to /app/data/attachments.
BLOOM_MAX_ATTACHMENT_SIZE 26214400 Largest single file, 25 MiB.
BLOOM_MAX_DOCUMENT_ATTACHMENT_BYTES 262144000 Total attachments per document, 250 MiB.
BLOOM_MIN_ATTACHMENT_FREE_BYTES 1073741824 Free space Bloom keeps on the attachment disk, 1 GiB.
BLOOM_ATTACHMENT_UPLOADS_PER_15_MINUTES 10 Upload starts per user in a rolling fifteen-minute window.
BLOOM_MAX_CONCURRENT_ATTACHMENT_UPLOADS_PER_USER 1 Active uploads per user.
ALLOWED_ATTACHMENT_MIME_TYPES unset Advanced. JSON array of accepted attachment MIME types, such as ["application/pdf","application/json"]. Unset uses the built-in allowlist.

Bloom streams each upload through a temporary file and promotes only a complete upload. Automated Bud report publishing uses a scoped service credential and is separate from the per-user allowance, with the same file, document, MIME type, and disk capacity checks. These controls limit resource exhaustion; they are not a malware scanner.

Bloom can keep attachments in an S3-compatible bucket (AWS S3, MinIO, Hetzner, Ceph), with the same local mirror, migration command, and readiness report as Bud. Local storage remains the default.

BLOOM_STORAGE_BACKEND=s3
BLOOM_STORAGE_LOCAL_MIRROR=true
BLOOM_S3_BUCKET=
BLOOM_S3_ENDPOINT_URL=
BLOOM_S3_REGION=
BLOOM_S3_PREFIX=bloom
BLOOM_S3_ACCESS_KEY_ID=
BLOOM_S3_SECRET_ACCESS_KEY=
Setting Default What it does
BLOOM_STORAGE_BACKEND local s3 keeps attachments in a bucket.
BLOOM_STORAGE_LOCAL_MIRROR true Also keep every file in BLOOM_ATTACHMENT_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.
BLOOM_S3_BUCKET empty Bucket name. The bucket must exist.
BLOOM_S3_ENDPOINT_URL empty Endpoint for any store that is not AWS.
BLOOM_S3_REGION empty Region, where the store needs one.
BLOOM_S3_PREFIX bloom Prefix that keeps several instances apart in one bucket.
BLOOM_S3_ACCESS_KEY_ID empty Access key. Leave both keys empty to use the ambient AWS credentials, such as an instance role.
BLOOM_S3_SECRET_ACCESS_KEY empty Secret key.

Copy existing files with python -m app.storage migrate --to s3. Each file keeps its storage name under the prefix, sizes are checked after the copy, and files already in the bucket are skipped, so the command can be run again. The local files stay where they are.

/api/ready reports storage: local, s3, or s3-unreachable when the bucket did not answer. It stays 200 in that case, because 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 yours to manage. On Cloudron, set these variables with cloudron env set and keep the mirror: it lives under /app/data and is part of Cloudron’s backup.

Bloom accepts .reqif XML and .reqifz ZIP archives. These limits bound upload size, archive expansion, hierarchy depth, object counts, and processing time. Raise them only when a legitimate file is rejected, and raise the specific one named in the error.

REQIF_MAX_REQUEST_BYTES=26214400 # 25 MiB: whole upload
REQIF_MAX_MEMBER_BYTES=26214400 # 25 MiB: any single file inside the zip
REQIF_MAX_ARCHIVE_ENTRIES=100 # files per archive
REQIF_MAX_COMPRESSION_RATIO=20 # decompression-bomb guard
REQIF_MAX_OBJECTS=999 # SPEC-OBJECTs per import
REQIF_MAX_RELATIONS=10000 # SPEC-RELATIONs per import
REQIF_MAX_HIERARCHY_DEPTH=100 # nesting depth
REQIF_PROCESSING_TIMEOUT_SECONDS=60
REQIF_IMPORTS_PER_15_MINUTES=5 # per-user rate limit
REQIF_STREAM_CHUNK_BYTES=1048576 # 1 MiB

An archive holds exactly one .reqif member. Bloom validates the import while streaming it, parses it in a time-limited worker process, and runs one active import per project. These controls limit malformed or resource-intensive imports; they are not a malware scanner.