Skip to main content

Configuration

This page is a guided map of what the Enterprise binary lets you tune. For the OIDC and Temporal blocks, see the dedicated pages.

Profile and configuration file

The backend loads its YAML config:

VariablePurpose
CONFIG_FILEAbsolute path to a config file.

Precedence is environment variable → YAML value → built-in default. So any field documented below as having an environment-variable name can be overridden at runtime without touching the YAML file.

REST API

The HTTP server that serves both the REST API and (optionally) the metrics endpoint.

VariableDefaultPurpose
REST_API_PORT8080Public API listen port.
REST_API_CONTEXT/URL prefix. Set to e.g. /api/v1 when sitting behind a path-based reverse proxy.
REST_READ_TIMEOUT30sHTTP request read timeout.
REST_WRITE_TIMEOUT60sHTTP response write timeout. Streaming endpoints (NDJSON exports) are bounded by this.
REST_IDLE_TIMEOUT120sKeep-alive idle connection timeout.
REST_MAX_HEADER_BYTES1 MiBMax request header size.
REST_MAX_BODY_BYTES5 MiBMax request body size. Raise if you deploy large BPMN/DMN XML uploads.
REST_ALLOWED_CORS_ORIGINS-Comma-separated CORS origin allowlist. Set to your SPA origin(s) in production. Wildcard is not supported.
REST_ALLOWED_WS_ORIGINS-Comma-separated WebSocket origin allowlist (used by the FEEL editor's LSP).
REST_METRICS_PORT9464Prometheus /metrics endpoint port. Separate HTTP server from the API.
REST_METRICS_ADDRempty (all interfaces)Bind address for the metrics server. Set to 127.0.0.1 to keep telemetry off ingress, or firewall the port.

Database

Connection settings for PostgreSQL. Internal schema migrations run automatically on every startup — there is no separate migration step.

VariableDefaultPurpose
DB_HOSTlocalhostPostgreSQL hostname.
DB_PORT5432PostgreSQL port.
DB_USER-PostgreSQL user.
DB_PASSWORD-PostgreSQL password.
DB_NAME-Database name.
DB_SSLMODEdisabledisable, require, verify-ca, or verify-full.
DB_MAX_CONNS50pgxpool max connections. Raise for high BPMN concurrency.
DB_MIN_CONNS5pgxpool min connections kept warm.

OIDC

See Authentication for the full picture. The five tunable variables are OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_AUDIENCES, OIDC_ROLES_CLAIM, OIDC_PROJECTS_CLAIM.

Temporal

See Temporal setup. The Temporal block has a deep tail of connection, TLS, and tuning knobs — TEMPORAL_HOST_PORT, TEMPORAL_API_KEY[_FILE], the TEMPORAL_TLS_* family, the TEMPORAL_CONN_* family, and TEMPORAL_CAN_* for long-running workflows.

Caching

Two independent caches.

Access cache — caches the project-UUID → PostgreSQL schema-name lookup so request routing doesn't hit projects.schema_name on every call. Role and membership are read from the JWT every request and are not cached.

VariableDefaultPurpose
ACCESS_CACHE_MAX_SIZE100Max cache entries. Bump above your project count to eliminate cold-miss lookups.
ACCESS_CACHE_TTL30sCache entry time-to-live. Pure performance knob — see Operations → Access cache.

Compiled DMN cache — caches compiled decision tables so evaluation doesn't re-parse them on every call.

VariableDefaultPurpose
COMPILED_CACHE_MAX_SIZE100Max compiled tables held in memory. The cache is per-replica, expect cold misses after deploys or replica rotation.

FEEL evaluation budgets

Caps on what a single FEEL expression can do. They protect against pathological or hostile input, defaults are unreachable for normal expressions.

VariableDefaultPurpose
FEEL_MAX_DEPTH256Max recursion depth in evaluation. 0 disables the cap.
FEEL_MAX_ITERATIONS1000000Max combined iterations across all for / some / every loops in one evaluation. 0 disables the cap.
FEEL_DEFAULT_TIMEOUT5sWall-clock timeout. Enforced via context cancellation. 0 disables the default.

BPMN engine

The operator-facing subset of the BPMN configuration. A worker here means a Temporal worker (workflow + activity poller pair) running against one tenant's namespace.

VariableDefaultPurpose
BPMN_MAX_CALL_ACTIVITY_DEPTH64Max recursion depth for callActivity chains.
BPMN_DEFAULT_WORKERS1Default number of Temporal workers per project.
BPMN_TIMER_POLLER_INTERVAL30sHow often the engine checks for due timer events.
BPMN_INSTANCE_RECONCILER_INTERVAL30mHow often the reconciler scans for drift between PostgreSQL state and Temporal.

Per-tenant worker counts can be overridden in YAML:

bpmnWorkers:
defaultWorkers: 1
overrides:
"<project-uuid>": 4

History retention and archival

The platform splits BPMN workflow history across two stores:

  • PostgreSQL holds the index into workflows — one row per BPMN instance, plus operator-facing queues: user tasks, external jobs, incidents. DMN evaluations are fully stored here (inputs, outputs, executed-at).
  • Temporal holds the workflow detail — events, variables, decision history, current node state. The instance detail page loads this on demand, the PG row gates access to it.

Retention is configured independently on both sides:

  1. App-side — a cleanup job deletes DMN executions, the BPMN instance index, user tasks, external jobs, and incidents from PostgreSQL after appRetentionDays.
  2. Temporal-side — each project's Temporal namespace is registered with a workflow-execution retention period that controls when Temporal expires the workflow history itself. After expiry, history can be archived to object storage if the Temporal cluster is configured for archival.

YAML shape:

history:
appRetentionDays: 365
temporal:
retentionDays: 30
archival:
historyURI: ""
visibilityURI: ""
VariableDefaultPurpose
APP_HISTORY_RETENTION_DAYS365Days of DMN execution and BPMN history kept in PostgreSQL before the cleanup job prunes them. 0 or negative disables cleanup (rows kept forever).
TEMPORAL_RETENTION_DAYS30Days of completed workflow history kept on the Temporal cluster before retention expiry. 0 or negative leaves the field unset on namespace registration so the cluster's default retention applies.
TEMPORAL_ARCHIVAL_HISTORY_URIemptyPer-namespace history archival URI. Empty uses the cluster default.
TEMPORAL_ARCHIVAL_VISIBILITY_URIemptyPer-namespace visibility archival URI. Setting this flips visibility archival to ENABLED, leave empty to inherit cluster defaults.

Why two retentions

The two settings control different layers and have different failure modes when misaligned:

  • App retention shorter than Temporal retention → BPMN instances fall off list views, DMN execution history disappears, and operator queues empty out. Temporal still has the workflow histories, but they're orphaned — no PG row, no way for an operator to reach them through the UI.
  • Temporal retention shorter than app retention → list views and audit queries keep showing past instances, but clicking through to detail errors with "workflow history not found" because Temporal has already expired it. Archival mitigates this — the cluster's archiver moves expired histories to object storage where they remain queryable.

Practical Enterprise pairing: keep APP_HISTORY_RETENTION_DAYS long enough to satisfy operator-side audit needs (1–10 years), keep TEMPORAL_RETENTION_DAYS short to keep the visibility store small (30 days is a common Temporal recommendation), and configure cluster-side archival to S3 / GCS / filestore so expired Temporal histories remain accessible.

Archival prerequisites

The TEMPORAL_ARCHIVAL_* variables are per-namespace URI overrides passed to Temporal on namespace registration. They take effect only when the Temporal cluster itself has archival enabled — both the history and visibility archivers need a configured backend (filestore, S3, GCS). Empty values are the right default, set them only when a specific namespace needs a different archive location than the cluster default.

History archival is always requested as ENABLED on namespace creation. If the cluster has no archival configured, this is a no-op rather than an error.

Observability

VariableDefaultPurpose
ENABLE_OTELfalseMaster switch for the telemetry subsystem. Enables Prometheus metrics export on REST_METRICS_PORT. See Operations → Observability for what's actually exported.
OTLP_ENDPOINTemptygRPC address of an OTLP collector (host:port, no scheme — e.g. otel-collector:4317). Empty leaves tracing as a no-op: spans are created but dropped, and metrics still export. Requires ENABLE_OTEL=true.
OTLP_INSECUREtrueSend plaintext gRPC to the collector. Set false for TLS.
OTLP_SAMPLE_RATIO1.0Head sampling ratio, applied as ParentBased(TraceIDRatioBased). 1.0 exports every trace, 0.1 ≈ 10%.

Advanced tuning

  • BPMN worker concurrencyBPMN_MAX_CONCURRENT_ACTIVITY_TASK_POLLERS, BPMN_MAX_CONCURRENT_WORKFLOW_TASK_POLLERS, BPMN_MAX_CONCURRENT_ACTIVITY_EXECUTION_SIZE, BPMN_MAX_CONCURRENT_WORKFLOW_TASK_EXECUTION_SIZE. Defaults are computed from worker count, override if you're tuning a worker for unusual load shapes.
  • External job lease tuningBPMN_LEASE_DURATION, BPMN_LEASE_REFILL_PER_WAITER, BPMN_LEASE_REFILL_MAX_BATCH, BPMN_LEASE_POLL_WAKE_BUFFER, BPMN_LEASE_BATCH_HOLD, BPMN_LEASE_BATCH_SAFETY_MARGIN. These control how external jobs are leased to workers, defaults are tuned for typical workloads.
  • Temporal connection tuning — see Temporal.

If you find yourself wanting to tune these, talk to support@quantumbpm.com — we'd rather understand the workload and give you a recipe than have you guess.