Configuration reference

All configuration is environment variables — no config file. Unknown ANTARES_STORE/ANTARES_BUS values are fatal at startup, never silently defaulted. This table is checked against the source by dev/check-env-docs.sh (CI fails when a variable exists in code but not here).

Core

VariableDefaultEffect
ANTARES_STOREmemoryStore mode: memory, file, postgres, timescale. Unknown value = fatal.
ANTARES_TEMPORALfollows ANTARES_STORETemporal driver: a store mode, or none — history off. Mix freely with ANTARES_STORE, e.g. file current state with timescale history; temporal reads answer OperationNotSupported (422, CIM 009 Table 6.3.2-1) and nothing is recorded. A backend different from the store builds a second store instance used only for history.
ANTARES_HTTP_PORT9090HTTP listen port.
ANTARES_ROLESallComma list of roles this process runs: api, matcher, notifier, temporal, registry — the role-split fleet shape.
ANTARES_BUSlocalChange-event bus: local (in-process, single node) or nats (JetStream, multi-pod). Unknown value = fatal.
ANTARES_HOST_ALIASantaresThis broker's name in federation Via chains (CIM 009 6.3.18) — loop detection identity. Two LB'd replicas of one logical broker share one alias.
ANTARES_PUBLIC_URLhttp://{host_alias}:{port}The URL peers can reach this broker at: forwarded subscription copies notify {ANTARES_PUBLIC_URL}/ex/v1/remote-notify (5.8.1.4). Set it whenever the default is not routable from peers.

Store backends

VariableDefaultEffect
ANTARES_DATA_DIR— (required for file)Directory for the redb file. Must be a mounted volume — data never lives inside the image.
ANTARES_DATABASE_URL— (required for postgres/timescale)PostgreSQL connection string; PostGIS required, TimescaleDB for timescale. Bounded startup retry while the DB boots.
ANTARES_REQUIRE_RLSunset1/true: refuse to start when the DB role bypasses Row-Level Security (defense-in-depth for shared-schema multi-tenancy).
ANTARES_PG_POOL20Connection-pool size for postgres/timescale. Unparsable value = fatal. Sessions carry lock_timeout 5 s. A request that waits the pool's 5 s acquire timeout without getting a connection is answered 503 with Retry-After — see the sizing formula in the operations runbook.
ANTARES_PG_STATEMENT_TIMEOUT_MS30000Per-session statement_timeout on every pooled connection: a query past it is cancelled and answered InternalError (500, "database statement timeout"; CIM 009 5.5.2 names database timeouts as InternalError). Migrations are exempt. Not a positive integer = fatal.
ANTARES_MIGRATEon0/false skips running migrations from this process, so serving replicas do not race the DDL — run them once from a job or init container instead.
ANTARES_ALLOW_SHARED_LOCALunset1 permits bus=local with a postgres/timescale store — safe ONLY for a strictly single-process deployment; two such processes double-fire notifications.
ANTARES_TEMPORAL_RECORDallHistory gate for the entity endpoints. all: every changed attribute instance is recorded. observed: only instances carrying observedAt enter history — a metadata-only write (no observedAt) updates current state and its modifiedAt but leaves no history, so timeproperty=modifiedAt/createdAt temporal queries return nothing for never-observed attributes. none: the entity endpoints record nothing; the temporal API still stores and serves what it is given directly (unlike ANTARES_TEMPORAL=none, which switches the temporal seam off). The ETSI temporal suites assume all, which is why it stays the default. Unknown value = fatal.
ANTARES_TEMPORAL_RETENTION_DAYSunset (keep forever)Temporal history retention; the sweep job prunes older attribute instances. Applies to the temporal half wherever it lives: a file store with postgres history still runs the job.
ANTARES_SWEEP_SECS900Cadence of the background GC sweep (expired entities/registrations, 4.22) — identical across store modes.

NATS scale-out

