YAML Config File
The YAML config file provides a convenient way to set all club options in a single file instead of individual environment variables. Environment variables always take precedence over values in the config file.
Priority order: Environment variable > YAML config file > Default value
File Location
club looks for the config file in this order:
- The path specified by the
CONFIGenvironment variable /etc/club/config.yaml(default location)
# Explicit pathexport CONFIG=/opt/club/config.yaml
# Or use the default location# /etc/club/config.yamlConfiguration Loading Order
When the server starts, configuration is loaded in this sequence:
- If
CONFIGis set, load the YAML file it points to - Otherwise, check for
/etc/club/config.yaml(skipped silently if absent) - Apply environment variables as overrides
- Validate required fields and backend-specific requirements
- Fail fast with a clear error message if misconfigured
Keeping Secrets Out of the Config File
The config file does not perform any placeholder substitution: values are read literally as written. To keep secrets (the JWT secret, database passwords, S3 keys) out of a committed config file, leave those keys out of the YAML entirely and supply them as environment variables instead. Environment variables always override the config file, so a partial YAML file plus a handful of secret environment variables is the recommended pattern.
# config.yaml — safe to commit (no secrets)server_url: "https://packages.example.com"db: backend: postgresblob: backend: s3 s3: bucket: "club-packages" region: "us-east-1"# Secrets supplied via the environment, never written to the fileJWT_SECRET=$(openssl rand -hex 32)POSTGRES_URL=postgres://club:secret@db.example.com:5432/clubS3_ACCESS_KEY=AKIA...S3_SECRET_KEY=wJalrXUtnFEMI...Key shape
The loader expects flat snake_case keys at the top level of the file. Every key in the table below is a top-level key.
db_backend: postgressqlite_path: /data/db/club.dbblob_backend: s3s3_bucket: club-packagesFull Annotated Example
# =============================================================================# club configuration file# =============================================================================# Values are read literally — there is no placeholder substitution.# Environment variables always override values in this file, so leave# secrets out of the file and supply them via the environment instead.# =============================================================================
# ---------------------------------------------------------------------------# Server# ---------------------------------------------------------------------------
# Public URL of the club server (required).# Must include scheme (https://). No trailing slash.server_url: "https://packages.example.com"
# IP address to bind the HTTP server to. Default: 0.0.0.0host: "0.0.0.0"
# Port the server listens on inside the container. Default: 8080# (Named "port" here; the equivalent env var is LISTEN_PORT, intentionally# distinct from docker-compose's host-side PORT convention.)port: 8080
# Log verbosity: debug, info, warning, error. Default: infolog_level: info
# ---------------------------------------------------------------------------# Authentication & sessions# ---------------------------------------------------------------------------
# JWT signing secret (required). Minimum 32 characters.# Generate with: openssl rand -hex 32. Prefer supplying this via the# JWT_SECRET environment variable instead of writing it here.jwt_secret: "your-32-plus-character-secret-here"
# Web session TTL (hours). Default: 1session_ttl_hours: 1
# Default API token expiry (days). Default: 365token_expiry_days: 365
# bcrypt cost factor (10-14). Default: 12bcrypt_cost: 12
# Enable the /signup page and public signup endpoint. Default: falsesignup_enabled: false
# Trust X-Forwarded-Proto / X-Forwarded-For. Enable only behind a proxy# that strips client-supplied copies. Default: falsetrust_proxy: false
# Extra origins permitted on public, unauthenticated state-changing# endpoints (login, signup, setup). SERVER_URL is trusted implicitly.# allowed_origins: "https://www.packages.example.com,https://packages.internal.example.com"
# ---------------------------------------------------------------------------# Database (Metadata Store)# ---------------------------------------------------------------------------
# sqlite | postgres. Default: sqlitedb_backend: sqlite
# SQLite file path. Default: /data/db/club.dbsqlite_path: /data/db/club.db
# Required when db_backend is postgres. Prefer the POSTGRES_URL# environment variable so the password stays out of this file.# postgres_url: "postgres://club:secret@db.example.com:5432/club"
# ---------------------------------------------------------------------------# Blob Storage# ---------------------------------------------------------------------------
# filesystem | s3 | gcs. Default: filesystemblob_backend: filesystem
# Filesystem root. Default: /data/blobs# Stores package tarballs, per-version screenshots, and (when# dartdoc_backend is `blob`) per-package dartdoc indexed blobs.blob_path: /data/blobs
# S3 configuration (uncomment when blob_backend is s3).# Works for AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, Backblaze B2,# and GCS via its S3 interop endpoint.# Prefer supplying s3_access_key / s3_secret_key via the S3_ACCESS_KEY and# S3_SECRET_KEY environment variables instead of writing them here.# s3_endpoint: "https://s3.amazonaws.com" # optional for AWS, required otherwise# s3_bucket: "club-packages"# s3_region: "us-east-1" # optional; defaults to us-east-1# s3_access_key: "AKIA..."# s3_secret_key: "wJalrXUtnFEMI..."
# GCS (native) configuration (uncomment when blob_backend is gcs).# Auth priority: gcs_credentials_file > gcs_credentials_json > Application# Default Credentials. Under ADC there's no private key in process,# so downloads proxy through the server instead of redirecting.# gcs_bucket: "my-project.appspot.com"# gcs_credentials_file: "/secrets/sa.json"# gcs_credentials_json: '{"type":"service_account",...}'
# ---------------------------------------------------------------------------# Search# ---------------------------------------------------------------------------
# sqlite (FTS5) | meilisearch. Default: sqlitesearch_backend: sqlite
# Meilisearch configuration (uncomment when search_backend is meilisearch).# Prefer the MEILISEARCH_KEY environment variable for the API key.# meilisearch_url: "http://meilisearch:7700"# meilisearch_key: "your-meilisearch-api-key"
# ---------------------------------------------------------------------------# Upload# ---------------------------------------------------------------------------
# Temp dir for upload processing. Default: /data/tmp/uploadstemp_dir: /data/tmp/uploads
# Max tarball size in bytes. Default: 104857600 (100 MiB)max_upload_bytes: 104857600
# ---------------------------------------------------------------------------# Dartdoc# ---------------------------------------------------------------------------
# Serve backend for rendered dartdoc HTML.# - filesystem (default): local tree at dartdoc_path, served by shelf_static.# Requires a persistent volume at that path.# - blob: indexed blob persisted via the configured Blob# Store (works with filesystem/s3/gcs — orthogonal# to blob.backend). Required for multi-replica or# ephemeral container deployments.# See https://docs.club.birju.dev/reference/dartdoc-serving/ for the full spec.dartdoc_backend: filesystem
# Local filesystem root used when dartdoc_backend is filesystem.# Ignored in blob mode. Default: /data/cache/dartdocdartdoc_path: /data/cache/dartdoc
# In-process LRU cap for the blob serve path, in MiB. Only used when# dartdoc_backend is blob. Default: 64dartdoc_cache_max_memory_mb: 64YAML Key to Environment Variable Mapping
Each flat top-level YAML key maps to its corresponding environment variable:
| YAML Key | Environment Variable |
|---|---|
server_url | SERVER_URL |
host | HOST |
port | LISTEN_PORT |
log_level | LOG_LEVEL |
jwt_secret | JWT_SECRET |
session_ttl_hours | SESSION_TTL_HOURS |
token_expiry_days | TOKEN_EXPIRY_DAYS |
bcrypt_cost | BCRYPT_COST |
signup_enabled | SIGNUP_ENABLED |
trust_proxy | TRUST_PROXY |
allowed_origins | ALLOWED_ORIGINS |
db_backend | DB_BACKEND |
sqlite_path | SQLITE_PATH |
postgres_url | POSTGRES_URL |
blob_backend | BLOB_BACKEND |
blob_path | BLOB_PATH |
s3_endpoint | S3_ENDPOINT |
s3_bucket | S3_BUCKET |
s3_region | S3_REGION |
s3_access_key | S3_ACCESS_KEY |
s3_secret_key | S3_SECRET_KEY |
gcs_bucket | GCS_BUCKET |
gcs_credentials_file | GCS_CREDENTIALS_FILE |
gcs_credentials_json | GCS_CREDENTIALS_JSON |
search_backend | SEARCH_BACKEND |
meilisearch_url | MEILISEARCH_URL |
meilisearch_key | MEILISEARCH_KEY |
temp_dir | TEMP_DIR |
max_upload_bytes | MAX_UPLOAD_BYTES |
dartdoc_backend | DARTDOC_BACKEND |
dartdoc_path | DARTDOC_PATH |
dartdoc_cache_max_memory_mb | DARTDOC_CACHE_MAX_MEMORY_MB |
static_files_path | STATIC_FILES_PATH |
Example: PostgreSQL + S3 Config
A production config using PostgreSQL for metadata and S3 for blob storage. Secrets (JWT_SECRET, POSTGRES_URL, S3_ACCESS_KEY, S3_SECRET_KEY) are supplied as environment variables and omitted from the file:
server_url: "https://packages.example.com"log_level: infotrust_proxy: true
token_expiry_days: 90
db_backend: postgres
blob_backend: s3s3_bucket: "club-packages"s3_region: "us-east-1"
search_backend: sqliteExample: MinIO Config
Using MinIO as an S3-compatible blob store. Secrets (JWT_SECRET, POSTGRES_URL, S3_ACCESS_KEY, S3_SECRET_KEY, MEILISEARCH_KEY) are supplied as environment variables:
server_url: "https://packages.internal.example.com"trust_proxy: true
db_backend: postgres
blob_backend: s3s3_endpoint: "http://minio:9000"s3_bucket: "club-packages"s3_region: "us-east-1"
search_backend: meilisearchmeilisearch_url: "http://meilisearch:7700"Example: GCS (native) on GKE
Using the native GCS backend with Application Default Credentials from a workload-identity service account:
server_url: "https://packages.example.com"trust_proxy: true
db_backend: postgres
blob_backend: gcsgcs_bucket: "my-project.appspot.com"# gcs_credentials_file / gcs_credentials_json both unset — use ADC.# Downloads proxy through the server (no V4 signing without a private key).JWT_SECRET and POSTGRES_URL are supplied as environment variables.