Skip to content

Configuration

Ontul reads its configuration from conf/ontul.properties. Every property can also be overridden at runtime, and the same keys apply to both the Master and the Worker (each node simply ignores the sections that do not concern it).

Configuration Precedence

A value is resolved in the following order, highest priority first:

  1. System Property — passed with -D, e.g. -Dontul.master.admin.port=8081
  2. Environment Variable — see the naming convention below
  3. Properties file — conf/ontul.properties

If a key is set in more than one place, the higher-priority source wins.

Environment Variable Naming

To set any property through an environment variable, take the property key, replace dots with underscores, and uppercase it:

Property Environment Variable
ontul.master.admin.port ONTUL_MASTER_ADMIN_PORT
ontul.zk.serverList ONTUL_ZK_SERVERLIST
ontul.base.data.dir ONTUL_BASE_DATA_DIR

Many path properties default to a subdirectory of ${ontul.base.data.dir} (for example kms/, iam/, metadata/, exchange/, job-history/). Changing the base data directory moves all of them at once unless they are overridden individually.

Persist the data directory

In containers ontul.base.data.dir MUST resolve to a mounted volume (e.g. /app/data). Otherwise all cluster state — KMS keys, IAM, metadata — is lost on restart.

Base

Property Default Description
ontul.base.data.dir ./data Root directory for all node-local persistent state. Most other path properties default to a subdirectory of this via ${ontul.base.data.dir}. Relative paths resolve against the process working directory.

ZooKeeper

Property Default Description
ontul.zk.serverList localhost:2181 Comma-separated ZooKeeper connect string (host:port[,host:port...]) used for cluster coordination: master leader election, live worker registry, and shared config watches. All masters and workers must point at the same ensemble.
ontul.zk.rootPath /ontul Base znode namespace under which Ontul creates all its coordination nodes. Change this to run multiple independent clusters on one ZK ensemble.
ontul.zk.sessionTimeoutMs 30000 ZooKeeper session timeout (ms). If a node cannot heartbeat within this window the session expires, the leader loses leadership, and workers are deregistered. Larger values tolerate GC/network blips but slow failover.
ontul.zk.connectionTimeoutMs 10000 Maximum time (ms) to wait for the initial TCP connection to ZooKeeper before giving up on a connect attempt. Affects startup and reconnect latency.
ontul.zk.admin.probeTimeoutMs 2000 Admin UI topology: socket timeout (ms) for asking each ZooKeeper server its status with the srvr command. A server that does not answer in time is shown Offline.
ontul.zk.admin.probeCacheTtlMs 5000 How long (ms) one probe of the ensemble is reused for topology requests, so open admin pages do not add load to ZooKeeper. 0 probes on every request.

Master

Property Default Description
ontul.master.host 0.0.0.0 Interface the Master binds to. 0.0.0.0 binds all interfaces. Also advertised to clients as the JDBC/Flight SQL host (unless ontul.flight.sql.advertised.host overrides it), so behind NAT or a proxy a routable address may be required.
ontul.master.admin.port 8080 TCP port for the Master's Admin HTTP server (REST API + Admin UI) — the main management/control-plane endpoint.
ontul.master.internal.port 19999 TCP port for the Master's internal NIO control-plane server, used for master↔worker control messaging (not bulk data, which goes over Arrow Flight).
ontul.master.admin.worker.threads 8 Size of the thread pool serving Admin HTTP requests. Raise for higher concurrent admin/REST load; each thread handles one request at a time.
ontul.master.admin.ui.static.path ./admin-ui Filesystem directory containing the pre-built Admin UI static assets (HTML/JS/CSS) served by the Admin HTTP server. Relative to the process working directory.
ontul.master.flight.sql.port 47470 TCP port for the Master's Arrow Flight SQL server — the unified JDBC endpoint (jdbc:arrow-flight-sql://host:port) and the SDK data endpoint.
ontul.master.broadcast.timeout.ms 5000 Timeout (ms) for broadcasting a control message from the Master to all workers (e.g. config/key sync). A worker that does not respond within this window is treated as not acknowledging.
ontul.master.leader.deference.window.ms 3000 Sticky-incumbent leader election: on restart, defer this many ms before joining the election if a previous leader is still alive, so it can reclaim leadership without a leadership toggle. Set to 0 to disable.

Worker

Property Default Description
ontul.worker.host 0.0.0.0 Interface this Worker binds to and the host it registers under in the worker registry. The registered host:port (using the internal port) becomes the worker's nodeId.
ontul.worker.internal.port 19998 TCP port for this Worker's internal NIO control-plane server (master↔worker and worker↔worker control messaging). Also forms the worker's nodeId (host:port); each worker on the same host needs a distinct value.
ontul.worker.flight.port.offset 1000 Offset added to ontul.worker.internal.port to derive the Worker's Arrow Flight data port (flightPort = internal.port + offset). With the defaults the Flight port is 19998 + 1000 = 20998.
ontul.worker.shutdown.drain.timeout.seconds 60 Graceful-shutdown drain timeout (seconds). On shutdown the Worker stops accepting new work and waits up to this long for in-flight tasks to finish before forcing termination.

KMS