VariableDefaultEffect
ANTARES_NATS_URL— (required for bus=nats)NATS server URL; JetStream streams and the subscription KV bucket are asserted at startup.
ANTARES_NATS_REPLICAS1JetStream replica count for streams/KV (set 3 on a 3-node NATS cluster).
ANTARES_OUTBOX_DRAINonoff disables the notification outbox drainer in this process (crash-drill lever / dedicated-drainer split).

Federation & egress hardening

VariableDefaultEffect
ANTARES_EGRESS_ALLOW_PRIVATEtrueBroker-initiated HTTP and MQTT (notifications, forwards, @context fetches) may reach loopback, link-local and RFC 1918 destinations. Set false (or 0) on an internet-exposed deployment to deny those together with carrier-grade NAT (100.64.0.0/10), 0.0.0.0/8, the IETF assignment and benchmarking blocks and the reserved space above 240.0.0.0. The cloud instance-metadata endpoints are refused whatever this is set to, in every IPv6 spelling. The scheme allowlist, redirect cap, DNS pinning and response-size caps apply regardless. A refused delivery is booked as a failure (lastFailure, status: failed) and never retried.
ANTARES_FED_FANOUT8Concurrent forwards per distributed read (4.3.6.1 orders the merge, not the requests).
ANTARES_FED_INFLIGHT256Forwarded requests in flight for the whole process; callers over the cap wait. Bounds the buffers and connections open federated queries hold (6 000 open queries × 34 sources once reached 7.7 GB).
ANTARES_MAX_FED_RESPONSE_BYTES16777216 (16 MiB)Ceiling on one forwarded response body — one misbehaving peer cannot balloon broker memory. Over-cap parts fail as warning 111 (Table 6.3.17-1).
ANTARES_MAX_BATCH_ITEMS1000Batch entity-count cap (DoS bound; the spec sets none). Raise for trusted bulk producers.
ANTARES_MAX_BODY_BYTES4194304 (4 MiB)Request body cap, answered with a bare 413 (6.3.4). One number governs the extractor limit and the bounds wall.
ANTARES_POLICYallow-allThe policy engine every operation is asked about (ADR-0020). The shipped binary is built with allow-all alone, which decides nothing; an unknown name is fatal at startup and names the shelf the binary was built with, so a typo cannot quietly serve every request wide open. An engine that refuses is answered 403 with the ProblemDetails type urn:antares:error:AccessDenied and the engine's own reason — this broker's own URN, because Table 6.3.2-1 names no access-denied error and none is invented under the ETSI namespace. An engine that narrows instead conjoins its condition into the query the store runs and drops the members it named from every document served; a read narrowed that way answers Antares-Results-Restricted: true when the engine asked for the marker, and is otherwise silent. A single Entity outside the narrowing answers 404 like an absent one, so a caller cannot tell the two apart. The engine is also asked about each notification before it is sent: it may drop one — which is no delivery attempt, so timesSent and lastNotification do not move — or project the entities it carries.
ANTARES_POLICY_SUBJECT_HEADERSunset (no headers)Comma list of request headers copied into the subject the engine is given, matched case-insensitively. The broker never interprets them and never lets them leave: they are stripped from forwarded requests and absent from notifications, dead letters and logs. One copy is persisted: a Subscription, an EntityMap and a Snapshot each keep their creator's headers, because all three are used again after the request that made them — 5.8.6 delivery is broker-initiated, a snapshot's fill runs once its request has been answered, and an EntityMap presented by a different subject is treated as one that cannot be accessed (5.5.14), so a new one is built for that request. That copy is a broker-internal member — no representation renders it, no client can set it, and the 5.8.1.4 copy forwarded to a Context Source is stripped of it — but it does live in the store, so name a header that identifies the subscriber, not one that authenticates them.
ANTARES_POLICY_TIMEOUT_MS250How long the policy engine has to answer one request before the seam stops waiting and denies (ADR-0020). The engine the broker ships allows everything and never waits, so this matters only to a deployment that attached its own.
ANTARES_CORS_ORIGINSunset (no CORS headers)Browser origins allowed, comma-separated, or *. Preflights are answered for every method and header; Link, NGSILD-Tenant and NGSILD-Results-Count are exposed.
ANTARES_API_SURFACESadminComma list of HTTP surfaces mounted beside the NGSI-LD API root, each under its own reserved prefix (admin serves /q). An unknown name is fatal at startup and names the shelf the binary was built with; a selection that leaves out admin serves no /q at all, probes included.
ANTARES_EXTRA_CA_FILEunsetPEM bundle of ADDITIONAL trust anchors for egress TLS (private CAs). Egress TLS trusts the host's certificate store (/etc/ssl/certs on Linux; the shipped image carries one), and this widens it. Verification itself is never disableable.

