Skip to main content

Configuration Reference

This is the comprehensive reference for Fire Arrow Server's application.yaml configuration. Properties are organized by section, with descriptions, defaults, and examples.

spring.datasource - Database Connection​

Fire Arrow Server uses PostgreSQL as its primary data store. For quick local experimentation, H2 in-memory is also supported.

spring:
datasource:
url: jdbc:postgresql://localhost:5432/hapi
username: hapi
password: hapi
driverClassName: org.postgresql.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
jpa:
properties:
hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect
hibernate.search.enabled: false

H2 In-Memory (Development Only)​

spring:
datasource:
url: jdbc:h2:mem:hapi;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
driverClassName: org.h2.Driver
jpa:
properties:
hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
warning

H2 is suitable for quick experiments only. Data is lost when the server stops. Always use PostgreSQL for persistent deployments.

PropertyDescriptionDefault
urlJDBC connection URLjdbc:postgresql://localhost:5432/hapi
usernameDatabase usernamehapi
passwordDatabase passwordhapi
driverClassNameJDBC driver classorg.postgresql.Driver
hikari.maximum-pool-sizeMaximum connections in the pool20
hikari.minimum-idleMinimum idle connections maintained5

hapi.fhir - FHIR Server Behavior​

Controls the FHIR server's capabilities, supported resources, and API features.

hapi:
fhir:
fhir_version: R4
graphql_enabled: true
openapi_enabled: true
default_page_size: 20
max_page_size: 200
search_index_full_text_enabled: false
cors:
enabled: true
allowed_origins:
- "*"
subscription:
resthook_enabled: false
message_enabled: false
websocket_enabled: false
email:
enabled: false
from: "noreply@example.com"
PropertyDescriptionDefault
fhir_versionFHIR specification versionR4
graphql_enabledEnable the GraphQL API endpointtrue
openapi_enabledEnable Swagger UI and OpenAPI spectrue
default_page_sizeDefault number of results per page20
max_page_sizeMaximum results per page200
search_index_full_text_enabledEnable full-text search (_text, _content parameters)false
cors.enabledEnable CORS headerstrue
cors.allowed_originsAllowed CORS origins["*"]
subscription.resthook_enabledEnable REST hook subscription channelfalse
subscription.message_enabledEnable message subscription channel (used by Azure Queue delivery and the CarePlan nop channel)false
subscription.websocket_enabledEnable WebSocket subscription channelfalse
subscription.email.enabledEnable email subscription channelfalse
subscription.email.fromSender address for email subscriptions--

fire-arrow.authentication - Authentication Providers​

Configure OAuth 2.0 / OIDC providers and API token support. See Authentication for a detailed guide.

fire-arrow:
authentication:
enabled: true
providers:
- name: my_oidc_provider
type: oidc
enabled: true
oidc-uri: "https://login.example.com"
audience: "api://fire-arrow"
api-scope: "fhir.access"
claim-mapping:
scope-claim: "scp"
roles-claim: "roles"
subject-claim: "sub"
email-claim: "email"
name-claim: "name"
fhir-resource-type-claim: "resource_type"
fhir-id-claim: "fhir_id"
identity-resolution:
identifier-system: "https://your-app.example.com/fhir/user-id"
auto-create-enabled: true
api-tokens:
hmac-secret: "${API_TOKEN_SECRET}"
PropertyDescriptionDefault
enabledEnable authentication globallyfalse
providers[].nameUnique provider name--
providers[].typeProvider type (oidc, azure-identity)--
providers[].enabledEnable this providertrue
providers[].oidc-uriOIDC issuer URI--
providers[].audienceExpected token audience--
providers[].api-scopeRequired API scope--
providers[].claim-mapping.*Maps token claims to identity fields--
providers[].identity-resolution.identifier-systemFHIR identifier system for user lookup--
providers[].identity-resolution.auto-create-enabledCreate FHIR resources for new usersfalse
api-tokens.hmac-secretSecret key for API token generation/validation--

fire-arrow.authorization - Access Control​

