Blockyard is configured via a TOML file. By default, it looks for blockyard.toml in the current directory. Override the path with the --config CLI argument:

blockyard --config /etc/blockyard/config.toml

Every config field can also be overridden via environment variables using the pattern BLOCKYARD_<SECTION>_<FIELD> (uppercased). For example:

BLOCKYARD_SERVER_BIND=0.0.0.0:9090
BLOCKYARD_DOCKER_IMAGE=ghcr.io/rocker-org/r-ver:4.4.0

Sections#

[server]#

FieldDefaultDescription
bind127.0.0.1:8080Address and port the server listens on
shutdown_timeout30sTime to drain in-flight requests on shutdown
log_levelinfoLog verbosity: trace, debug, info, warn, error
management_bindSeparate listener for /healthz, /readyz, /metrics. See Observability.
session_secretSecret for signing session cookies. Required when [oidc] is set without [openbao]; auto-generated and stored in vault when [openbao] is configured.
external_urlPublic-facing URL of the server (used for OIDC redirect URIs)
trusted_proxiesCIDRs whose X-Forwarded-For to trust (e.g. ["10.0.0.0/8"])

[docker]#

FieldDefaultDescription
socket/var/run/docker.sockPath to the Docker (or Podman) socket
image(required)Base container image for workers and builds
shiny_port3838Port Shiny listens on inside the container
pak_versionstablepak release channel (stable, rc, or devel)
service_networkDocker network whose containers are reachable from workers
store_retention0Package store eviction duration (0 = disabled)
default_memory_limitFallback memory limit for workers (e.g. "2g"). Applies when no per-app limit is set.
default_cpu_limitFallback CPU limit for workers (e.g. 4.0). Applies when no per-app limit is set.

[storage]#

FieldDefaultDescription
bundle_server_path/data/bundlesDirectory for uploaded bundles
bundle_worker_path/appMount point inside worker containers
bundle_retention50Max bundles to keep per app before cleanup
max_bundle_size104857600Maximum upload size in bytes (default 100 MB)
soft_delete_retention0How long to keep soft-deleted apps (e.g. 720h for 30 days). 0 means immediate hard delete.

[database]#

FieldDefaultDescription
driversqliteDatabase driver: sqlite or postgres
path/data/db/blockyard.dbPath to the SQLite database file (when driver = "sqlite")
urlPostgreSQL connection string (when driver = "postgres")

[proxy]#

FieldDefaultDescription
ws_cache_ttl60sHow long to keep a backend WebSocket open after client disconnect. Enables transparent reconnection on network blips — see Reconnection on network interruptions.
health_interval15sInterval between worker health checks
worker_start_timeout60sMax time to wait for a worker to become healthy
max_workers100Maximum number of concurrent worker containers
log_retention1hHow long to keep worker log entries before cleanup
session_idle_ttl0Idle timeout for sessions and WebSocket connections. When non-zero, idle WebSocket connections are closed and stale sessions are swept. 0 = disabled.
idle_worker_timeout5mTime before an idle worker container is stopped
http_forward_timeout5mTimeout for forwarding HTTP requests to workers
max_cpu_limit16.0Max CPU limit settable per app
transfer_timeout60sTimeout for bundle transfers to workers
session_max_lifetime0Hard cap on session duration. 0 (default) means unlimited — sessions only end via idle timeout or worker shutdown.

[oidc] (optional)#

Enable OIDC authentication. When configured, server.session_secret is required unless [openbao] is also configured (in which case it can be auto-generated).

FieldDefaultDescription
issuer_url(required)OIDC provider issuer URL (must match the iss claim in tokens)
issuer_discovery_urlInternal URL for OIDC discovery when the IdP is at a different address server-side (e.g. Docker DNS). See Configuration Reference.
client_id(required)OIDC client ID
client_secret(required)OIDC client secret
cookie_max_age24hMax lifetime of session cookies
initial_adminOIDC sub of the first admin user. Checked only on first login. See First Admin Setup.

