# All properties

<!-- Generated by tools/config-reference/generate.py from flow.service.workflow 5.0.0-beta.39. Don't edit by hand. -->

# All properties

This page lists every property in the `application.properties` files of Flow's core and dispatcher images at `5.0.0-beta.39`: 189 properties, 120 for core and 69 for the dispatcher. It's the complete list. For the properties most deployments set, start with the [Configuration reference](https://www.useboomerang.io/docs/flow/main/hosting/configuration-reference).

- **Restart:** each service reads its properties when it starts, so a changed property takes effect only when you restart that service. Behaviour an admin changes while Flow runs is a setting: see [All settings](https://www.useboomerang.io/docs/flow/main/hosting/settings-reference).
- **Environment variable:** derived, not listed in the file. Spring's relaxed binding reads each property from an environment variable named for it in upper case, with dots, dashes and list indexes turned into underscores, and the variable overrides the file: `flow.encrypt.secret` becomes `FLOW_ENCRYPT_SECRET`.
- **Default:** the value in the file. A secret's default is never shown: its row says "(set your own)". Keep secrets in a Kubernetes Secret, as [Set the required configuration](https://www.useboomerang.io/docs/flow/main/hosting/configuring#set-the-required-configuration) shows. "Not set" marks a property the file shows only as a commented-out example.
- **Description:** the comment above the property in the file, with internal project codes removed. "See" names the property whose comment covers this one too. A **Note** is added by the docs, from Flow's specifications and changelog.

Rows are sorted by property, so properties that share a prefix sit together.

## Core

Set these on core (`service-core`). The file is `service-core/src/main/resources/application.properties`.

| Property | Environment variable | Default | Description |
| --- | --- | --- | --- |
| `core.feature.docs.enable` | `CORE_FEATURE_DOCS_ENABLE` | `true` |  |
| `core.feature.notifications.enable` | `CORE_FEATURE_NOTIFICATIONS_ENABLE` | `true` |  |
| `core.feature.support.enable` | `CORE_FEATURE_SUPPORT_ENABLE` | `true` |  |
| `core.platform.name` | `CORE_PLATFORM_NAME` | `Boomerang` |  |
| `flamingock.lock-quit-trying-after-millis` | `FLAMINGOCK_LOCK_QUIT_TRYING_AFTER_MILLIS` | `1800000` | See `flamingock.management-mode`. |
| `flamingock.management-mode` | `FLAMINGOCK_MANAGEMENT_MODE` | `INITIALIZING_BEAN` | Change units run while the context starts, before the web server and the scheduled sweeps; a failed unit fails startup and is retried on the next start. Replicas starting together wait on the lock for up to 30 minutes (a large v3 upgrade outlasts Flamingock's 3-minute default), then skip what another replica applied. `flamingock.enabled=false` turns migrations off. |
| `flow.activation-code` | `FLOW_ACTIVATION_CODE` | (set your own) | The activation code that makes the first admin of an empty install: at least 32 characters, else the start fails. Unset, activation is off and the start warns. `flow.otc` / `FLOW_OTC` is the retired name, still read when this is unset. An admin claim (`flow.security.admin-claim.*`) replaces the code.<br>**Note:** Must be at least 32 characters. See [Make the first admin with the activation code](https://www.useboomerang.io/docs/flow/main/hosting/sign-in#make-the-first-admin-with-the-activation-code). |
| `flow.apps.flow.url` | `FLOW_APPS_FLOW_URL` | `/apps/flow` |  |
| `flow.apps.url` | `FLOW_APPS_URL` | `/apps/flow` | **Note:** Not read by Flow at this release; `flow.apps.flow.url` is. |
| `flow.artifacts.enabled` | `FLOW_ARTIFACTS_ENABLED` | `false` | Artifacts: files upload tasks attach to a run, kept in an S3-compatible store (AWS S3, MinIO, Cloud Storage's S3 mode). Off until enabled; every artifact call then answers `ARTIFACTS_NOT_CONFIGURED`. Retention, the largest artifact and the storage quota are admin settings (Settings > Artifacts, Quotas), not properties. link-endpoint is the store address that task pods reach when it differs from service-core's (e.g. `host.docker.internal` under OrbStack). |
| `flow.artifacts.link-ttl-minutes` | `FLOW_ARTIFACTS_LINK_TTL_MINUTES` | `15` | See `flow.artifacts.enabled`. |
| `flow.artifacts.store.access-key` | `FLOW_ARTIFACTS_STORE_ACCESS_KEY` | (set your own) | See `flow.artifacts.enabled`. |
| `flow.artifacts.store.bucket` | `FLOW_ARTIFACTS_STORE_BUCKET` | `flow-artifacts` | See `flow.artifacts.enabled`. |
| `flow.artifacts.store.endpoint` | `FLOW_ARTIFACTS_STORE_ENDPOINT` | blank | See `flow.artifacts.enabled`. |
| `flow.artifacts.store.link-endpoint` | `FLOW_ARTIFACTS_STORE_LINK_ENDPOINT` | blank | See `flow.artifacts.enabled`. |
| `flow.artifacts.store.path-style` | `FLOW_ARTIFACTS_STORE_PATH_STYLE` | `true` | See `flow.artifacts.enabled`. |
| `flow.artifacts.store.region` | `FLOW_ARTIFACTS_STORE_REGION` | `us-east-1` | See `flow.artifacts.enabled`. |
| `flow.artifacts.store.secret-key` | `FLOW_ARTIFACTS_STORE_SECRET_KEY` | (set your own) | See `flow.artifacts.enabled`. |
| `flow.artifacts.upload-grace-minutes` | `FLOW_ARTIFACTS_UPLOAD_GRACE_MINUTES` | `60` | See `flow.artifacts.enabled`.<br>**Note:** How long, in minutes, a partly written upload is kept before Flow deletes it. |
| `flow.auth.oidc.client-id` | `FLOW_AUTH_OIDC_CLIENT_ID` | blank | See `flow.auth.oidc.issuer`. |
| `flow.auth.oidc.issuer` | `FLOW_AUTH_OIDC_ISSUER` | blank | `flow.auth.oidc.issuer` and `flow.auth.oidc.client-id` seed the auth settings' `oidc.issuer` and `oidc.clientId` at startup, only while those settings are empty (OidcSettingsSeedService); a value set later in Settings wins and is never overwritten. The issuer must be an absolute https URL (http only for localhost). Blank leaves the settings alone.<br>**Note:** See [Sign in with OpenID Connect](https://www.useboomerang.io/docs/flow/main/hosting/sign-in#sign-in-with-openid-connect). |
| `flow.baseUrl` | `FLOW_BASEURL` | blank |  |
| `flow.dispatcher.auth.enabled` | `FLOW_DISPATCHER_AUTH_ENABLED` | `true` | `/dispatcher/**` requires a real Flow token (global scope + a machine actorKind - see TokenActorKind/DispatcherAuthFilter). Set false only for local dev/test to disable enforcement entirely. Replaces the interim `flow.dispatcher.token` shared secret. |
| `flow.dispatcher.lease-ms` | `FLOW_DISPATCHER_LEASE_MS` | `90000` | Lease length (ms), set when a dispatcher claims a task and renewed by each heartbeat. MUST stay >= 2x the dispatcher beat interval (30s) plus a sweep interval - 90s is the floor - so a healthy dispatcher's lease never lapses between its claim and its next heartbeat, or between two. |
| `flow.dispatcher.poll.recheck-ms` | `FLOW_DISPATCHER_POLL_RECHECK_MS` | `1000` | How often (ms) a held dispatcher poll looks at the claimable page again; a task admitted just after a look waits up to this long to be claimed, and a sequential chain pays it on every hop. Floor 100. Cost: each dispatcher re-checks every pool it polls, so its idle queries per second are pools x 1000 / this value, all on the `claim_page` index. One dispatcher with seven pools against its own engine would set 200 (35 queries/s instead of 7). |
| `flow.encrypt.salt` | `FLOW_ENCRYPT_SALT` | (set your own) | See `flow.encrypt.secret`. |
| `flow.encrypt.secret` | `FLOW_ENCRYPT_SECRET` | (set your own) | Encryption of secured settings (AES-GCM). `flow.encrypt.*` is the key every install must set; a blank pair refuses startup. `mongo.encrypt.*` is the pair a v3 install encrypted with, read once by the migration that re-encrypts those values. Both default to `FLOW_ENCRYPT_SECRET`/`FLOW_ENCRYPT_SALT`, the variables v3's chart generated and kept in its Secret, so a v3 install upgrades with that Secret unchanged. `MONGO_ENCRYPT_SECRET`/`MONGO_ENCRYPT_SALT` override the v3 pair on its own.<br>**Note:** See [Keep the encryption key](https://www.useboomerang.io/docs/flow/main/hosting/configuring#keep-the-encryption-key). |
| `flow.engine.task.log.max-bytes` | `FLOW_ENGINE_TASK_LOG_MAX_BYTES` | `10485760` | Task logs. A task's stored log, when its dispatcher uploaded one, is served first. Otherwise the engine reads the live log from the dispatcher holding the claim, at the logsUrl it registered and with the token it registered; nothing on core names a dispatcher (`engine/LogClient.java`). The most bytes of a finished log a dispatcher may store for a task (PUT `/dispatcher/v1/taskrun/{id}/log`); a larger one is refused whole, never truncated. |
| `flow.engine.task.params.max-bytes` | `FLOW_ENGINE_TASK_PARAMS_MAX_BYTES` | `16384` | Engine-enforced payload caps (bytes, serialized JSON): resolved TaskRun params are checked at admission (oversize = the task is invalidated before it is ever claimable) and TaskRun results at end (oversize = the task fails, results are not persisted). Large values pass by reference: a workspace path or a URI. The results check is a storage guard against a faulty dispatcher, not an executor limit - each dispatcher enforces its own (Kubernetes' 4096-byte termination message). |
| `flow.engine.task.results.max-bytes` | `FLOW_ENGINE_TASK_RESULTS_MAX_BYTES` | `1048576` | See `flow.engine.task.params.max-bytes`. |
| `flow.environment` | `FLOW_ENVIRONMENT` | blank |  |
| `flow.error.include-cause` | `FLOW_ERROR_INCLUDE_CAUSE` | `false` | **Note:** `true` adds the underlying exception to an error response, as `cause`. |
| `flow.events.sink.destinations[0].header-name` | `FLOW_EVENTS_SINK_DESTINATIONS_0_HEADER_NAME` | not set | See `flow.events.sink.destinations[0].url`.<br>Commented out in the file, as an example: `flow.events.sink.destinations[0].header-name=Authorization`. |
| `flow.events.sink.destinations[0].header-value` | `FLOW_EVENTS_SINK_DESTINATIONS_0_HEADER_VALUE` | (set your own) | See `flow.events.sink.destinations[0].url`.<br>Commented out in the file, as an example. |
| `flow.events.sink.destinations[0].labels` | `FLOW_EVENTS_SINK_DESTINATIONS_0_LABELS` | not set | See `flow.events.sink.types`.<br>Commented out in the file, as an example: `flow.events.sink.destinations[0].labels=team=legal`. |
| `flow.events.sink.destinations[0].phases` | `FLOW_EVENTS_SINK_DESTINATIONS_0_PHASES` | not set | See `flow.events.sink.types`.<br>Commented out in the file, as an example: `flow.events.sink.destinations[0].phases=completed`. |
| `flow.events.sink.destinations[0].statuses` | `FLOW_EVENTS_SINK_DESTINATIONS_0_STATUSES` | not set | See `flow.events.sink.types`.<br>Commented out in the file, as an example: `flow.events.sink.destinations[0].statuses=failed,timedout`. |
| `flow.events.sink.destinations[0].types` | `FLOW_EVENTS_SINK_DESTINATIONS_0_TYPES` | not set | See `flow.events.sink.types`.<br>Commented out in the file, as an example: `flow.events.sink.destinations[0].types=workflowrun,taskrun`. |
| `flow.events.sink.destinations[0].url` | `FLOW_EVENTS_SINK_DESTINATIONS_0_URL` | not set | Sinks that authenticate with a request header instead. header-name defaults to Authorization; header-value is a secret and MUST come from the environment, never a checked-in value:<br>Commented out in the file, as an example: `flow.events.sink.destinations[0].url=https://receiver.example.com/events`. |
| `flow.events.sink.enabled` | `FLOW_EVENTS_SINK_ENABLED` | `false` | **Note:** `true` sends each run and task run status change to every sink as a CloudEvent. See [Send status events and traces](https://www.useboomerang.io/docs/flow/main/hosting/configuring#send-status-events-and-traces). |
| `flow.events.sink.payload` | `FLOW_EVENTS_SINK_PAYLOAD` | `thin` | How much of the run a status CloudEvent carries: thin (default) is identity and lifecycle only - id, refs, status, statusReason, phase, labels, creationDate, startTime, duration - and a consumer reads the run back over the API for params and results; full carries the whole public run model. |
| `flow.events.sink.phases` | `FLOW_EVENTS_SINK_PHASES` | not set | See `flow.events.sink.types`.<br>Commented out in the file, as an example: `flow.events.sink.phases=completed`. |
| `flow.events.sink.statuses` | `FLOW_EVENTS_SINK_STATUSES` | not set | See `flow.events.sink.types`.<br>Commented out in the file, as an example: `flow.events.sink.statuses=terminal`. |
| `flow.events.sink.types` | `FLOW_EVENTS_SINK_TYPES` | not set | Optional filters, empty = no filter. Top level (every sink) and per destination; a destination with none of its own inherits the top level. statuses matches the status as emitted, so with finally tasks use phases=completed for the true end of a run; labels (key=value, all must match) filter at delivery.<br>Commented out in the file, as an example: `flow.events.sink.types=workflowrun`. |
| `flow.events.sink.urls` | `FLOW_EVENTS_SINK_URLS` | blank | Sinks as bare URLs (comma separated). Any secret has to ride in the query string (...?token=xyz), where every proxy and access log records it - prefer the header form below. |
| `flow.externalUrl.navigation` | `FLOW_EXTERNALURL_NAVIGATION` | blank |  |
| `flow.externalUrl.platformNavigation` | `FLOW_EXTERNALURL_PLATFORMNAVIGATION` | `${users.base.url}/users/navigation` |  |
| `flow.externalUrl.user` | `FLOW_EXTERNALURL_USER` | blank |  |
| `flow.migration.reset-unreadable-settings` | `FLOW_MIGRATION_RESET_UNREADABLE_SETTINGS` | `false` | A v3 settings value that does not decrypt under `mongo.encrypt.*` stops the upgrade; true blanks each such value instead, logged by setting, to be entered again in Settings. |
| `flow.mongo.collection.prefix` | `FLOW_MONGO_COLLECTION_PREFIX` | `flow` | **Note:** Every collection is named `<prefix>_<name>`. |
| `flow.otc` | `FLOW_OTC` | (set your own) | See `flow.activation-code`.<br>**Warning:** Deprecated: use `flow.activation-code`. Flow reads it only while `flow.activation-code` is unset, and logs a deprecation warning. If both are set and differ, core doesn't start. |
| `flow.parameters.layer-cache.enabled` | `FLOW_PARAMETERS_LAYER_CACHE_ENABLED` | `true` | Per-instance cache of the global, workspace and context parameter layers and of revisions (ParamLayerCache): tasks admitted within the TTL share one read of each store. A write clears it on the writing instance; another instance sees the write within ttl. ttl=0s turns it off. |
| `flow.parameters.layer-cache.max-size` | `FLOW_PARAMETERS_LAYER_CACHE_MAX_SIZE` | `1000` | See `flow.parameters.layer-cache.enabled`. |
| `flow.parameters.layer-cache.ttl` | `FLOW_PARAMETERS_LAYER_CACHE_TTL` | `10s` | See `flow.parameters.layer-cache.enabled`. |
| `flow.queue.enabled` | `FLOW_QUEUE_ENABLED` | `true` | Kill switch: stops agent queue claiming only; recovery sweeps are never gated. |
| `flow.security.admin-claim.name` | `FLOW_SECURITY_ADMIN_CLAIM_NAME` | blank | An identity provider decides who is an admin: a token whose claim `name` (a string or an array such as roles or groups) holds any of `values` (comma-separated) makes its holder an admin on each sign-in, and a holder who no longer matches is demoted. Both unset leaves admin to the first sign-in. A verified OIDC `id_token` is always read; the unverified JWT an authenticating proxy forwards is read only with trust-forwarded-token=true, when that proxy is the trust boundary.<br>**Note:** See [Let your identity provider decide admins](https://www.useboomerang.io/docs/flow/main/hosting/sign-in#let-your-identity-provider-decide-admins). |
| `flow.security.admin-claim.trust-forwarded-token` | `FLOW_SECURITY_ADMIN_CLAIM_TRUST_FORWARDED_TOKEN` | `false` | See `flow.security.admin-claim.name`. |
| `flow.security.admin-claim.values` | `FLOW_SECURITY_ADMIN_CLAIM_VALUES` | blank | See `flow.security.admin-claim.name`. |
| `flow.security.bootstrap-token` | `FLOW_SECURITY_BOOTSTRAP_TOKEN` | (set your own) | `flow.security.bootstrap-token` is a secured install's first credential when no person will sign in: a `bfg_` token of at least 32 further characters, registered at startup as a global service token (BootstrapTokenService). Supply it as a secret; rotate it by changing the value and restarting. It is registered whether or not security is on; blank registers nothing.<br>**Note:** Clearing it revokes the bootstrap token at the next start. See [Machine tokens](https://www.useboomerang.io/docs/flow/main/hosting/machine-tokens). |
| `flow.security.enabled` | `FLOW_SECURITY_ENABLED` | `true` | Deployment shape. Every bean loads in every deployment; these properties express the shape. `flow.security.enabled` is the single switch for both security halves (authentication filter chain + endpoint authorization). Default true; with it false every request runs as a synthetic admin (UnauthenticatedGlobalToken) - set it only for a headless embedded engine or local dev.<br>**Note:** See [Choose a deployment shape](https://www.useboomerang.io/docs/flow/main/hosting/configuring#choose-a-deployment-shape). |
| `flow.security.token-cache.enabled` | `FLOW_SECURITY_TOKEN_CACHE_ENABLED` | `true` | Per-instance cache of validated bearer tokens keyed by hash (TokenLookupCache): one Mongo read per token per TTL; revoke evicts locally only, so another instance may honour a revoked token for up to ttl. |
| `flow.security.token-cache.max-size` | `FLOW_SECURITY_TOKEN_CACHE_MAX_SIZE` | `10000` | See `flow.security.token-cache.enabled`. |
| `flow.security.token-cache.ttl` | `FLOW_SECURITY_TOKEN_CACHE_TTL` | `60s` | See `flow.security.token-cache.enabled`. |
| `flow.security.tokens.dispatcher.hash` | `FLOW_SECURITY_TOKENS_DISPATCHER_HASH` | (set your own) | `flow.security.tokens.<name>` declares narrower machine tokens, registered at startup beside the bootstrap token (DeclaredTokenService). &lt;name> is 1 to 63 lowercase letters and digits only (not "bootstrap"): an underscore in an environment variable becomes a dot, so no hyphen or underscore survives. Keys: exactly one of hash or value, actor-kind (default SERVICE), permissions (global grants such as workspace/read; leave out for a token that holds none and so reaches only the dispatcher protocol), and optionally description and expiry (ISO-8601 instant or date). hash is the lowercase hex SHA-256 of the raw token, so the environment never holds it: printf %s "$RAW" \| sha256sum (macOS: shasum -a 256). The raw token a client sends must start with `bfg_`. Removing an entry revokes its token; to rotate, declare the new entry, roll the client, then remove the old one. As environment variables the name is part of the variable: `FLOW_SECURITY_TOKENS_DISPATCHER_HASH`, `_PERMISSIONS` (comma separated), `_ACTORKIND`, `_EXPIRY`.<br>Commented out in the file, as an example.<br>**Note:** Give every core instance the same `flow.security.tokens` entries: each instance revokes the declared tokens its own configuration doesn't name. See [Machine tokens](https://www.useboomerang.io/docs/flow/main/hosting/machine-tokens). |
| `flow.services.workflow.host` | `FLOW_SERVICES_WORKFLOW_HOST` | `localhost:7700` | **Note:** Read only through the `flow.workflow.*.url` properties, which start with it. |
| `flow.signOutUrl` | `FLOW_SIGNOUTURL` | blank |  |
| `flow.token.max-user-session-duration` | `FLOW_TOKEN_MAX_USER_SESSION_DURATION` | `24` | **Note:** Hours a session token lasts. |
| `flow.version` | `FLOW_VERSION` | blank |  |
| `flow.workflow.event.url` | `FLOW_WORKFLOW_EVENT_URL` | `${flow.services.workflow.host}/api/v2/event` | **Note:** Every run gets it as the context parameter `event-url`: `$(context.params.event-url)`. |
| `flow.workflow.webhook.url` | `FLOW_WORKFLOW_WEBHOOK_URL` | `${flow.services.workflow.host}/api/v2/webhook` | **Note:** Every run gets it as the context parameter `webhook-url`: `$(context.params.webhook-url)`. |
| `flow.workflow.wfe.url` | `FLOW_WORKFLOW_WFE_URL` | `${flow.services.workflow.host}/api/v2/callback` | **Note:** The wait-for-event route. Every run gets it as the context parameter `wfe-url`: `$(context.params.wfe-url)`. |
| `flow.workflowrun.auto-start-on-submit` | `FLOW_WORKFLOWRUN_AUTO_START_ON_SUBMIT` | `true` | Webhook and CloudEvent triggers start the runs they submit (submit means run; a run with workspaces still waits for dispatcher provisioning). Set false to park trigger-created runs for an external PUT `/workflowrun/{id}/start`. |
| `flow.workspace.single` | `FLOW_WORKSPACE_SINGLE` | `false` | `flow.workspace.single` serves the seeded `system` workspace alone: any other {workspace} path segment is refused (SingleWorkspaceInterceptor) and workspaces cannot be created or deleted. An embedding product sets it together with `flow.security.enabled=false`.<br>**Note:** See [Choose a deployment shape](https://www.useboomerang.io/docs/flow/main/hosting/configuring#choose-a-deployment-shape). |
| `logging.level.io.boomerang.core` | `LOGGING_LEVEL_IO_BOOMERANG_CORE` | `INFO` | See `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter`. |
| `logging.level.io.boomerang.engine` | `LOGGING_LEVEL_IO_BOOMERANG_ENGINE` | `INFO` | See `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter`. |
| `logging.level.io.boomerang.integrations` | `LOGGING_LEVEL_IO_BOOMERANG_INTEGRATIONS` | `INFO` | See `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter`. |
| `logging.level.io.boomerang.scheduler` | `LOGGING_LEVEL_IO_BOOMERANG_SCHEDULER` | `INFO` | See `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter`. |
| `logging.level.io.boomerang.security` | `LOGGING_LEVEL_IO_BOOMERANG_SECURITY` | `INFO` | See `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter`. |
| `logging.level.io.boomerang.workflow` | `LOGGING_LEVEL_IO_BOOMERANG_WORKFLOW` | `INFO` | See `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter`. |
| `logging.level.org.springframework.data.mongodb.core.MongoTemplate` | `LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_DATA_MONGODB_CORE_MONGOTEMPLATE` | `WARN` | See `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter`. |
| `logging.level.org.springframework.security` | `LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_SECURITY` | `WARN` | See `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter`. |
| `logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter` | `LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB_FILTER_COMMONSREQUESTLOGGINGFILTER` | `WARN` | Spring Logging. `io.boomerang.*` runs at INFO; DEBUG is opt-in (`logging.level.io.boomerang.engine=DEBUG`). No level prints a parameter value: logs carry names, and a secret's toString is masked. |
| `management.endpoint.health.probes.enabled` | `MANAGEMENT_ENDPOINT_HEALTH_PROBES_ENABLED` | `true` | Customising path of Health probe for kubernetes to be `/health` |
| `management.endpoints.web.base-path` | `MANAGEMENT_ENDPOINTS_WEB_BASE_PATH` | `/` |  |
| `management.health.mongodb.enabled` | `MANAGEMENT_HEALTH_MONGODB_ENABLED` | `false` |  |
| `management.tracing.export.enabled` | `MANAGEMENT_TRACING_EXPORT_ENABLED` | `false` | Distributed tracing (Micrometer Tracing + OTel/OTLP). To enable, set to true and point `management.opentelemetry.tracing.export.otlp.endpoint` at an OTLP collector. |
| `max.concurrent.workflows` | `MAX_CONCURRENT_WORKFLOWS` | `4` | **Note:** Not read by Flow at this release. Quotas are settings: see [Quotas](https://www.useboomerang.io/docs/flow/main/hosting/settings-reference#quotas) in All settings. |
| `max.workflow.count` | `MAX_WORKFLOW_COUNT` | `10` | **Note:** Not read by Flow at this release. Quotas are settings: see [Quotas](https://www.useboomerang.io/docs/flow/main/hosting/settings-reference#quotas) in All settings. |
| `max.workflow.duration` | `MAX_WORKFLOW_DURATION` | `300` | **Note:** Not read by Flow at this release. Quotas are settings: see [Quotas](https://www.useboomerang.io/docs/flow/main/hosting/settings-reference#quotas) in All settings. |
| `max.workflow.execution.monthly` | `MAX_WORKFLOW_EXECUTION_MONTHLY` | `100` | **Note:** Not read by Flow at this release. Quotas are settings: see [Quotas](https://www.useboomerang.io/docs/flow/main/hosting/settings-reference#quotas) in All settings. |
| `max.workflow.execution.time` | `MAX_WORKFLOW_EXECUTION_TIME` | `30` | **Note:** Not read by Flow at this release. Quotas are settings: see [Quotas](https://www.useboomerang.io/docs/flow/main/hosting/settings-reference#quotas) in All settings. |
| `mongo.encrypt.salt` | `MONGO_ENCRYPT_SALT` | Same as `FLOW_ENCRYPT_SALT` | See `flow.encrypt.secret`. |
| `mongo.encrypt.secret` | `MONGO_ENCRYPT_SECRET` | Same as `FLOW_ENCRYPT_SECRET` | See `flow.encrypt.secret`.<br>**Note:** Read once, by the migration that re-encrypts a v3 install's secured settings. See [Set the encryption keys](https://www.useboomerang.io/docs/flow/main/hosting/upgrading#set-the-encryption-keys). |
| `proxy.host` | `PROXY_HOST` | blank |  |
| `proxy.port` | `PROXY_PORT` | blank |  |
| `server.max-http-request-header-size` | `SERVER_MAX_HTTP_REQUEST_HEADER_SIZE` | `80KB` |  |
| `server.port` | `SERVER_PORT` | `7700` |  |
| `service.name` | `SERVICE_NAME` | `Workflow` |  |
| `spring.aop.proxy-target-class` | `SPRING_AOP_PROXY_TARGET_CLASS` | `true` |  |
| `spring.application.name` | `SPRING_APPLICATION_NAME` | `${service.name}-${flow.environment}` |  |
| `spring.autoconfigure.exclude[0]` | `SPRING_AUTOCONFIGURE_EXCLUDE_0` | `org.springframework.boot.security.autoconfigure.web.servlet.ServletWebSecurityAutoConfiguration` |  |
| `spring.data.mongodb.auto-index-creation` | `SPRING_DATA_MONGODB_AUTO_INDEX_CREATION` | `false` | The change units in `io.boomerang.migration` are the sole index authority; entity annotations stay inert so no index depends on which replica boots first. |
| `spring.data.mongodb.repositories.type` | `SPRING_DATA_MONGODB_REPOSITORIES_TYPE` | `auto` |  |
| `spring.main.allow-circular-references` | `SPRING_MAIN_ALLOW_CIRCULAR_REFERENCES` | `true` | temporary workaround, until the circualr dependencies are removed |
| `spring.mongodb.representation.uuid` | `SPRING_MONGODB_REPRESENTATION_UUID` | `java-legacy` | `JAVA_LEGACY` matches the pre-Boot-4 driver default so existing stored UUIDs keep resolving |
| `spring.mongodb.uri` | `SPRING_MONGODB_URI` | (set your own) | **Note:** Always set it: the default is a local development sample. See [Connect MongoDB](https://www.useboomerang.io/docs/flow/main/hosting/configuring#connect-mongodb). |
| `spring.mvc.async.request-timeout` | `SPRING_MVC_ASYNC_REQUEST_TIMEOUT` | `7200000` | How long a streamed response (a running task's log, an artifact download) may stay open: two hours, so a live log is followed for a long task rather than cut at the old 10 minutes. |
| `spring.profiles.active` | `SPRING_PROFILES_ACTIVE` | `live` |  |
| `spring.threads.virtual.enabled` | `SPRING_THREADS_VIRTUAL_ENABLED` | `true` | Virtual threads for request handling: the agent long-poll parks one request thread per connected agent for the poll window, so platform threads cap concurrent agents at the pool size. The claim path is measured not to pin virtual threads (see DispatcherPollerVirtualThreadTest), so parked pollers cost a cheap virtual thread and the agent count is no longer thread-bound. |
| `springdoc.api-docs.path` | `SPRINGDOC_API_DOCS_PATH` | `/api/docs/spec` |  |
| `springdoc.packages-to-scan` | `SPRINGDOC_PACKAGES_TO_SCAN` | `io.boomerang` |  |
| `springdoc.paths-to-exclude` | `SPRINGDOC_PATHS_TO_EXCLUDE` | `/internal/**` | `/internal/**` is called by the webapp's server only and is not part of the published API. |
| `springdoc.swagger-ui.disable-swagger-default-url` | `SPRINGDOC_SWAGGER_UI_DISABLE_SWAGGER_DEFAULT_URL` | `true` |  |
| `springdoc.swagger-ui.enabled` | `SPRINGDOC_SWAGGER_UI_ENABLED` | `true` |  |
| `springdoc.swagger-ui.operationsSorter` | `SPRINGDOC_SWAGGER_UI_OPERATIONSSORTER` | `alpha` |  |
| `springdoc.swagger-ui.path` | `SPRINGDOC_SWAGGER_UI_PATH` | `/api/docs/swagger-ui` |  |
| `springdoc.swagger-ui.showExtensions` | `SPRINGDOC_SWAGGER_UI_SHOWEXTENSIONS` | `true` |  |
| `springdoc.swagger-ui.tagsSorter` | `SPRINGDOC_SWAGGER_UI_TAGSSORTER` | `alpha` |  |
| `springdoc.swagger-ui.tryItOutEnabled` | `SPRINGDOC_SWAGGER_UI_TRYITOUTENABLED` | `false` |  |
| `users.base.url` | `USERS_BASE_URL` | blank |  |

## Dispatcher

Set these on the dispatcher (`service-dispatcher`). The file is `service-dispatcher/src/main/resources/application.properties`.

| Property | Environment variable | Default | Description |
| --- | --- | --- | --- |
| `dispatcher.executor` | `DISPATCHER_EXECUTOR` | `tekton` | Task execution runtime (TaskExecutor SPI): tekton \| kube-jobs.<br>**Note:** See [Choose an executor](https://www.useboomerang.io/docs/flow/main/hosting/dispatcher#choose-an-executor). |
| `dispatcher.logging.host` | `DISPATCHER_LOGGING_HOST` | `localhost` |  |
| `dispatcher.logging.port` | `DISPATCHER_LOGGING_PORT` | `9200` |  |
| `dispatcher.logging.type` | `DISPATCHER_LOGGING_TYPE` | `default` | **Note:** `default` reads a running task's log from its pod; `loki` queries Loki at `dispatcher.logging.host` and `dispatcher.logging.port`. |
| `dispatcher.tasks.hostaliases` | `DISPATCHER_TASKS_HOSTALIASES` | `[]` | **Note:** Applied to every task pod this deployment runs. Empty entries are dropped. |
| `dispatcher.tasks.nodeselector` | `DISPATCHER_TASKS_NODESELECTOR` | `{}` | **Note:** Applied to every task pod this deployment runs. |
| `dispatcher.tasks.runtimeClassName` | `DISPATCHER_TASKS_RUNTIMECLASSNAME` | blank | Kubernetes RuntimeClass for Task Pods (kube-jobs executor only); blank leaves it unset. |
| `dispatcher.tasks.serviceaccount` | `DISPATCHER_TASKS_SERVICEACCOUNT` | blank | **Note:** Set on Jobs only, with the `kube-jobs` executor. |
| `dispatcher.tasks.tolerations` | `DISPATCHER_TASKS_TOLERATIONS` | `[]` | **Note:** Applied to every task pod this deployment runs. Empty entries are dropped. |
| `flow.dispatcher.ai.endpoint` | `FLOW_DISPATCHER_AI_ENDPOINT` | blank | Executor-side credential defaults for every `ai` task pod: `OPENAI_BASE_URL` from the endpoint and `OPENAI_API_KEY` from the token, each injected only when set. A task's own endpoint and token params win. |
| `flow.dispatcher.ai.image-prefixes` | `FLOW_DISPATCHER_AI_IMAGE_PREFIXES` | `boomerangio/task-ai` | An `ai` catalogue revision declares its own image and command (boomerangio/task-ai, released from the boomerang-io/tasks repository on its own version line); the image must start with one of these comma-separated prefixes or the task is refused at dispatch (ImageNotAllowed). Add a prefix to run another ai worker image (`executor/TaskImageResolver.java`). |
| `flow.dispatcher.ai.otel.endpoint` | `FLOW_DISPATCHER_AI_OTEL_ENDPOINT` | blank | The OTLP endpoint `ai` task pods export their spans to (`OTEL_EXPORTER_OTLP_ENDPOINT`); blank sends none. |
| `flow.dispatcher.ai.otel.headers` | `FLOW_DISPATCHER_AI_OTEL_HEADERS` | (set your own) | The exporter's headers (`OTEL_EXPORTER_OTLP_HEADERS`), visible in the pod spec like the token. |
| `flow.dispatcher.ai.otel.protocol` | `FLOW_DISPATCHER_AI_OTEL_PROTOCOL` | `http/protobuf` | The exporter's protocol (`OTEL_EXPORTER_OTLP_PROTOCOL`), set only when the endpoint is: task-ai exports over http/protobuf or http/json; grpc is unsupported and logs one warning, exporting nothing. |
| `flow.dispatcher.ai.token` | `FLOW_DISPATCHER_AI_TOKEN` | (set your own) | See `flow.dispatcher.ai.endpoint`. |
| `flow.dispatcher.artifact.image` | `FLOW_DISPATCHER_ARTIFACT_IMAGE` | `boomerangio/task-flow:3.3.0` | The default worker image that runs the uploadartifact and downloadartifact tasks (its `artifact upload` / `artifact download` commands), released from boomerang-io/tasks (tasks/flow). |
| `flow.dispatcher.lease.beat-ms` | `FLOW_DISPATCHER_LEASE_BEAT_MS` | `30000` | Batched lease heartbeat: reports every TaskRun this dispatcher holds - from its claim, while it runs, and while its end report is still being retried - so the engine renews their lease rather than reap a task that is merely slow. The engine sets each lease at claim, so it MUST stay well under the engine's `flow.dispatcher.lease-ms` (90s). |
| `flow.dispatcher.logs.upload` | `FLOW_DISPATCHER_LOGS_UPLOAD` | `true` | See `flow.dispatcher.logs.url`. |
| `flow.dispatcher.logs.url` | `FLOW_DISPATCHER_LOGS_URL` | blank | Task logs. `flow.dispatcher.logs.url` is where the engine reads a running task's log from this dispatcher: this process's `/api/v1/logs/stream` at an address the engine can reach, e.g. `http://service-dispatcher:7702/api/v1/logs/stream`. It is sent at registration with a bearer generated at every start, which the engine presents and this process checks (LogAuthFilter). Blank registers no URL: the engine then has no live log for this dispatcher's running tasks, only the stored ones. With `flow.dispatcher.logs.upload=true` a finished task's pod log is stored with the engine (when it has an artifact store), where it is served first and outlives the pod. |
| `flow.dispatcher.name` | `FLOW_DISPATCHER_NAME` | `flow-tekton-dispatcher` | **Note:** The name the dispatcher registers with. |
| `flow.dispatcher.task-types` | `FLOW_DISPATCHER_TASK_TYPES` | `template,custom,script,ai,uploadartifact,downloadartifact` | The task types this dispatcher registers for; the engine hands it claims for these only. `ai` is registered by default, so a single-dispatcher install runs AI tasks out of the box; `generic` is dispatchable but opt-in. A deployment started with `flow.dispatcher.task-types=ai` is an AI network zone (see README.md). |
| `flow.dispatcher.task.max-in-flight` | `FLOW_DISPATCHER_TASK_MAX_IN_FLIGHT` | `25` | The most tasks this dispatcher runs at once. A slot is held from the claim until the engine has taken the task's end report, a Pending pod and a retried end included, and each poll asks the engine for no more than the free slots. 0 means no cap. |
| `flow.dispatcher.task.reconcile-ms` | `FLOW_DISPATCHER_TASK_RECONCILE_MS` | `3600000` | See `flow.dispatcher.task.reconcile.enabled`. |
| `flow.dispatcher.task.reconcile.enabled` | `FLOW_DISPATCHER_TASK_RECONCILE_ENABLED` | `true` | Task runtime TTL: runtime objects are removed `kube.task.ttlDays` days after completion regardless of `kube.task.deletion` - Jobs natively via ttlSecondsAfterFinished, Tekton TaskRuns via this reconciler (tekton executor only; each tick deletes this dispatcher's own finished TaskRuns that have outlived the TTL). |
| `flow.dispatcher.workspace.reconcile-ms` | `FLOW_DISPATCHER_WORKSPACE_RECONCILE_MS` | `60000` | See `flow.dispatcher.workspace.reconcile.enabled`. |
| `flow.dispatcher.workspace.reconcile.enabled` | `FLOW_DISPATCHER_WORKSPACE_RECONCILE_ENABLED` | `true` | Workspace reconciliation: each tick lists the workspace PVCs this dispatcher holds and asks the engine which owners are finished, then deletes those claims. Nothing on the run records it. |
| `flow.engine.dispatcher.token` | `FLOW_ENGINE_DISPATCHER_TOKEN` | (set your own) | Bearer token sent to the engine on dispatcher calls, and nowhere else. Blank = send nothing. It must be a global Flow token with an actor kind (a machine token), minted via the token API or declared in service-core's `flow.security.tokens` (no permissions needed).<br>**Note:** See [Machine tokens](https://www.useboomerang.io/docs/flow/main/hosting/machine-tokens). |
| `flow.engine.url` | `FLOW_ENGINE_URL` | `http://localhost:7700` | Engine: the base URL of service-core. The dispatcher routes are fixed by the contract (`contracts/dispatcher-v1.yaml`) under `/dispatcher/v1`. |
| `flow.environment` | `FLOW_ENVIRONMENT` | blank |  |
| `flow.instance` | `FLOW_INSTANCE` | `bmrg-flow` |  |
| `flow.product` | `FLOW_PRODUCT` | `bmrg-flow` |  |
| `flow.version` | `FLOW_VERSION` | `0.0.0` | **Note:** Passed to every task container as `FLOW_VERSION`. |
| `info.version` | `INFO_VERSION` | `@version.name@` |  |
| `kube.image.pullPolicy` | `KUBE_IMAGE_PULLPOLICY` | `IfNotPresent` |  |
| `kube.image.pullSecret` | `KUBE_IMAGE_PULLSECRET` | `boomerang.registrykey` | **Note:** The image pull secret on every task pod this deployment runs. |
| `kube.namespace` | `KUBE_NAMESPACE` | blank | The namespace every task Job/TaskRun, PVC and ConfigMap is created in. Blank = the kubeconfig context's namespace, or the service-account namespace in-cluster.<br>**Note:** When neither resolves, the dispatcher doesn't start. |
| `kube.resource.limit.cpu` | `KUBE_RESOURCE_LIMIT_CPU` | blank | CPU is unset by default: a CPU limit throttles a task rather than failing it, so an operator opts in. Kubernetes CPU units - 1 = one core, 500m = half a core. |
| `kube.resource.limit.ephemeral-storage` | `KUBE_RESOURCE_LIMIT_EPHEMERAL_STORAGE` | `16Gi` | Compute resources applied to every task container this deployment runs - one sizing per deployment, not per task. Blank means that request or limit is not set at all, so a deployment can run with memory limits and no CPU limit, or with none at all. Typically the request is 1/4 to 1/8 of the limit. A memory-backed `/data` (`kube.task.storage.data.memory`) is a tmpfs and counts against the memory limit, not ephemeral-storage. |
| `kube.resource.limit.memory` | `KUBE_RESOURCE_LIMIT_MEMORY` | `16Gi` | See `kube.resource.limit.ephemeral-storage`. |
| `kube.resource.request.cpu` | `KUBE_RESOURCE_REQUEST_CPU` | blank | See `kube.resource.limit.cpu`. |
| `kube.resource.request.ephemeral-storage` | `KUBE_RESOURCE_REQUEST_EPHEMERAL_STORAGE` | `2Gi` | See `kube.resource.limit.ephemeral-storage`. |
| `kube.resource.request.memory` | `KUBE_RESOURCE_REQUEST_MEMORY` | `2Gi` | See `kube.resource.limit.ephemeral-storage`. |
| `kube.task.backOffLimit` | `KUBE_TASK_BACKOFFLIMIT` | `0` | **Note:** Set on each Job, with the `kube-jobs` executor. |
| `kube.task.deletion` | `KUBE_TASK_DELETION` | `Never` | **Note:** Never applies: core sends every task with the **Tasks › Deletion Policy** setting, and this is only its fallback. See [Tasks](https://www.useboomerang.io/docs/flow/main/hosting/settings-reference#tasks) in All settings. |
| `kube.task.restartPolicy` | `KUBE_TASK_RESTARTPOLICY` | `Never` | **Note:** Set on each Job, with the `kube-jobs` executor. |
| `kube.task.storage.data.memory` | `KUBE_TASK_STORAGE_DATA_MEMORY` | `false` | **Note:** `true` backs a task's `/data` with memory, for a task whose `worker.storage.data.memory` param is also `true`. |
| `kube.task.timeout` | `KUBE_TASK_TIMEOUT` | `60` | **Note:** Minutes. The per-pod backstop for a task that reaches the dispatcher with no timeout, or 0. A timeout core sets wins. |
| `kube.task.ttlDays` | `KUBE_TASK_TTLDAYS` | `7` | **Note:** A finished Job or TaskRun is removed this many days after it completes, whatever the deletion policy. See [Clean up finished tasks](https://www.useboomerang.io/docs/flow/main/hosting/dispatcher#clean-up-finished-tasks). |
| `kube.timeout.failedConditionGraceSeconds` | `KUBE_TIMEOUT_FAILEDCONDITIONGRACESECONDS` | `60` | The Job controller writes `status.failed` before it writes the Failed condition (and the reason, e.g. DeadlineExceeded), so kube-jobs's JobWatcher waits this long after first observing `status.failed` >= 1 with no condition yet, before falling back to a generic JobFailed. |
| `kube.timeout.reconcileSeconds` | `KUBE_TIMEOUT_RECONCILESECONDS` | `30` | How often the executor watch loop reconciles by listing the Job/TaskRun by label, on top of the watch event stream - catches a dropped watch connection or a missed event. |
| `kube.timeout.startFailureGraceSeconds` | `KUBE_TIMEOUT_STARTFAILUREGRACESECONDS` | `60` | How long a container may be held on an image it cannot pull (ErrImagePull, ImagePullBackOff, InvalidImageName) or a container it cannot create (CreateContainerConfigError) before the task fails. These are mistakes in the task, so they are not retried. |
| `kube.timeout.startMinutes` | `KUBE_TIMEOUT_STARTMINUTES` | `15` | How long a task's pod may take to START - to leave Pending - apart from how long it may run. A pod still not running by then (unschedulable, held by a quota, never created) is reported as StartTimeout and the engine requeues the task. 0 turns the check off. |
| `kube.timeout.waitUntil` | `KUBE_TIMEOUT_WAITUNTIL` | `30` | **Note:** Seconds the dispatcher waits for a new run workspace claim to reach `Pending` or `Bound`. |
| `kube.timeout.watchGraceMinutes` | `KUBE_TIMEOUT_WATCHGRACEMINUTES` | `2` | Backstop grace on the executor watch, added to the task's own budget. The engine owns the deadline and reaps first, so this only releases the thread when the watch never fires. Raise it where image pulls are slow. |
| `kube.workspace.storage.accessMode` | `KUBE_WORKSPACE_STORAGE_ACCESSMODE` | `ReadWriteMany` | See `kube.workspace.storage.size`. |
| `kube.workspace.storage.class` | `KUBE_WORKSPACE_STORAGE_CLASS` | blank | See `kube.workspace.storage.size`. |
| `kube.workspace.storage.size` | `KUBE_WORKSPACE_STORAGE_SIZE` | `1Gi` | Workspace storage defaults, taken when a run leaves the field blank |
| `logging.level.io.boomerang.dispatcher` | `LOGGING_LEVEL_IO_BOOMERANG_DISPATCHER` | `INFO` | Log4j Logging (`io.boomerang.dispatcher` includes the SDK, `io.boomerang.dispatcher.sdk`) |
| `management.endpoints.web.base-path` | `MANAGEMENT_ENDPOINTS_WEB_BASE_PATH` | `/` |  |
| `proxy.enable` | `PROXY_ENABLE` | `false` | **Note:** `true` passes `PROXY_HOST`, `PROXY_PORT`, `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY`, and their lower-case forms, to every task container. |
| `proxy.host` | `PROXY_HOST` | blank |  |
| `proxy.ignore` | `PROXY_IGNORE` | blank |  |
| `proxy.port` | `PROXY_PORT` | blank |  |
| `server.port` | `SERVER_PORT` | `7702` |  |
| `service.name` | `SERVICE_NAME` | `dispatcher` |  |
| `spring.application.name` | `SPRING_APPLICATION_NAME` | `${service.name}-${flow.environment}` |  |
| `spring.mvc.async.request-timeout` | `SPRING_MVC_ASYNC_REQUEST_TIMEOUT` | `7200000` | How long the live log route may stream one running task's log: two hours, matching service-core. |
| `spring.threads.virtual.enabled` | `SPRING_THREADS_VIRTUAL_ENABLED` | `true` | Request handling on virtual threads: a live log stream parks its request thread for up to that long, and the SDK already runs its polls, tasks and heartbeat on virtual threads. |
| `tekton.results.maxBytes` | `TEKTON_RESULTS_MAXBYTES` | `4096` | The results limit handed to a Tekton task as `RESULTS_MAX_BYTES`: 4096 in Tekton's default termination-message mode; raise it to the cluster's max-result-size under sidecar-logs. |

## Next steps

- [Configuring](https://www.useboomerang.io/docs/flow/main/hosting/configuring): set up each image step by step.
- [Configuration reference](https://www.useboomerang.io/docs/flow/main/hosting/configuration-reference): the properties most deployments set, by area.
- [All settings](https://www.useboomerang.io/docs/flow/main/hosting/settings-reference): what an admin changes in **Admin › Settings**, without a restart.