Notification delivery

VariableDefaultEffect
ANTARES_NOTIFY_ATTEMPTS1Delivery attempts per notification, first one included. 1 is 5.8.6 as written: one send, the outcome booked. Higher values retry on their own task with exponential backoff; the retries never move timesSent again.
ANTARES_NOTIFY_BACKOFF_MS1000Delay before the first retry; doubles per retry (±20 % jitter, 60 s ceiling).
ANTARES_NOTIFY_MAX_AGE_SECS300No retry starts later than this after the first attempt. When the attempts or the age run out the notification becomes a dead letter (/q/dead-letters, see operations).
ANTARES_DELIVERY_WIDTH64Notifications in flight at once across the whole broker. A slot is held until the endpoint answers, so the number that fits depends on the subscribers: local sinks free a slot in milliseconds, a remote endpoint sitting at its 30 s timeout (Table 5.2.15-1) holds one for the whole timeout. Published as deliveryWidth in /q/health.
ANTARES_DELIVERY_WIDTH_PER_TENANT8Of that width, what one tenant may hold, capped at the width. Without it a single tenant with enough dead endpoints holds every slot and nothing leaves the broker for anyone else. Published as deliveryWidthPerTenant.

Lifecycle & observability

VariableDefaultEffect
ANTARES_HEADER_READ_TIMEOUT_MS10000A connection that has not finished its request HEAD within this window is closed (slow-loris bound).
ANTARES_MAX_CONNECTIONS10000Concurrent-connection ceiling; further accepts are dropped. Counts keep-alive and LB health-check connections too — size accordingly.
ANTARES_DISCOVERY_SCAN_MAX100000Documents one unpaginated whole-tenant fold may read or hold: the /types//attributes discovery folds (5.7.5-5.7.10 define no pagination) and the registration query (5.10.2.4 filters before it pages, so the whole match set is held). Past it the answer is 403 TooManyResults (5.5.6) instead of an unbounded scan. Published as maxFoldDocs in /q/health.
ANTARES_DRAIN_DELAY_MS2000Rolling update, step 2: keep serving this long after /q/health flips to 503 — the load balancer's notice window, sized so a health poll actually observes the 503 before the socket goes.
ANTARES_DRAIN_DEADLINE_SECS20Bound on waiting for in-flight connections during drain. Container stop_grace_period / terminationGracePeriodSeconds MUST exceed delay + deadline.
ANTARES_TELEMETRYoffAny value but an off spelling enables the metrics recorder and, with the endpoint, the OTLP span and log pipelines.
ANTARES_OTLP_ENDPOINTunsetOTLP/HTTP collector for traces and logs, e.g. http://collector:4318/v1/traces; log records go to the v1/logs twin of that URL with the same resource attributes. Unset costs nothing.

Compile-time bounds (no variable sets them; spec-shaped rejections): URI 8 KiB → 414, JSON depth 64 → 400. The body cap is ANTARES_MAX_BODY_BYTES above. Every bound in force is reported live by GET /q/health under limits.

Node-shim (wasm tier) extras: ANTARES_FILE (redb path per shim) — see the browser guide.