Property Default Description
ontul.kms.enabled true Master switch for the built-in Key Management Service. When true the KMS provides envelope-encryption keys for metadata, spill data, internal protocol payloads, etc. Disabling turns off at-rest and in-transit envelope encryption that depends on KMS keys.
ontul.kms.rocksdb.path ${ontul.base.data.dir}/kms Filesystem path to the embedded RocksDB store holding wrapped (encrypted) data encryption keys. Must persist across restarts; losing it makes all KMS-encrypted data unrecoverable.
ontul.kms.master.key.env ONTUL_MASTER_KEY Name of the environment variable (not the key value) that holds the KMS master passphrase/key. The master key unwraps all data encryption keys and seeds JWT signing. The named env var must be set identically on every node.
ontul.kms.pbkdf2.iterations 200000 PBKDF2 iteration count used to derive the KMS master key from the passphrase. Higher is stronger against brute force but slower at startup. Must be identical on all nodes or derived keys will not match.
ontul.kms.encrypt.internal.protocol true When true, internal NIO control-protocol message payloads are KMS envelope-encrypted in transit between master and workers using the key id below.
ontul.kms.internal.protocol.key.id internal-protocol KMS key id used to encrypt internal NIO protocol payloads. Only meaningful when ontul.kms.encrypt.internal.protocol=true. The Master ensures this key exists.
ontul.kms.fetch.max.retries 60 Maximum retries a Worker performs when fetching KMS keys from the Master at startup (the Master may not have finished key init). Combined with the retry interval this bounds total wait (~60s default).
ontul.kms.fetch.retry.interval.ms 1000 Delay (ms) between successive KMS key-fetch retry attempts (see ontul.kms.fetch.max.retries).

IAM

Property Default Description
ontul.iam.rocksdb.path ${ontul.base.data.dir}/iam Filesystem path to the embedded RocksDB store for IAM state (users, access/secret keys, roles, policies). Must persist across restarts.
ontul.iam.admin.user admin Username of the bootstrap admin account, created on first startup if no users exist. Used to log into the Admin UI/REST API initially.
ontul.iam.admin.password admin Initial password for the bootstrap admin account (used only when the account is first created). Change this in production; afterwards rotate via the Admin UI or bin/ontul-cli.sh iam:reset-password.
ontul.iam.audit.dir ${ontul.base.data.dir}/iam-audit Directory for the append-only IAM/security audit log (authentication attempts, access-key and password changes, role/policy edits). Entries are pruned per ontul.audit.log.retention.days. Must persist across restarts.
ontul.admin.socket.enabled true Whether the Master exposes the out-of-band Unix domain socket used for local admin recovery (bin/ontul-cli.sh iam:reset-password). The socket file's OS permission (mode 600) is the only authentication. Set false to remove the local recovery path. See Admin Password Recovery.
ontul.admin.socket.path ${ontul.base.data.dir}/admin.sock Filesystem path of the admin recovery Unix domain socket. Must live on a local filesystem that supports Unix domain sockets (not a networked mount); recreated on each Master start.
ontul.admin.socket.marker.file master.socket Name of the file under <ontul.home>/bin where the Master writes the socket path it actually bound to. bin/ontul-cli.sh prefers that published path over re-deriving one from this file, since ontul.base.data.dir can be overridden with -D at launch. Removed on shutdown.

Metadata

Property Default Description
ontul.metadata.rocksdb.path ${ontul.base.data.dir}/metadata Filesystem path to the embedded RocksDB store holding cluster metadata: catalog definitions, connections, table maintenance config, storage settings, etc. Must persist across restarts.
ontul.metadata.kms.key.id metadata-encryption KMS key id used to envelope-encrypt metadata at rest in the RocksDB store. Requires KMS to be enabled.
ontul.metadata.forward.queue.capacity 10000 Metadata replicates leader → followers only, so a write made on a follower (audit record, lineage edge, Flow status, …) is also forwarded to the leader, asynchronously and in order. This is the most writes that may wait to be forwarded; beyond it, a write stays on that follower only and a warning is logged.
ontul.metadata.forward.max.attempts 5 Attempts to forward one follower write while the leader is unreachable or an election is in progress (pause 1 s, 2 s, … between attempts) before giving up on it.

RPC Timeouts

Property Default Description
ontul.query.rpc.timeout.ms 30000 Timeout (ms) for query-related RPCs the Master issues to Workers (e.g. dispatching a query stage and awaiting its result). Raise for long-running stages over slow links.
ontul.internal.rpc.timeout.ms 10000 Timeout (ms) for internal (non-query) control-plane RPCs, e.g. small master↔worker coordination calls. Keep relatively short.

Semantic Layer / Retrievers / Ontology

Property Default Description
ontul.retriever.max.rows.ceiling 1000 Hard cluster-wide cap on the number of rows a single retriever invocation (POST /api/v1/retrievers/{fqn}/invoke) may return. Applied on top of each retriever's own maxRowsCeiling, so the effective cap is the smaller of the two. Lets an operator bound retriever result size globally regardless of per-retriever definitions.
ontul.rerank.enabled true Master kill switch for retriever re-ranking. false makes every retriever's rerank block report applied=false, reason=disabled-cluster-wide — an operator can drop re-ranking during an incident without editing any retriever.
ontul.rerank.max.timeout.ms 5000 Upper bound on a retriever's rerank.timeoutMs. The re-rank call sits inside the synchronous invoke, so this is also the worst-case latency a caller pays before falling back to the backend's ordering. Keeping it low is what stops a generative LLM — seconds per call, prose output, needs retries — from being wired into a retriever; that belongs in an agent loop.
ontul.rerank.min.timeout.ms 50 Lower bound on rerank.timeoutMs. A budget below this is almost always a typo (a 5 meant as seconds), and it would make every call time out while looking like the endpoint is at fault.
ontul.rerank.max.candidates 200 Upper bound on rerank.maxCandidates: how many rows may be sent to the endpoint in one request. Bounds endpoint load and payload size regardless of the maxRows a caller asks for.
ontul.rerank.max.text.chars 4000 Upper bound on rerank.maxTextChars, the per-document truncation. Cross-encoders take a fixed window (commonly 512 tokens) and silently drop the tail, so this caps how much text may be sent per row before that happens invisibly.
ontul.rerank.http.connect.timeout.ms 1000 TCP connect timeout for the re-ranker endpoint, separate from the per-call budget: an unreachable host fails fast instead of burning the whole request timeout.
ontul.rerank.default.path /rerank Request path appended to a RERANK connection's baseUrl when that connection does not set its own path. Change it only if every endpoint in the deployment mounts the API elsewhere — per-endpoint differences belong on the connection.
ontul.ontology.read.max.rows.ceiling 10000 Hard cluster-wide cap on rows returned by an ontology read — an ObjectSet query (POST /api/v1/object-types/{fqn}/query) or a link traversal (POST /api/v1/link-types/{fqn}/traverse). Applied on top of each request's own limit, so the effective cap is the smaller of the two. Mirrors the retriever ceiling: it bounds what an agent can pull through the ontology in one call regardless of what it asks for.
ontul.action.exec.timeout.ms 600000 (10 min) Time budget for a single action type invocation in DML mode — the governed write-back executed through the query engine. A write that exceeds it is abandoned rather than holding the invoke connection open indefinitely.
ontul.action.http.timeout.seconds 60 Per-request timeout for an action in OPERATION mode, i.e. the outbound HTTP call to the rest-operation connector (ERP/SaaS write-back). Keep it well under ontul.action.exec.timeout.ms; a slow ERP should surface as a failed operation, not as a stuck action.