Configure role-based access control rules. See Authorization for a detailed guide.

fire-arrow:
authorization:
default-validator: Forbidden
debug: false
cache:
enabled: true
ttl-seconds: 300
validation-rules:
- client-role: "Patient"
resource: "Patient"
operation: "read"
validator: "PatientCompartment"
- client-role: "Patient"
resource: "Patient"
operation: "graphql-read"
validator: "PatientCompartment"
- client-role: "Practitioner"
resource: "Patient"
operation: "search"
validator: "LegitimateInterest"
PropertyDescriptionDefault
default-validatorValidator applied when no rule matchesForbidden
debugEnable authorization debug mode (logs detailed rule evaluation)false
cache.enabledCache authorization decisionstrue
cache.ttl-secondsCache entry time-to-live in seconds300
validation-rules[]List of authorization rules[]
validation-rules[].client-roleRole this rule applies to (Patient, Practitioner, RelatedPerson, Device, Public)--
validation-rules[].resourceFHIR resource type this rule applies to--
validation-rules[].operationOperation (read, search, create, write, delete, graphql-read, graphql-search, me, binary-upload, subscribe, generate-durable-token, generate-one-time-token)--
validation-rules[].validatorValidator to use (PatientCompartment, LegitimateInterest, CareTeam, Forbidden, Permit)--
Authorization Debug Mode

Set debug: true during development to see detailed logs of how each request is evaluated against your authorization rules. Disable it in production - debug mode logs sensitive information.

fire-arrow.binary-storage - Azure Blob Storage​

Configure binary file storage. See Binary Storage for the full guide.

fire-arrow:
binary-storage:
enabled: false
azure:
connection-string: "${AZURE_STORAGE_CONNECTION_STRING}"
container-name: "fhir-binaries"
max-file-size: 10485760
pre-signed-url-expiration-seconds: 120
PropertyDescriptionDefault
enabledEnable binary storagefalse
azure.connection-stringAzure Blob Storage connection string--
azure.endpointAzure Blob endpoint (for managed identity)--
azure.container-nameBlob container name--
azure.max-file-sizeMaximum upload size in bytes10485760 (10 MB)
azure.pre-signed-url-expiration-secondsPre-signed URL lifetime120

fire-arrow.careplan-events - CarePlan Scheduling​

Configure server-side CarePlan activity materialization. See CarePlan Events for the full guide, including Task Expiration and Validity.

fire-arrow:
careplan-events:
enabled: true
horizon-duration: "P30D"
subscriptions:
max-ttl: "P30D"
require-end: true
excess-end-action: reject
delivery:
max-consecutive-failures: 5
disable-after-no-success: "P7D"
cleanup:
sweep-interval: "PT1H"
delete-disabled-after: "P30D"
due-task-check-interval: "PT1M"
expired-task-check-interval: "PT1M"
task:
initial-status: requested
due-status: ready
auto-transition-to-ready: true
auto-transition-to-terminal: true
auto-transition-terminal-state: cancelled
default-validity-period: "PT4H"
task-validity-periods:
PT1H: "PT15M"
P1D: "PT1H"
P1W: "P1D"
careplan-validity-extension:
enabled: false
PropertyDescriptionDefault
enabledEnable CarePlan event processingtrue
horizon-durationHow far ahead to materialize Tasks (ISO 8601 duration)P30D
subscriptions.max-ttlMaximum allowed subscription lifetimeP30D
subscriptions.require-endWhether Subscription.end is mandatorytrue
subscriptions.excess-end-actionAction when end exceeds max-ttl (reject or truncate)reject
delivery.max-consecutive-failuresConsecutive failures before disabling a subscription5
delivery.disable-after-no-successDuration without a successful delivery before disablingP7D
cleanup.sweep-intervalHow often the subscription cleanup job runsPT1H
cleanup.delete-disabled-afterWhen to delete disabled subscriptions (P0D = keep forever)P30D
cleanup.due-task-check-intervalHow often the due-task sweeper runsPT1M
cleanup.expired-task-check-intervalHow often the expired-task sweeper runsPT1M
task.initial-statusStatus of newly materialized Tasksrequested
task.due-statusStatus Tasks transition to when dueready
task.auto-transition-to-readyAutomatically move Tasks to due-status when duetrue
task.auto-transition-to-terminalAutomatically transition expired Tasks to terminal status (1.13.0+)true
task.auto-transition-terminal-stateTerminal status for expired Tasks (cancelled, failed, entered-in-error) (1.13.0+)cancelled
task.default-validity-periodHow long after its due time a Task remains actionable (1.13.0+)PT4H
task.task-validity-periodsMap of scheduling interval → validity period (ceiling lookup) (1.13.0+)PT1H: PT15M, P1D: PT1H, P1W: P1D
task.careplan-validity-extension.enabledHonor per-CarePlan validity overrides (1.13.0+)false
$subscribe-due-events also needs a subscription channel