[openbao] (optional)#

Enable OpenBao credential management. Requires [oidc] to be configured.

FieldDefaultDescription
address(required)OpenBao server address
role_idOne of role_id or admin_tokenAppRole role identifier. The secret_id is delivered via BLOCKYARD_OPENBAO_SECRET_ID env var at bootstrap.
admin_tokenOne of role_id or admin_tokenDeprecated. Static admin token. Use role_id with AppRole auth instead.
token_ttl1hTTL for issued tokens
jwt_auth_pathjwtAuth method path in OpenBao
skip_policy_scope_checkfalseSkip vault policy scope verification during bootstrap. Useful when the OpenBao policy format differs from what Blockyard expects.

[[openbao.services]]#

Define third-party services whose API keys users can enroll via OpenBao.

[[openbao.services]]
id    = "openai"
label = "OpenAI"

Credentials are stored at secret/data/users/{sub}/apikeys/{id}.

FieldDefaultDescription
id(required)Unique identifier (also the vault path segment)
label(required)Human-readable label

[board_storage] (optional)#

Enable board storage via PostgREST. Requires database.driver = "postgres" and [openbao].

FieldDefaultDescription
postgrest_url(required)URL of the PostgREST instance (e.g. http://postgrest:3000)

Workers receive a POSTGREST_URL environment variable when this is configured.

[audit] (optional)#

Enable append-only audit logging.

FieldDefaultDescription
path(required)Path to the JSONL audit log file

[telemetry] (optional)#

Enable Prometheus metrics and OpenTelemetry tracing.

FieldDefaultDescription
metrics_enabledfalseExpose a /metrics endpoint for Prometheus
otlp_endpointOpenTelemetry collector endpoint (e.g. http://otel-collector:4317)

Example#

[server]
bind             = "127.0.0.1:8080"
shutdown_timeout = "30s"

[docker]
socket     = "/var/run/docker.sock"
image      = "ghcr.io/rocker-org/r-ver:4.4.3"
shiny_port = 3838
pak_version = "stable"

[storage]
bundle_server_path    = "/data/bundles"
bundle_worker_path    = "/app"
bundle_retention      = 50
max_bundle_size       = 104857600
# soft_delete_retention = "720h"   # 30 days; omit or 0 = immediate hard delete

[database]
driver = "sqlite"
path   = "/data/db/blockyard.db"

[proxy]
ws_cache_ttl         = "60s"
health_interval      = "15s"
worker_start_timeout = "60s"
max_workers          = 100
log_retention        = "1h"
# session_idle_ttl   = "0"
idle_worker_timeout  = "5m"

# Optional: OIDC authentication
# When enabled, server.session_secret is required unless [openbao] is also
# configured (in which case it is auto-generated and stored in vault).
# [oidc]
# issuer_url           = "https://idp.example.com/realms/myapp"
# issuer_discovery_url = ""      # optional: internal URL for OIDC discovery (e.g. Docker DNS)
# client_id            = "blockyard"
# client_secret        = "oidc-client-secret"
# cookie_max_age       = "24h"
# initial_admin        = "google-oauth2|abc123"   # OIDC sub of the first admin

# Optional: OpenBao credential management (requires [oidc])
# [openbao]
# address       = "http://openbao:8200"
# role_id       = "blockyard-server"       # AppRole role identifier (recommended)
# # admin_token = "vault-admin-token"      # deprecated: use role_id instead
# token_ttl     = "1h"
# jwt_auth_path = "jwt"
#
# [[openbao.services]]
# id    = "openai"
# label = "OpenAI"

# Optional: Board storage via PostgREST (requires postgres + openbao)
# [board_storage]
# postgrest_url = "http://postgrest:3000"

# Optional: Audit logging
# [audit]
# path = "/data/audit/blockyard.jsonl"

# Optional: Telemetry
# [telemetry]
# metrics_enabled = true
# otlp_endpoint   = "http://otel-collector:4317"