Worker Health Check

Property Default Description
ontul.worker.health.check.interval.ms 10000 Interval (ms) between rounds of the Master's worker health checker. Each round pings every registered worker. Lower means faster failure detection but more background traffic.
ontul.worker.health.check.timeout.ms 5000 Per-worker health-check ping timeout (ms). A ping that does not return within this window counts as one failure for that worker.
ontul.worker.health.max.consecutive.failures 3 Number of consecutive failed health checks before a worker is declared down and removed from the cluster (its in-flight work is rescheduled/failed). With the defaults a worker is evicted after ~3 missed 10s rounds.
ontul.worker.health.check.future.grace.ms 2000 Extra grace (ms) added on top of the per-check timeout when waiting for each Future result during a health-check round. Prevents premature TimeoutException for checks about to return.

Catalogs

Property Default Description
ontul.catalog.reload.retry.seconds 30 A catalog that cannot be loaded (its REST catalog or that catalog's database is down when a master starts or becomes leader) is kept as unavailable: queries on it fail with Catalog '<name>' is unavailable: <reason> and GET /admin/catalogs shows available: false. Every master retries it at this interval until it loads. POST /admin/catalogs/<name>/reload retries at once. 0 turns the timer off. See Catalog availability.

Query

Property Default Description
ontul.query.history.max.size 200 Maximum number of finished queries retained in the in-memory query history (shown in the Admin UI). Oldest entries are evicted once the cap is reached.
ontul.query.max.global.concurrency 100 Cluster-wide admission limit: max queries allowed to run concurrently across all users. Enforced by a global semaphore; excess queries wait (see acquire timeout) then are rejected.
ontul.query.max.per.user.concurrency 10 Per-user admission limit: max concurrent queries a single IAM identity may run. Prevents one user from starving others within the global limit.
ontul.query.default.timeout.ms 300000 Default per-query execution timeout (ms, 5 minutes) when a query/job does not specify its own. Long-running queries exceeding this are cancelled.
ontul.query.max.memory.bytes 1073741824 Soft memory budget (bytes) per query for in-memory (Arrow) execution before operators spill intermediate data to disk via the exchange manager. Default is 1 GiB.
ontul.query.semaphore.acquire.timeout.seconds 30 Maximum time (seconds) a query waits to acquire a concurrency admission slot before it is rejected as overloaded.
ontul.iam.operation.permission.enforced false Whether a missing ontology:InvokeOperation grant BLOCKS an ontology action that writes to an EXTERNAL system, or is only reported. The grant's resource namespace is operation:<operationCatalog>:<operation> — separate from data:table:… used for reads, so broad read access cannot imply the right to post into an ERP. Report-only by default so upgrading does not revoke working actions: the missing grant is logged and audited as action:invoke:grant-missing with the exact resource, then set this to true to enforce. Applies to both the REST invoke endpoint and SQL CALL.
ontul.sql.strict true SQL capability gate. The master validates every planned query and rejects constructs the execution engine cannot evaluate correctly (window functions, subqueries, INTERSECT/EXCEPT/de-duplicating UNION, COUNT(DISTINCT), OFFSET, composite/non-equi join conditions, unimplemented scalar functions, …) with an explicit error. Set to false ONLY to triage a regression: it restores the previous behaviour where those queries run and return wrong results. DELETE/UPDATE/MERGE predicates are never gated. See SQL Capability Gate.

Connector

Property Default Description
ontul.connector.batch.size 4096 Number of rows per Arrow record batch when connectors read from / write to external sources (the vector batch size). Larger batches improve throughput and vectorization but use more memory per in-flight batch.

Iceberg Catalog (REST)

Server-wide defaults for Iceberg REST catalogs. Every value is a default: a catalog registered through the Admin UI / POST /admin/catalogs can override it with its own catalog.rest.* config. These apply on both the Master (interactive query) and Worker (job source/sink) code paths. See Iceberg Integration for per-catalog config.

Property Default Description
ontul.iceberg.polaris.default.scope PRINCIPAL_ROLE:ALL Default OAuth2 scope requested from a Polaris REST catalog when a catalog does not set catalog.rest.scope. Ignored for the glue flavor (not OAuth2).
ontul.iceberg.glue.signing.name glue AWS SigV4 signing service name for the AWS Glue Iceberg REST endpoint. Use s3tables when pointing at the Amazon S3 Tables REST endpoint.
ontul.iceberg.default.region us-east-1 Default AWS region used whenever none is otherwise specified — for S3 data access (when s3.region is absent) and for SigV4-signing Glue REST requests (when neither catalog.rest.signing.region nor s3.region is set). The single fallback for every Iceberg AWS region.
ontul.iceberg.rest.credential.vending.bypass true Suppress catalog-vended S3 credentials (Polaris STS-subscoped tokens / Glue Lake Formation) so Ontul reads and writes S3 directly with the configured static keys, keeping Ontul IAM the authoritative access boundary. Leave true (the only tested path; S3-compatible servers reject the STS temp keys Polaris would otherwise vend).
ontul.iceberg.default.format.version 2 Default Iceberg table format version for newly created tables. 2 = position-delete files; 3 = deletion vectors (Puffin), row lineage, variant. Opt-in: existing tables keep their own version and the library reads/writes all versions. On 3, DELETE/UPDATE/MERGE write deletion vectors instead of position-delete files. See Iceberg Integration.

Cluster Maintenance Mode

Unrelated to Iceberg Maintenance below, which is table compaction and keeps running during a window. See Cluster Maintenance Mode.

Property Default Description
ontul.cluster.maintenance.retry.after.seconds 30 How long a client is told to wait (Retry-After, seconds) before retrying a write that a maintenance window refused. Set it to roughly how long a window usually lasts: too low and clients hammer a cluster that is deliberately closed, too high and they stay away well after it reopens. The window itself is a runtime switch stored in the replicated metadata store, not a property — toggle it from Cluster Topology in the Admin UI or POST /admin/cluster/maintenance.

Iceberg Maintenance

Property Default Description
ontul.maintenance.interval.hours 6 Cluster-wide default interval (hours) between automatic Iceberg table maintenance runs (expire snapshots + rewrite/compact data files). Runs on the Master leader. Individual tables can override this.
ontul.maintenance.snapshot.retention.hours 168 Default snapshot retention (hours) for expire-snapshots: snapshots older than this are removed during maintenance (default 7 days). Expired snapshots can no longer be time-travelled to.
ontul.maintenance.target.file.size.mb 128 Default target output file size (MB) for the data-file rewrite/compaction step (passed as file_size_threshold_mb to OPTIMIZE). Larger means fewer, bigger files and fewer small-file reads.
ontul.maintenance.initial.delay.ms 60000 Initial delay (ms) after Master startup before the first maintenance round runs, so the cluster can finish coming up first (default 60s).
ontul.maintenance.history.retention.days 30 Retention (days) for the maintenance job history (records of past expire/compaction runs). Older entries are pruned.

Iceberg Observability (Table Health)

Continuous collection of Iceberg table health behind the Admin UI's Iceberg Health page, the /admin/iceberg/health REST surface, and the ontul_iceberg_table_* Prometheus gauges. Collection is a cluster singleton — only the Master leader walks the catalogs, because table health is a property of the table rather than of the master that read it. See Iceberg Observability.

Property Default Description
ontul.iceberg.observability.enabled true Master collects Iceberg table health. Set false to disable the Iceberg Health page, the REST surface, and the per-table gauges.
ontul.iceberg.observability.fast.interval.seconds 60 Fast-tier period. The fast tier reads only the already-loaded metadata.json — current snapshot summary, snapshot list, metadata log, manifest list — so it performs no manifest I/O and is safe to run often.
ontul.iceberg.observability.deep.interval.seconds 1800 Deep-tier period, per table. The deep tier plans a file scan to build the per-file size distribution (small-file ratio, percentiles, histogram); it reads every manifest, so it runs rarely and its result is cached and carried forward.
ontul.iceberg.observability.deep.max.per.cycle 3 Deep scans started per collection cycle. Bounds the manifest I/O a freshly elected leader performs when it holds no cached distribution for any table yet — remaining tables are picked up on later cycles. A scan requested from the UI bypasses this cap.
ontul.iceberg.observability.series.retention.seconds 86400 How long the per-table time series is kept in memory (default 24h). It backs the trend chart only; it is not persisted and does not survive a leader failover.
ontul.iceberg.observability.small.file.divisor 4 A data file smaller than targetFileSizeMB / N counts as "small". The divisor applies to each table's own maintenance target, so a table configured for 512 MB files is judged against 512 MB, not a global constant.
ontul.iceberg.observability.effects.max 500 How many maintenance before/after records to keep in memory. Every maintenance run is bracketed with a health sample so the UI can show what the run actually changed.

Audit Log

Property Default Description
ontul.audit.log.retention.days 90 Retention (days) for the query/access audit log and IAM/security entries — the absolute local cap; records older than this are pruned from the local store whether or not tiering is on. Editable at runtime from the Admin UI.
ontul.audit.read.enabled true Whether SELECT/read queries are audited (who ran which query). Volume is bounded by the sample rate.
ontul.audit.read.sample.rate 1.0 Fraction (0.0–1.0) of read queries recorded when read auditing is on.
ontul.audit.tier.mode none Audit tiering target: none (local only), iceberg (recommended), or parquet (fallback). Older records are offloaded to durable, queryable S3.
ontul.audit.tier.iceberg.table ice.ops.audit_events Target Iceberg table (catalog.schema.table) for tier.mode=iceberg. Written as a format-v2 table partitioned by day(event_time) via Ontul's in-process Iceberg writer; the namespace and table are created if missing. A target set from the Admin UI is validated on save (HTTP 400 if unusable).
ontul.audit.tier.parquet.path (empty) Target s3:// prefix for the tier.mode=parquet fallback.
ontul.audit.tier.connectionId (empty) S3 connection id (from the Connection registry) used by the tier.mode=parquet fallback.
ontul.audit.tier.retain.local.days 7 Days a tiered record is also kept locally as a hot copy (durable history lives in S3). Only records already tiered and older than this are pruned; 0 keeps only untiered records locally. The Admin UI still shows Iceberg-tiered history.
ontul.audit.tier.interval.ms 300000 How often the tiering sweep runs (ms).
ontul.audit.tier.batch.size 5000 Max records tiered per sweep. The sweep runs on the leader only and selects records by ingest time.
ontul.audit.tier.ingest.settle.ms 2000 Records ingested within this many ms of a sweep wait for the next one, so a record stored in the same millisecond as the scan is not stranded behind the watermark.
ontul.audit.viewer.max.page 5000 Max events one GET /admin/audit request (Admin UI Audit pages) may return; each request merges the local store with a scan of the Iceberg-tiered tables.
ontul.audit.tier.maintenance.enabled true When tiering→iceberg, auto-register the audit table for periodic Iceberg Table Maintenance (compaction + expire-snapshots + orphan cleanup).
ontul.audit.tier.maintenance.cron 0 * * * * 5-field UNIX cron for the audit table's maintenance (default hourly).
ontul.audit.tier.maintenance.snapshot.retain.last 24 expire_snapshots retain-last for the audit table — always keep at least this many recent commits.
ontul.audit.tier.maintenance.remove.orphan.files true Run remove_orphan_files on the audit table (reclaims files left by compaction/expiry).

See Audit Log for the full feature description.

Job

Property Default Description
ontul.job.history.dir ${ontul.base.data.dir}/job-history Directory where submitted-job history and job logs are stored. Accepts a local path or an S3 path (e.g. s3://bucket/ontul/job-history). If a jobLogs S3 storage connection is configured in the Admin UI, that S3 location overrides this value.
ontul.job.large.row.threshold 100000 Row-count threshold above which a job's result set is treated as "large" and handled via the streaming/spill path rather than buffered fully in memory.
ontul.driver.sync.chunk.size.bytes 4194304 (4 MiB) Chunk size used when the master streams an uploaded JDBC driver JAR out to the workers. Larger chunks mean fewer round-trips on a fast network; smaller chunks keep each NIO frame (and its retry cost) small on a lossy one.
ontul.deps.parent.first.prefixes (empty) Extra package prefixes kept parent-first in the child-first classloader used for UDFs and job dependencies, beyond the built-in JDK / Arrow / Ontul / slf4j set. Comma-separated. Use it when a job JAR ships its own copy of a library that must stay identical to the engine's (otherwise you get ClassCastException between two copies of the same class).

Flight SQL

Property Default Description
ontul.flight.sql.advertised.host (commented / optional) Specific hostname advertised for nginx-safe result streaming. When behind nginx, set to this Master's direct IP/hostname. Leave empty/unset to reuse the same connection (works without nginx).

Logging

Property Default Description
ontul.log.path ${user.dir}/logs Directory where log files are written (defaults to a logs folder under the process working directory). Used by the logback configuration to place per-node log files.
ontul.log.output.name (commented / optional) Base name used when naming this node's log/stdout file (e.g. master or worker). When unset, workers fall back to a name derived from their internal port (worker-<port>.out).

Exchange Manager

The Exchange Manager handles spill of intermediate shuffle/exchange data and execution-state checkpoints (streaming snapshots). It has two storage modes, selected by ontul.exchange.s3.enabled, and all payloads are KMS envelope-encrypted at rest in both modes.

  • Local-disk mode (default). Data is written to — and read from — a worker-local directory. This is fast, but the data is tied to that worker's disk: a stage that is retried on a different worker cannot see it.
  • S3-primary mode (ontul.exchange.s3.enabled=true). Data is read and written directly to S3 (the object store is the primary exchange storage, not a background copy). Because the spill/snapshot objects live in S3, a task that is reassigned to a different worker reads the same data from S3 — this is what enables worker-independent, fault-tolerant recovery. Object keys are worker-independent ({prefix}spill/{stageId}/…, {prefix}checkpoints/{stateId}/chk-{id}/w{worker}), so any worker configured with the same bucket and prefix resolves them.

Streaming checkpoints are stored per job under checkpoints/{stateId}/ (one part per worker plus a _latest pointer to the last globally completed checkpoint) and pruned per job; the startup orphan sweep never touches them. Payloads are encrypted with the KMS key exchange-spill, which the leader master creates at startup and sends to the workers. A worker that does not have the key refuses to write exchange data (the query fails and says why) rather than writing plaintext, unless ontul.exchange.encryption.enabled=false. Every stored object carries a format marker (OXE1 envelope-encrypted, OXP1 plaintext, written only with encryption turned off), so a reader never has to guess. A streaming job keeps its window state and offsets across a worker failure only in S3-primary mode — in local-disk mode a checkpoint lives on the disk of the worker that wrote it.

Note

S3-primary mode is analogous to a filesystem/object-store-backed exchange manager (exchange.base-directories=s3://…) in engines that support fault-tolerant execution: the object store replaces local disk for exchange data — it is not merely a backup of it.

Property Default Description
ontul.exchange.base.dir ${ontul.base.data.dir}/exchange Worker-local directory used in local-disk mode for spilling intermediate shuffle/exchange data and execution-state checkpoints. Scratch/spill space (safe to be node-local); KMS envelope-encrypted at rest. Ignored in S3-primary mode.
ontul.exchange.encryption.enabled true true: exchange data (both modes) is always KMS envelope-encrypted with the key exchange-spill; a worker without the key fails the write instead of storing plaintext. false: plaintext is written when the key is missing, with a WARN in the worker log.
ontul.exchange.s3.enabled false false selects local-disk mode. true selects S3-primary mode, where spill and snapshot data is read and written directly to S3 (not local disk, and not a background backup).
ontul.exchange.s3.endpoint (commented / optional) S3 endpoint URL for the exchange bucket. Set for S3-compatible stores (e.g. MinIO http://localhost:9000); leave unset to use the AWS default endpoint.
ontul.exchange.s3.region (commented / optional, default us-east-1) AWS region for the exchange bucket.
ontul.exchange.s3.bucket (commented / optional, default ontul-exchange) S3 bucket name used to store exchange objects.
ontul.exchange.s3.prefix exchange/ Key prefix under which exchange spill/snapshot objects are stored inside the bucket, so multiple clusters can share one bucket. Trailing slash optional.
ontul.exchange.s3.path.style (commented / optional, default true) Use path-style S3 addressing (bucket in the URL path) instead of virtual-hosted style. Required by most S3-compatible servers like MinIO.
ontul.exchange.s3.access.key (commented / optional) Static access key for the exchange S3 bucket. Leave empty to use the default AWS credential provider chain (env/instance profile/etc).
ontul.exchange.s3.secret.key (commented / optional) Static secret key paired with the access key above.
ontul.exchange.writer.threads 2 Number of background writer threads the exchange manager uses for spill and state-snapshot writes in local-disk mode. More threads means more parallel disk I/O for spill-heavy or fault-tolerant workloads.
ontul.exchange.s3.orphan.ttl.ms 86400000 S3-primary mode only. On worker startup, sweep exchange objects older than this TTL (ms) — orphans left by crashed jobs whose stage cleanup never ran. 0 disables the sweep. Default 24h.
ontul.exchange.s3.multipart.threshold.bytes 67108864 S3-primary mode only. Object size (bytes) above which uploads switch to multipart — avoids the 5 GB single-PUT limit and improves throughput for large spills. Default 64 MiB.
ontul.exchange.s3.multipart.part.size.bytes 16777216 S3-primary mode only. Part size (bytes) for multipart uploads (S3 minimum is 5 MiB). Default 16 MiB.

Streaming

Server-wide defaults for streaming jobs (Streaming Guide). Where a row names a "job override", that key in the SUBMIT STREAMING config wins for that job; the other rows are cluster-wide only.

Property Default Description
ontul.streaming.checkpoint.interval.ms 10000 Interval (ms) between checkpoints — the commit point of exactly-once sinks and the restore point after a failure. Job override: checkpointIntervalMs (an older job that sets only commitIntervalMs uses that).
ontul.streaming.checkpoint.timeout.ms 30000 A checkpoint not acknowledged by every worker within this time is aborted: it is never a restore point, and Iceberg files that did arrive are committed with the next checkpoint. Job override: checkpointTimeoutMs.
ontul.streaming.checkpoint.send.timeout.ms 5000 Timeout (ms) for delivering a checkpoint barrier to one worker.
ontul.streaming.checkpoint.tolerable.failures 3 Consecutive failed commits of a checkpoint to the Iceberg sink (by the master's single committer) tolerated before the job fails and restarts from the last completed checkpoint. A transient catalog or S3 error is retried with the next checkpoint; a persistent one fails the job instead of stalling it.
ontul.streaming.checkpoint.max.empty.commits 10 On an idle stream the coordinator still stamps an empty Iceberg commit (checkpoint id + source offsets, no data files) after this many consecutive empty checkpoints, so the restore point recorded in the table keeps up. 1 stamps every checkpoint.
ontul.streaming.max.retries 3 Automatic restarts before the job is marked FAILED. Job override: maxRetries.
ontul.streaming.retry.reset.checkpoints 5 A restarted job that completes this many checkpoints gets its retry budget back, so failures weeks apart do not add up.
ontul.streaming.source.poll.timeout.ms 100 Source poll timeout (ms). The executor polls continuously — there is no micro-batch trigger.
ontul.streaming.commit.interval.ms 10000 Flush interval (ms) for non-transactional sinks (Elasticsearch, HTTP, non-transactional Kafka) and for an Iceberg sink with ontul.streaming.exactly.once=false. Source offsets are never committed on this timer — only at a checkpoint. Job override: commitIntervalMs.
ontul.streaming.commit.row.threshold 10000 Row-count flush threshold for the same sinks (whichever of interval/rows comes first). Job override: commitRowThreshold.
ontul.streaming.log.interval.ms 30000 Interval (ms) between streaming progress log lines.
ontul.streaming.exactly.once (JVM system property) true Exactly-once Iceberg sink: the master commits every worker's files of a checkpoint in one Iceberg commit stamped with the checkpoint id and offsets. false restores per-worker commits (at-least-once). Job override: ontul.streaming.exactly.once in the job config.

Ontul Flow

Tuning for the named streaming-pipeline registry (Ontul Flow). A running flow's live status + run history are replicated across masters via the metadata store so the Flow list and History tab are consistent regardless of which master serves the request.

Property Default Description
ontul.flow.status.persist.interval.ms 5000 How often the owning master persists a running flow's live status + records to the replicated metadata store (flow.status.<name> / flow.history.<name>). Lower = fresher Flow list at the cost of more metadata writes/broadcasts.
ontul.flow.status.freshness.ms 15000 A persisted flow.status snapshot older than this is treated as stale (the flow is shown as not-running until the owner refreshes it). Keep it a few multiples of the persist interval.
ontul.flow.history.max.runs 20 Maximum run records retained per flow in the replicated run history (History tab).

Rejected records, schema drift and lag

A record a flow cannot decode (malformed JSON, undecodable Avro, an empty payload) is rejected: it never reaches the sink, and it is counted. The count is reported on the job status as rejectedRows / rejectsByReason and as the Rejected column in the Admin UI Flow list, so a decode failure is never silent. See Ontul Flow → Data quality.

Property Default Description
ontul.flow.reject.log.first 10 Log the first N rejected records of a run in full (reason, error, topic-partition@offset). A poison topic can reject every record, so beyond this the log is sampled. The count is always exact regardless of how much is logged.
ontul.flow.reject.log.every 1000 After the first N above, log every Nth rejected record. Set both to 0 to silence the per-record lines entirely and rely on the counters.
ontul.flow.error.sink.buffer.max.rows 10000 Maximum rejected records buffered in memory for the quarantine (errorSink) table between flushes. Quarantine must never be the reason a worker runs out of memory: past this the records are still counted, but not written, and an error is logged once.
ontul.flow.error.sink.payload.max.chars 65536 Truncate a quarantined payload at this many characters (a …[truncated] marker is appended). Quarantine exists to diagnose and replay the offending record, not to mirror arbitrarily large source messages.
ontul.flow.lag.refresh.interval.ms 5000 How often a source refreshes its lag reading. For Kafka this is a broker round-trip (endOffsets), so it is rate-limited rather than measured on every poll. Lag is reported as sourceLag (records behind) and sourceLagMs (age of the newest record processed).
ontul.flow.schema.evolution add Default schema-drift policy for an Iceberg sink when the flow spec does not set sink.schemaEvolution. add — a column the source starts sending is added to the target table, and int→long / float→double are promoted, before the batch is written. none — the table schema wins and the new column is ignored. strict — drift fails the flow instead of guessing. Only additive, read-compatible changes are ever applied; a column the source dropped is left in the table (later rows carry NULL), because one absent field in one batch is not evidence the column is gone.

Job Logs

Streaming and batch job logs can be flushed to append-only rolling gzip segments on S3 ({prefix}jobLogs/<jobId>/<seq>-<ts>.log.gz), so long-running jobs' logs stay durable and queryable by time range after the in-memory log is gone. By default these settings inherit the Exchange Manager's S3 target, so turning on S3-primary exchange also puts job logs on S3.

Property Default Description
ontul.job.logs.s3.enabled (inherits ontul.exchange.s3.enabled) Enable flushing job logs to S3 rolling segments. Defaults to whatever the exchange S3 mode is.
ontul.job.logs.memory.tail 1000 Number of most-recent log lines kept in memory per job (the rest are flushed to S3 segments).
ontul.job.logs.flush.interval.ms 10000 How often the worker/master drains buffered job-log lines to an S3 segment (time-based roll).

Each of the following overrides the corresponding ontul.exchange.s3.* value for job logs only — useful when logs belong in a different bucket (or a different account) from the exchange state. Leave them unset to inherit.

Property Default Description
ontul.job.logs.s3.endpoint (inherits ontul.exchange.s3.endpoint) S3 endpoint for job-log segments (e.g. a ShannonStore/MinIO URL).
ontul.job.logs.s3.region (inherits) S3 region.
ontul.job.logs.s3.bucket (inherits) Bucket holding the job-log segments.
ontul.job.logs.s3.prefix (inherits) Key prefix; segments are written under {prefix}jobLogs/<jobId>/<seq>-<ts>.log.gz.
ontul.job.logs.s3.access.key (inherits) Access key. Prefer configuring an S3 connection from the Admin UI Storage page so the secret is not in a properties file.
ontul.job.logs.s3.secret.key (inherits) Secret key. Same caveat as above.
ontul.job.logs.s3.path.style (inherits) true forces path-style addressing (required by most S3-compatible stores); false uses virtual-host style.

The full set can also be overridden with ontul.job.logs.s3.*. The backend can also be switched from the Admin UI Storage page at runtime (no restart), which persists to the replicated metadata store as storage.jobLogs.* / storage.exchange.* referencing an S3 connection.

Admin

Property Default Description
ontul.admin.token.access.expiry.ms 900000 Lifetime (ms) of short-lived JWT access tokens issued after login. After expiry the client must use its refresh token to obtain a new access token (default 15 minutes).
ontul.admin.token.refresh.expiry.ms 86400000 Lifetime (ms) of JWT refresh tokens, which let a client mint new access tokens without re-authenticating (default 24 hours). Effectively the maximum idle session length.
ontul.admin.http.max.content.size.bytes 10485760 Maximum accepted Admin HTTP request body size (bytes); larger requests are rejected (default 10 MiB). Raise if uploading large dependency JARs/drivers via the Admin API.

Authentication — Password Storage

Property Default Description
ontul.auth.password.hash.iterations 600000 PBKDF2-HMAC-SHA256 iterations used when a local password is written. The count travels with each stored hash, so raising this does not invalidate existing passwords — they are rewritten at the new cost on their owner's next successful login. Values written under the previous unsalted SHA-256 verify the same way and are upgraded identically, so nobody is locked out.

Single Sign-On (OIDC / SAML / LDAP)

Every setting below can also be managed from the admin console under Single Sign-On, which stores it in the replicated metadata store and applies it on every master with no restart. Stored settings win over this file: the file brings a cluster up, and the console is how it is changed afterwards — if the file won, a console change would be reverted by the next restart, silently. See Single Sign-On for how the console and the REST surface differ.

Identity mapping (all three providers)

Property Default Description
ontul.sso.group.mappings (empty) idpGroup:ontulGroup pairs, comma-separated. Empty means provider group names are used as they are. Once set the mapping is exhaustive — a group not named here is dropped, so creating a group at the provider cannot grant access on this cluster by itself.
ontul.sso.allow.unmapped.groups false Whether an identity whose groups all map to nothing may still sign in. Off deliberately: such a session has no policies and is denied every action, so admitting it produces a caller who is signed in and can do nothing. The directory login endpoint reports that case as 403, separately from a wrong password's 401.
ontul.sso.federated.session.seconds 3600 Lifetime of a federated session — the record that lets /v1/api/authz/check resolve a federated caller's groups. It bounds how long access outlives a revocation at the provider, which this cluster is not told about. No refresh token is issued for a federated session for the same reason.

OpenID Connect

Property Default Description
ontul.sso.oidc.enabled false Enable the OIDC provider.
ontul.sso.oidc.issuer (empty) Issuer URL. Endpoints and the signing key set are read from its discovery document, so they are not configured individually.
ontul.sso.oidc.client.id (empty) Client id registered at the provider.
ontul.sso.oidc.client.secret (empty) Client secret. Credential — never read back by the console.
ontul.sso.oidc.redirect.uri http://localhost:8090/admin/auth/sso/oidc/callback Must match the redirect URI registered at the provider exactly, and must be the address browsers reach — the load balancer's, not one master's.
ontul.sso.oidc.scopes openid profile email Scopes requested. Deliberately excludes groups: it is not a standard scope, and a provider that does not define it rejects the whole authorization request with invalid_scope.
ontul.sso.oidc.username.claim preferred_username Claim holding the login name.
ontul.sso.oidc.groups.claim groups Claim holding group membership. The provider must be configured to include it.
ontul.sso.oidc.audience (empty) Expected audience. Empty falls back to the client id. A token issued for another application is refused even though it is genuine and correctly signed.

SAML 2.0

Property Default Description
ontul.sso.saml.enabled false Enable the SAML provider.
ontul.sso.saml.idp.entity.id (empty) Identity provider entity ID. Read automatically when the provider's metadata is imported from the console.
ontul.sso.saml.idp.sso.url (empty) IdP single sign-on URL.
ontul.sso.saml.idp.certificate (empty) Base64 IdP signing certificate. Every assertion's signature is verified against it.
ontul.sso.saml.sp.entity.id ontul This cluster's entity ID, as it appears in the SP metadata the provider imports.
ontul.sso.saml.sp.acs.url http://localhost:8090/admin/auth/sso/saml/acs Assertion consumer URL. As with the OIDC redirect, this must be the address browsers reach.
ontul.sso.saml.nameid.format (empty) Requested NameID format. Empty omits the request entirely and lets the provider issue what it is configured for — naming one breaks more integrations than it fixes.
ontul.sso.saml.sign.requests false Sign authentication requests. Needs an SP keypair, generated from the console; re-import the SP metadata at the provider afterwards so it picks up the certificate.
ontul.sso.saml.username.attribute uid Assertion attribute holding the login name.
ontul.sso.saml.groups.attribute groups Assertion attribute holding group membership.

LDAP / Active Directory

Property Default Description
ontul.sso.ldap.enabled false Enable the directory provider. With it on, a directory password also works on the ordinary console login form.
ontul.sso.ldap.url ldap://ldap.example.com:389 Directory URL. Use ldaps:// or enable StartTLS — otherwise the bind password crosses the network in the clear.
ontul.sso.ldap.bind.dn (empty) Service account that searches for user entries. Authentication is search then bind: the user's DN cannot be constructed, since Active Directory puts people under CN=John Doe,OU=Staff,… where neither component is the login name.
ontul.sso.ldap.bind.password (empty) Service account password. Credential.
ontul.sso.ldap.user.base.dn (empty) Subtree searched for user entries.
ontul.sso.ldap.user.filter (uid={0}) Filter locating the user; {0} is the login name, escaped per RFC 4515 before substitution. Active Directory usually wants (sAMAccountName={0}).
ontul.sso.ldap.group.base.dn (empty) Subtree searched for groups.
ontul.sso.ldap.group.filter (member={0}) Filter locating groups containing the user; {0} is the user's DN. Membership is read both from this search and from the user's memberOf, because directories disagree about which side records it.
ontul.sso.ldap.group.name.attribute cn Attribute holding the group name.
ontul.sso.ldap.starttls false Upgrade a plain ldap:// connection with StartTLS.

Python

Property Default Description
ontul.python.path python3 Python interpreter used by Workers to run Python UDFs and Python-based exports (PDF/XLSX/PPTX). Either a command on PATH or an absolute path. Must exist on every worker host with the required packages.

Cache

Property Default Description
ontul.cache.max.bytes 268435456 Maximum size (bytes) of the in-memory Arrow LRU result/data cache. Once full, least-recently-used entries are evicted (default 256 MiB). Larger means more cache hits but higher memory use.
ontul.result.cache.enabled true Cache small query results so a repeated identical query is answered without re-planning or re-scanning.
ontul.result.cache.max 1000 Maximum number of cached result entries.
ontul.result.cache.max.bytes 8388608 Largest single result that may be cached (8 MiB). Bigger results stream through uncached rather than evicting everything else.
ontul.result.cache.ttl.ms 60000 How long a cached result stays valid (60s). Kept short because the underlying tables can commit at any time.
ontul.cache.data.enabled false Opt-in on-disk cache of Iceberg data files, so repeated scans of the same files avoid the object store. See Query Performance.
ontul.cache.data.dir <ontul.base.data.dir>/… Directory holding the on-disk data cache.
ontul.cache.data.max.bytes 5368709120 Cache capacity on disk (5 GiB). Least-recently-used files are evicted past this.
ontul.cache.data.max.file.bytes 67108864 Largest single data file that may be cached (64 MiB).
ontul.cache.data.ttl.ms 0 Time-to-live for a cached file; 0 disables expiry and relies on LRU eviction alone.
ontul.cache.data.kms.key.id metadata-encryption KMS key used to encrypt cached data files at rest, so the cache does not weaken the encryption guarantees of the source.

Data Lineage

Property Default Description
ontul.lineage.openlineage.url (commented / optional) OpenLineage-compatible HTTP endpoint (Marquez, DataHub, ...) to emit a RunEvent on every CTAS/INSERT/MERGE/CREATE VIEW. Leave empty to disable emission (lineage is still captured and stored locally).
ontul.lineage.openlineage.namespace ontul Namespace string reported in emitted OpenLineage events, used by the lineage backend to group datasets/jobs under this Ontul instance.
ontul.lineage.openlineage.timeout.ms 5000 HTTP timeout (ms) for the POST that emits a single OpenLineage event. Kept short so lineage emission never blocks query completion for long.

Client / SDK Properties (ontul.user.*)

These properties are not used by the server directly. They are set on the client side — by the SDK, JDBC driver, or job submission — via system properties (-D) or environment variables. They are listed in ontul.properties for reference only.

Property Default Description
ontul.job.driver.mode (commented / optional, default master) Job driver delegation mode: master (interactive) or worker (submit). Set per-job in the job config, not as a server property.
ontul.master.flight.port (commented / optional, default 47470) Flight SQL port used by the SDK to connect to the Master. The server uses ontul.master.flight.sql.port; this is the client-side equivalent passed as a system property when launching jobs.
ontul.user.accessKey (commented / optional) Client-side credential: IAM access key id used by the SDK / submitted jobs to authenticate to the Master. Set via -D or env var on the client. Paired with ontul.user.secretKey.
ontul.user.secretKey (commented / optional) Client-side credential: IAM secret key paired with ontul.user.accessKey. Treat as a secret; set on the client side only.
ontul.user.token (commented / optional) Client-side credential: pre-obtained JWT bearer token for SDK/JDBC auth, as an alternative to access/secret key login. Set on the client side only.
ontul.user.stsToken (commented / optional) Client-side credential: short-lived STS temporary token for SDK auth (temporary session credentials). Set on the client side only.