careplan-events.enabled: true on its own does not activate subscription-based materialization. $subscribe-due-events relies on HAPI's subscription pipeline, which turns on only when at least one channel flag (hapi.fhir.subscription.resthook_enabled, message_enabled, websocket_enabled, or email.enabled) is true. There is no separate subscription.enabled flag. The enabled channel must match the subscription's channel: rest-hook needs resthook_enabled, and the polling-only nop channel needs message_enabled. With no matching channel enabled, the Subscription is created but stays in requested status and no Tasks are materialized. $materialize (1.11+) schedules Tasks without a channel. See CarePlan Events.

fire-arrow.subscription.azure-queue - Azure Storage Queue​

Configure the Azure Storage Queue subscription delivery channel. See Subscriptions for details, including per-Subscription queue routing.

fire-arrow:
subscription:
azure-queue:
enabled: false
connection-string: "${AZURE_STORAGE_CONNECTION_STRING}"
queue-name: "fhir-subscription-events"
endpoint-prefix: "channel:fire-arrow-azure-queue"
auto-create-queue: true
PropertyDescriptionDefault
enabledEnable Azure Storage Queue deliveryfalse
connection-stringAzure Storage account connection string--
service-urlQueue service URL when authenticating via managed identity--
credential-modedefault or managed-identity (used with service-url)default
queue-nameDefault queue used when a Subscription doesn't override itfhir-subscription-events
endpoint-prefixWell-known prefix that opts a Subscription into per-queue routing via channel.endpoint = <prefix>:<queue-name>channel:fire-arrow-azure-queue
allowed-override-queuesAllow-list of queue names that may be selected via the endpoint suffix. Empty means no overrides accepted (deny-by-default).[]
auto-create-queueAuto-create the default queue at boottrue
auto-create-override-queuesAuto-create suffix-derived override queues on first delivery (off by default; provision per-tenant queues out-of-band instead)false
override-queue-cache-max-sizeMaximum number of distinct override QueueClients kept cached in memory (bounded LRU)256
include-full-resourceInclude the full FHIR resource payload in each messagetrue
payload-formatPayload serialisation format (JSON or XML)JSON
channel-typeSubscription channel types to intercept (message, rest-hook, all)message

fire-arrow.license - License Configuration​

Configure the Fire Arrow Server license. See Licensing for the full guide.

fire-arrow:
license:
source: file
path: "/etc/fire-arrow/license.key"
deployment-id: "my-deployment-prod"
runtime-environment: prod
PropertyDescriptionValues
sourceLicense delivery methodfile, inline
pathPath to license fileFile path (when source: file)
contentInline license stringLicense content (when source: inline)
deployment-idDeployment identifierMust match the issued license
runtime-environmentDeployment tierprod, nonprod

fire-arrow.admin - Admin Web UI​

Configure the browser-based administration interface.

fire-arrow:
admin:
enabled: true
config-viewer:
enabled: true
auth-profiles:
- name: "Development"
token: "${ADMIN_DEV_TOKEN}"
PropertyDescriptionDefault
enabledEnable the admin web UI at /admin/true
config-viewer.enabledShow server configuration in the admin UItrue
auth-profiles[]Pre-configured auth profiles for the admin GraphQL explorer[]
auth-profiles[].nameDisplay name for the profile--
auth-profiles[].tokenBearer token to use with this profile--

