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.
PostgreSQL (Recommended)
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
H2 is suitable for quick experiments only. Data is lost when the server stops. Always use PostgreSQL for persistent deployments.
| Property | Description | Default |
|---|---|---|
url | JDBC connection URL | jdbc:postgresql://localhost:5432/hapi |
username | Database username | hapi |
password | Database password | hapi |
driverClassName | JDBC driver class | org.postgresql.Driver |
hikari.maximum-pool-size | Maximum connections in the pool | 20 |
hikari.minimum-idle | Minimum idle connections maintained | 5 |
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"
| Property | Description | Default |
|---|---|---|
fhir_version | FHIR specification version | R4 |
graphql_enabled | Enable the GraphQL API endpoint | true |
openapi_enabled | Enable Swagger UI and OpenAPI spec | true |
default_page_size | Default number of results per page | 20 |
max_page_size | Maximum results per page | 200 |
search_index_full_text_enabled | Enable full-text search (_text, _content parameters) | false |
cors.enabled | Enable CORS headers | true |
cors.allowed_origins | Allowed CORS origins | ["*"] |
subscription.resthook_enabled | Enable REST hook subscription channel | false |
subscription.message_enabled | Enable message subscription channel (used by Azure Queue delivery and the CarePlan nop channel) | false |
subscription.websocket_enabled | Enable WebSocket subscription channel | false |
subscription.email.enabled | Enable email subscription channel | false |
subscription.email.from | Sender 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}"
| Property | Description | Default |
|---|---|---|
enabled | Enable authentication globally | false |
providers[].name | Unique provider name | -- |
providers[].type | Provider type (oidc, azure-identity) | -- |
providers[].enabled | Enable this provider | true |
providers[].oidc-uri | OIDC issuer URI | -- |
providers[].audience | Expected token audience | -- |
providers[].api-scope | Required API scope | -- |
providers[].claim-mapping.* | Maps token claims to identity fields | -- |
providers[].identity-resolution.identifier-system | FHIR identifier system for user lookup | -- |
providers[].identity-resolution.auto-create-enabled | Create FHIR resources for new users | false |
api-tokens.hmac-secret | Secret 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"
| Property | Description | Default |
|---|---|---|
default-validator | Validator applied when no rule matches | Forbidden |
debug | Enable authorization debug mode (logs detailed rule evaluation) | false |
cache.enabled | Cache authorization decisions | true |
cache.ttl-seconds | Cache entry time-to-live in seconds | 300 |
validation-rules[] | List of authorization rules | [] |
validation-rules[].client-role | Role this rule applies to (Patient, Practitioner, RelatedPerson, Device, Public) | -- |
validation-rules[].resource | FHIR resource type this rule applies to | -- |
validation-rules[].operation | Operation (read, search, create, write, delete, graphql-read, graphql-search, me, binary-upload, subscribe, generate-durable-token, generate-one-time-token) | -- |
validation-rules[].validator | Validator to use (PatientCompartment, LegitimateInterest, CareTeam, Forbidden, Permit) | -- |
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
| Property | Description | Default |
|---|---|---|
enabled | Enable binary storage | false |
azure.connection-string | Azure Blob Storage connection string | -- |
azure.endpoint | Azure Blob endpoint (for managed identity) | -- |
azure.container-name | Blob container name | -- |
azure.max-file-size | Maximum upload size in bytes | 10485760 (10 MB) |
azure.pre-signed-url-expiration-seconds | Pre-signed URL lifetime | 120 |
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
| Property | Description | Default |
|---|---|---|
enabled | Enable CarePlan event processing | true |
horizon-duration | How far ahead to materialize Tasks (ISO 8601 duration) | P30D |
subscriptions.max-ttl | Maximum allowed subscription lifetime | P30D |
subscriptions.require-end | Whether Subscription.end is mandatory | true |
subscriptions.excess-end-action | Action when end exceeds max-ttl (reject or truncate) | reject |
delivery.max-consecutive-failures | Consecutive failures before disabling a subscription | 5 |
delivery.disable-after-no-success | Duration without a successful delivery before disabling | P7D |
cleanup.sweep-interval | How often the subscription cleanup job runs | PT1H |
cleanup.delete-disabled-after | When to delete disabled subscriptions (P0D = keep forever) | P30D |
cleanup.due-task-check-interval | How often the due-task sweeper runs | PT1M |
cleanup.expired-task-check-interval | How often the expired-task sweeper runs | PT1M |
task.initial-status | Status of newly materialized Tasks | requested |
task.due-status | Status Tasks transition to when due | ready |
task.auto-transition-to-ready | Automatically move Tasks to due-status when due | true |
task.auto-transition-to-terminal | Automatically transition expired Tasks to terminal status (1.13.0+) | true |
task.auto-transition-terminal-state | Terminal status for expired Tasks (cancelled, failed, entered-in-error) (1.13.0+) | cancelled |
task.default-validity-period | How long after its due time a Task remains actionable (1.13.0+) | PT4H |
task.task-validity-periods | Map of scheduling interval → validity period (ceiling lookup) (1.13.0+) | PT1H: PT15M, P1D: PT1H, P1W: P1D |
task.careplan-validity-extension.enabled | Honor per-CarePlan validity overrides (1.13.0+) | false |
$subscribe-due-events also needs a subscription channelcareplan-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
| Property | Description | Default |
|---|---|---|
enabled | Enable Azure Storage Queue delivery | false |
connection-string | Azure Storage account connection string | -- |
service-url | Queue service URL when authenticating via managed identity | -- |
credential-mode | default or managed-identity (used with service-url) | default |
queue-name | Default queue used when a Subscription doesn't override it | fhir-subscription-events |
endpoint-prefix | Well-known prefix that opts a Subscription into per-queue routing via channel.endpoint = <prefix>:<queue-name> | channel:fire-arrow-azure-queue |
allowed-override-queues | Allow-list of queue names that may be selected via the endpoint suffix. Empty means no overrides accepted (deny-by-default). | [] |
auto-create-queue | Auto-create the default queue at boot | true |
auto-create-override-queues | Auto-create suffix-derived override queues on first delivery (off by default; provision per-tenant queues out-of-band instead) | false |
override-queue-cache-max-size | Maximum number of distinct override QueueClients kept cached in memory (bounded LRU) | 256 |
include-full-resource | Include the full FHIR resource payload in each message | true |
payload-format | Payload serialisation format (JSON or XML) | JSON |
channel-type | Subscription 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
| Property | Description | Values |
|---|---|---|
source | License delivery method | file, inline |
path | Path to license file | File path (when source: file) |
content | Inline license string | License content (when source: inline) |
deployment-id | Deployment identifier | Must match the issued license |
runtime-environment | Deployment tier | prod, 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}"
| Property | Description | Default |
|---|---|---|
enabled | Enable the admin web UI at /admin/ | true |
config-viewer.enabled | Show server configuration in the admin UI | true |
auth-profiles[] | Pre-configured auth profiles for the admin GraphQL explorer | [] |
auth-profiles[].name | Display name for the profile | -- |
auth-profiles[].token | Bearer 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
| Property | Description | Values |
|---|---|---|
provider | Lock implementation | jdbc (default in the bundled configuration; shared across instances through the database), local (single instance only) |
jdbc.lock-ttl-seconds | Age after which a lock left behind by a crashed instance may be reclaimed | Default 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.
| Property | Description | Default |
|---|---|---|
enabled | auto 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-timeout | How long a shutting-down instance waits for in-flight subscription processing before it releases leadership | PT30S |
quiesce-entry-ttl | In-flight subscription work older than this is no longer counted during that wait. Keep it above 60 seconds. | PT2M |
read-fence-timeout | How 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 |
heartbeat | How often the leader renews its lease. The lease is two heartbeats; another instance can take over after one missed lease. | PT5S |
backlog-rescan-interval | How often the leader rescans the CarePlan reconcile backlog | PT15M |
watchdog-interval | How often an instance checks that it can still exchange cluster events | PT10S |
watchdog-timeout | How 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-timeout | How long the leader waits for other instances to apply a Subscription, SubscriptionTopic, or SearchParameter change before it continues | PT10S |
reconnect-backoff | Delay between attempts to reopen a cluster database session | PT2S |
poll-timeout | How long the event session waits for the next notification before looping. Leave at the default. | PT0.25S |
keepalive.idle-seconds | Idle time before PostgreSQL starts TCP keepalive probes on the cluster's dedicated sessions | 10 |
keepalive.interval-seconds | Time between those probes | 5 |
keepalive.count | Probes 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:
- Remove the YAML hierarchy and join with underscores
- Convert to uppercase
- Replace dots and hyphens with underscores
Examples:
| YAML Path | Environment Variable |
|---|---|
spring.datasource.url | SPRING_DATASOURCE_URL |
spring.datasource.password | SPRING_DATASOURCE_PASSWORD |
hapi.fhir.graphql_enabled | HAPI_FHIR_GRAPHQL_ENABLED |
fire-arrow.authentication.enabled | FIRE_ARROW_AUTHENTICATION_ENABLED |
fire-arrow.binary-storage.azure.connection-string | FIRE_ARROW_BINARY_STORAGE_AZURE_CONNECTION_STRING |
fire-arrow.careplan-events.scheduling.horizon-duration | FIRE_ARROW_CAREPLAN_EVENTS_SCHEDULING_HORIZON_DURATION |
fire-arrow.license.source | FIRE_ARROW_LICENSE_SOURCE |
fire-arrow.mutex.provider | FIRE_ARROW_MUTEX_PROVIDER |
fire-arrow.cluster.enabled | FIRE_ARROW_CLUSTER_ENABLED |
fire-arrow.cluster.quiesce-timeout | FIRE_ARROW_CLUSTER_QUIESCE_TIMEOUT |
fire-arrow.cluster.read-fence-timeout | FIRE_ARROW_CLUSTER_READ_FENCE_TIMEOUT |
fire-arrow.cluster.heartbeat | FIRE_ARROW_CLUSTER_HEARTBEAT |
fire-arrow.cluster.backlog-rescan-interval | FIRE_ARROW_CLUSTER_BACKLOG_RESCAN_INTERVAL |
fire-arrow.cluster.keepalive.idle-seconds | FIRE_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