Configuration Reference
raptor reads a TOML file (default raptor.toml, override with
serve --config <path>). Every key can be overridden by a RAPTOR_*
environment variable; nested tables use a __ separator (e.g.
RAPTOR_DDI__ANONYMOUS).
Top level
| Key | Type | Default | Description |
|---|---|---|---|
bind | socket addr | 0.0.0.0:8088 | address the HTTP server listens on |
database_url | string | (required) | sqlite://… or postgres://…; selects the backend |
artifact_dir | path | (required) | root of the content-addressed artifact store |
max_artifact_size | integer (bytes) | 1073741824 (1 GiB) | maximum artifact upload size |
url | string | (unset) | external base URL for _links; derived from the Host header when unset |
rollout_eval_interval_secs | integer | 5 | how often the background evaluator / auto-assign sweep runs |
rollout_approval_enabled | bool | false | gate new rollouts behind an operator approval (waiting_for_approval); reported as hawkBit’s rollout.approval.enabled |
tenant | string | DEFAULT | tenant name this instance answers to on the DDI /{tenant}/... path segment (matched case-insensitively); any other segment gets 404 |
[quota] — per-entity growth caps
Mirrors hawkBit’s quotas (hawkbit.server.security.dos.*), with its default
values. These bound unbounded growth — a chatty device appending action-status
rows forever, a runaway upload loop — rather than rate-limiting requests. A
breach is rejected with 429 Too Many Requests and hawkBit’s
hawkbit.server.error.quota.tooManyEntries error code.
Set a key to 0 to disable that quota. This matches hawkBit, which treats
any limit <= 0 as unlimited.
| Key | Default | Caps |
|---|---|---|
max_status_entries_per_action | 1000 | status entries a device may report against one action |
max_messages_per_action_status | 50 | messages a device may attach to one reported status |
max_attribute_entries_per_target | 100 | attributes a device may report about itself |
max_metadata_entries_per_target | 100 | metadata entries per target |
max_metadata_entries_per_software_module | 100 | metadata entries per software module |
max_metadata_entries_per_distribution_set | 100 | metadata entries per distribution set |
max_artifacts_per_software_module | 50 | artifacts per software module |
max_software_modules_per_distribution_set | 100 | modules per distribution set |
max_rollout_groups_per_rollout | 500 | deployment groups per rollout |
max_targets_per_rollout_group | 20000 | targets in any one rollout group |
The two device-reported caps apply only to what a device sends over DDI. raptor’s own status entries — “rollout stopped”, “superseded by a new assignment” — are never capped, so a device that has exhausted its quota cannot stop the server recording why its action was cancelled.
max_status_entries_per_action also exempts feedback that closes an action
(closed, canceled, a downloadonly action’s downloaded, and a cancel’s
closed/rejected). Without that carve-out a device that spent its budget on
progress reports could never file its terminal one, leaving the action active
forever with no way to close it. hawkBit does the same, checking the count only
for intermediate statuses.
Artifact size is capped separately by the top-level max_artifact_size.
[cleanup] — automatic action cleanup
Deletes closed actions past a retention window, along with their status
history. action_status is the one table that otherwise grows without bound:
quotas cap how much history any single action can accumulate, but nothing
caps how many actions a fleet accumulates over years of updates.
Off by default — deleting deployment history is not something to start doing to an existing installation unasked.
| Key | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | run the sweep at all |
action_expiry_days | integer | 30 | how long a closed action is kept, from when it was last modified |
action_statuses | list | ["finished", "error", "canceled"] | which statuses may be deleted |
interval_secs | integer | 3600 | how often the sweep runs |
[cleanup]
enabled = true
action_expiry_days = 90
An active action is never eligible, whatever action_statuses says — a
device can hold a listed status while its action is still live, and deleting it
would strand that device holding an action id the server no longer knows. Deletion is
batched — 1000 actions per statement, up to 10k per sweep — so a long-neglected
instance drains over successive sweeps rather than in one enormous statement.
(hawkBit bounds its own cleanup the same way, but at one batch per run.)
Rollout progress is unaffected. totalTargetsPerStatus is derived by counting
actions, so deleting a finished one would walk a completed rollout’s targets
back to scheduled — reporting as though the deployment had never run. raptor
records each group’s purged outcomes before the rows go and folds them back in.
(hawkBit has this drift and tolerates it; raptor does not.)
The two hawkBit tenant config keys are reported on
/rest/v1/system/configs: action.cleanup.auto.expiry (milliseconds, -1
when disabled) and action.cleanup.auto.status.
[ddi] — device-facing API
| Key | Type | Default | Description |
|---|---|---|---|
anonymous | bool | false | disable all DDI auth (dev only) |
gateway_token | string | (unset) | shared token; enables auto-registration |
polling_interval | string | 00:05:00 | poll sleep advertised to devices — see Polling time overrides below |
confirmation_flow | bool | false | require confirmation before a deployment starts |
auto_confirm_default | bool | false | give newly created targets autoConfirm, so confirmation_flow can’t strand confirmation-unaware clients |
artifact_http_url | string | (unset) | plain-HTTP base advertised in the DDI download-http links; unset means they reuse url |
trusted_proxy_header | string | (unset) | header to read the device address from behind a reverse proxy, e.g. x-forwarded-for; unset uses the socket peer |
Polling time overrides
polling_interval accepts hawkBit’s pollingTime grammar (hawkBit 0.10,
PR #2533): a default
interval, optionally followed by ordered <RSQL> -> <interval> override
rules, evaluated in written order — first match wins:
<default>[, <RSQL> -> <interval>]*
Each interval is HH:MM:SS, optionally with ~NN% jitter (NN 0–99):
raptor draws fresh randomness on every poll response, matching hawkBit
exactly, so a device’s advertised sleep can vary poll to poll rather than
settling on one value.
[ddi]
polling_interval = "00:05:00, group==eu -> 00:00:30~10%, updateStatus!=in_sync -> 00:02:00"
- The bare
HH:MM:SSform (no rules) is unchanged from before this feature and behaves identically. - Override filters use raptor’s own FIQL dialect — the same one every
q=parameter accepts — not hawkBit’s fuller Spring RSQL grammar. In particular, whitespace around the operator is not tolerated: writegroup==eu, not hawkBit’s own doc examplegroup == 'eu'. A rule that fails to parse is rejected at startup (see below), not silently ignored. - raptor does not support hawkBit’s multi-day (
d+:HH:mm:ss) or ISO-8601 (P2DT3H4M) interval forms — the incident-shaped use case this exists for (“poll this device faster while I’m watching it”) doesn’t need day-scale intervals. - A malformed
polling_interval— bad grammar, or a rule referencing a filter field raptor doesn’t recognize — failsraptor serveat startup with an error, rather than surfacing on a device’s poll. - Known limitation, inherited from hawkBit: the Management API’s
pollStatus.overdueis always computed from the default interval, even for a target currently matched by an override rule. hawkBit’s own release notes call this out as an accepted inaccuracy rather than a bug to fix.
[mgmt] — Management API / web console
| Key | Type | Default | Description |
|---|---|---|---|
username | string | (required) | admin username |
password_hash | string | (required) | argon2id hash from raptor hash-password |
Example
bind = "0.0.0.0:8088"
database_url = "postgres://raptor:raptor@localhost/raptor"
artifact_dir = "/var/lib/raptor/artifacts"
max_artifact_size = 2147483648 # 2 GiB
url = "https://raptor.example.com"
rollout_eval_interval_secs = 10
rollout_approval_enabled = false
[quota]
max_artifacts_per_software_module = 20
max_status_entries_per_action = 0 # unlimited
[ddi]
anonymous = false
gateway_token = "shared-registration-secret"
polling_interval = "00:05:00"
confirmation_flow = true
[mgmt]
username = "admin"
password_hash = "$argon2id$v=19$m=19456,t=2,p=1$..."
Environment overrides
RAPTOR_BIND=127.0.0.1:9090
RAPTOR_DATABASE_URL=sqlite://raptor.db?mode=rwc
RAPTOR_DDI__ANONYMOUS=true
RAPTOR_DDI__GATEWAY_TOKEN=super-secret
RAPTOR_MGMT__PASSWORD_HASH='$argon2id$...'
Environment values take precedence over the TOML file — the recommended way to inject secrets.