Auth profiles let you switch between different user identities in the admin GraphQL explorer without manually copying tokens. This is useful during development when you want to test queries as different roles.

Web UI Login (web-ui-auth)​

Interactive sign-in to the web UI is configured separately under fire-arrow.admin.web-ui-auth. Each login profile is a confidential OAuth client and must have a client-secret — the server signs users in through a server-side Backend-for-Frontend and never exposes tokens to the browser.

fire-arrow:
admin:
web-ui-auth:
profiles:
- profile: patient-login
name: Patient
authentication-system: azure-b2c
client-id: "${OIDC_PATIENT_CLIENT_ID}"
client-secret: "${OIDC_PATIENT_CLIENT_SECRET}"
scopes: "openid profile"

See Web UI Sign-In for the full property reference, the identity provider redirect URI, Content Security Policy options, and upgrade guidance from the pre-1.14.0 browser-based login.

fire-arrow.mutex - Distributed Locking​

Controls how Fire Arrow Server serializes concurrent operations on the same resource (for example identity-resource protection and CarePlan materialization).

fire-arrow:
mutex:
provider: jdbc
PropertyDescriptionValues
providerLock implementationjdbc (default in the bundled configuration; shared across instances through the database), local (single instance only)
jdbc.lock-ttl-secondsAge after which a lock left behind by a crashed instance may be reclaimedDefault 300

Keep jdbc when running more than one instance.

fire-arrow.cluster - Multi-Node Coordination​

Controls cluster mode for deployments where several instances share one PostgreSQL database. The procedure for turning a cluster on, checking it, and responding when it is unhealthy is in Cluster.

fire-arrow:
cluster:
enabled: auto
quiesce-timeout: PT30S
read-fence-timeout: PT1S

Durations use ISO-8601 (PT30S, PT1S, PT15M). Set enabled: true on every instance that runs beside another replica.

PropertyDescriptionDefault
enabledauto turns cluster mode on for PostgreSQL when the startup check passes, and otherwise logs an error and runs that instance alone. true stops startup if the check fails. false never starts cluster mode.auto
quiesce-timeoutHow long a shutting-down instance waits for in-flight subscription processing before it releases leadershipPT30S
quiesce-entry-ttlIn-flight subscription work older than this is no longer counted during that wait. Keep it above 60 seconds.PT2M
read-fence-timeoutHow long a request with Cache-Control: no-cache or no-store waits for this instance to apply authorization changes committed on other instances. On timeout, that request is authorized without the caches.PT1S
heartbeatHow often the leader renews its lease. The lease is two heartbeats; another instance can take over after one missed lease.PT5S
backlog-rescan-intervalHow often the leader rescans the CarePlan reconcile backlogPT15M
watchdog-intervalHow often an instance checks that it can still exchange cluster eventsPT10S
watchdog-timeoutHow long that check may take. Two failures in a row reconnect the event session; authorization caches are not used until it is ready again.PT15S
barrier-timeoutHow long the leader waits for other instances to apply a Subscription, SubscriptionTopic, or SearchParameter change before it continuesPT10S
reconnect-backoffDelay between attempts to reopen a cluster database sessionPT2S
poll-timeoutHow long the event session waits for the next notification before looping. Leave at the default.PT0.25S
keepalive.idle-secondsIdle time before PostgreSQL starts TCP keepalive probes on the cluster's dedicated sessions10
keepalive.interval-secondsTime between those probes5
keepalive.countProbes before the dead session is dropped. A partitioned leader is released after about idle + interval × count seconds.3

Environment Variable Override Pattern​

Every YAML property can be overridden with an environment variable. The conversion rules:

  1. Remove the YAML hierarchy and join with underscores
  2. Convert to uppercase
  3. Replace dots and hyphens with underscores

Examples:

YAML PathEnvironment Variable
spring.datasource.urlSPRING_DATASOURCE_URL
spring.datasource.passwordSPRING_DATASOURCE_PASSWORD
hapi.fhir.graphql_enabledHAPI_FHIR_GRAPHQL_ENABLED
fire-arrow.authentication.enabledFIRE_ARROW_AUTHENTICATION_ENABLED
fire-arrow.binary-storage.azure.connection-stringFIRE_ARROW_BINARY_STORAGE_AZURE_CONNECTION_STRING
fire-arrow.careplan-events.scheduling.horizon-durationFIRE_ARROW_CAREPLAN_EVENTS_SCHEDULING_HORIZON_DURATION
fire-arrow.license.sourceFIRE_ARROW_LICENSE_SOURCE
fire-arrow.mutex.providerFIRE_ARROW_MUTEX_PROVIDER
fire-arrow.cluster.enabledFIRE_ARROW_CLUSTER_ENABLED
fire-arrow.cluster.quiesce-timeoutFIRE_ARROW_CLUSTER_QUIESCE_TIMEOUT
fire-arrow.cluster.read-fence-timeoutFIRE_ARROW_CLUSTER_READ_FENCE_TIMEOUT
fire-arrow.cluster.heartbeatFIRE_ARROW_CLUSTER_HEARTBEAT
fire-arrow.cluster.backlog-rescan-intervalFIRE_ARROW_CLUSTER_BACKLOG_RESCAN_INTERVAL
fire-arrow.cluster.keepalive.idle-secondsFIRE_ARROW_CLUSTER_KEEPALIVE_IDLE_SECONDS

Complete Example​

Here's a production-ready application.yaml with all major features enabled:

spring:
datasource:
url: jdbc:postgresql://db:5432/hapi
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driverClassName: org.postgresql.Driver
hikari:
maximum-pool-size: 20
jpa:
properties:
hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect

hapi:
fhir:
fhir_version: R4
graphql_enabled: true
openapi_enabled: true
default_page_size: 20
max_page_size: 200
cors:
enabled: true
allowed_origins:
- "https://your-app.example.com"
subscription:
resthook_enabled: true
websocket_enabled: true

fire-arrow:
authentication:
enabled: true
providers:
- name: primary_oidc
type: oidc
enabled: true
oidc-uri: "https://login.example.com"
audience: "api://fire-arrow"
api-scope: "fhir.access"
claim-mapping:
scope-claim: "scp"
roles-claim: "roles"
subject-claim: "sub"
email-claim: "email"
name-claim: "name"
fhir-resource-type-claim: "resource_type"
fhir-id-claim: "fhir_id"
identity-resolution:
identifier-system: "https://your-app.example.com/fhir/user-id"
auto-create-enabled: true
api-tokens:
hmac-secret: ${API_TOKEN_SECRET}

authorization:
default-validator: Forbidden
debug: false
cache:
enabled: true
ttl-seconds: 300
validation-rules:
- client-role: "Patient"
resource: "Patient"
operation: "read"
validator: "PatientCompartment"
- client-role: "Patient"
resource: "Patient"
operation: "graphql-read"
validator: "PatientCompartment"
- client-role: "Patient"
resource: "Observation"
operation: "graphql-search"
validator: "PatientCompartment"
- client-role: "Practitioner"
resource: "Patient"
operation: "search"
validator: "LegitimateInterest"

binary-storage:
enabled: true
azure:
connection-string: ${AZURE_STORAGE_CONNECTION_STRING}
container-name: "fhir-binaries"
max-file-size: 10485760

careplan-events:
enabled: true
scheduling:
horizon-duration: "P30D"
check-interval: "PT1H"
subscriptions:
max-ttl: "P90D"
delivery:
max-retries: 3
failure-threshold: 5

subscription:
azure-queue:
enabled: true
connection-string: ${AZURE_STORAGE_CONNECTION_STRING}
queue-name: "fhir-events"

license:
source: inline
content: ${FIRE_ARROW_LICENSE_CONTENT}
deployment-id: "production-01"
runtime-environment: prod

admin:
enabled: true

mutex:
provider: jdbc

cluster:
enabled: true