Configuration Options
You configure Azul Intelligence On-Premises through environment variables in /etc/ljvmi/ljvmi.env. systemd reads that file through EnvironmentFile=, and run.sh does not read it itself. The process reads its environment directly, so every variable on this page works. A change takes effect when you restart the service:
sudo systemctl restart ljvmi
Most settings have a default. Five do not, and run.sh refuses to start without them: APP_DB_PATH, EVENTS_DB_PATH, INBOX_DIR, SESSION_SECRET, and FORWARDER_KEY_SECRET. You must also choose how the installation serves HTTPS: the application refuses to start without either TLS_KEYSTORE_FILE or ALLOW_PLAIN_HTTP=true. See HTTPS and sessions.
Tags are data, not configuration. An Admin sets them on the Forwarder in the web interface. See Tagging a Forwarder.
Storage locations
These settings decide where your data lives. Point each one at a persistent location outside the release directory. An upgrade adds a new release directory beside the old one, so anything inside a release directory stays behind.
The installation procedure lays the data out under one directory, /var/lib/ljvmi:
| Directory | Variable | What it holds |
|---|---|---|
|
|
The application database. |
|
|
The observation dataset. Scratch space for queries that do not fit in memory sits in |
|
|
The deliveries from your Forwarders. |
|
|
The vulnerability datasets. |
All four paths are settings. Set VULN_DATA_DIR as in the example env file: when it is left unset, the datasets land beside the release directories, and a backup of /var/lib/ljvmi misses them. See Backup and Restore.
| Variable | Description |
|---|---|
|
The observation dataset, and therefore your inventory, its runs, its tags, and its trends. Your migration waves live in the same file. One native DuckDB file. Required. |
|
The application database, an embedded H2 database: tenants, accounts, memberships, Forwarder registrations with their keys and tags, the delivery ledger, quota counters, wave schedules, exclusion groups, audit entries, and API tokens. Required. |
|
Required. The root under which each Forwarder’s own delivery directory sits, |
|
|
|
What check-in hands back, where a Forwarder reaches the same inbox under a different name than the application does: a share mounted at another path on the Forwarder host, or a container volume. Check-in then hands back |
|
Keep delivered files after their rows reach the dataset. |
|
The age at which an abandoned partial delivery is swept. 86400000 (one day) by default. |
There is one inbox root. Both the Agent channel and the AMT channel live under the same per-Forwarder prefix.
Using an S3-compatible bucket
| Variable | Description |
|---|---|
|
The bucket this installation is bound to. Your installation never creates it. The region is |
|
Leave both unset to use the ambient credential chain: |
|
Address the bucket as |
|
How long a Forwarder’s write grant lives. 900 seconds (15 minutes) by default. This is also your revocation window on this backend: a Forwarder you disable keeps write access until its current grant lapses, and is issued no new one. |
|
The hard ceiling on one delivery, enforced by the store itself as a condition of the grant rather than by a Forwarder behaving well. 268435456 (256 MiB) by default. |
Ingestion
| Variable | Description |
|---|---|
|
How often the ingestion pass runs. 600000 (ten minutes) by default. Also bounds how stale a Forwarder’s tags can be inside the query engine, because every pass refreshes them. |
|
Whether the pass runs at all. |
|
The number of sealed files one batch takes per channel. 50 by default. A backlog, such as the deliveries that piled up while the installation was down, is drained in several batches within one pass. |
|
Raw inbox entries read per discovery page, 1 to 1000. 1000 by default. |
Re-ingesting the kept deliveries
You can rebuild every observation from the deliveries kept in the inbox, for example after a release changes how a value is derived at ingestion. The installation empties the observation dataset in place, keeps your migration waves, forgets which deliveries it has read, and reads them all again. You request it with a marker file.
Before you start, check that:
-
INGEST_ENABLEDistrue, the default, because the ingestion pass acts on the request. -
INBOX_RETAIN_AFTER_INGESThas beentrue, the default, for the whole history you want back. Turning it on now restores nothing that was already removed. -
Every Forwarder whose deliveries you want replayed is registered and enabled. The directory of a disabled Forwarder is skipped.
-
The data disk has room for a write-ahead log about the size of the data being removed, on top of the usual spill space.
-
You have noted the current usage under Settings > Quota and Usage.
Then:
-
Stop the service and take a backup of
/var/lib/ljvmiand of/etc/ljvmi/ljvmi.env. Keep the service stopped. -
Edit
/etc/ljvmi/ljvmi.env, keeping a copy of the original:-
Set
LICENSING_RESOLUTION_ENABLED=false. This is required: while it istrue, the installation refuses the request, logs an error, and ingests as before, because waves would otherwise be closed against a half-replayed estate. -
Raise
QUOTA_MONTHLY_UNITSandQUOTA_TOTAL_UNITSenough for the replay. A replay is charged like new data, andQUOTA_ENABLED=falsestops refusals but not counting. A spent allowance pauses ingestion. -
Optionally, raise
INGEST_MAX_FILES_PER_BATCH, for example to500, and shortenINGEST_INTERVAL_MS, for example to60000, so the replay finishes sooner.
-
-
Create the marker
reingest.requestedin the directory ofEVENTS_DB_PATH, as the service user, and start the service:sudo -u ljvmi touch /var/lib/ljvmi/events/reingest.requested sudo systemctl start ljvmiThe log shows
Re-ingest reset complete, and the marker disappears.Re-ingest requested but refusedmeansLICENSING_RESOLUTION_ENABLEDis stilltrue.Re-ingest reset failedmeans the marker stays and the next pass retries, so fix the cause shown in the log. If the failure isCannot remove …reingest.requested, the reset itself completed: delete the marker by hand before the next pass, or the replay starts over and is charged again. -
Wait for the replay, which can take several passes. It is finished when the
ljvmi_inbox_backlog_filesmetric is0for every channel, in a sample taken at leastMETRICS_GAUGE_TTL_MS(one minute by default) after the reset:curl -s http://127.0.0.1:9000/q/metrics | grep ljvmi_inbox_backlog_filesCheck the log for rejected deliveries, and the inventory totals against what you expect.
-
Restore
LICENSING_RESOLUTION_ENABLED,INGEST_MAX_FILES_PER_BATCH, andINGEST_INTERVAL_MS, and restart the service. Waves past their end close on the next pass, against the full data. Restore the quota limits only after checking Settings > Quota and Usage: lifetime usage now includes the replay, soQUOTA_TOTAL_UNITSmust stay above it or ingestion pauses for good, and this month’s usage includes it too.
Closed waves keep their recorded outcomes, and open waves keep their members. A member whose machine had no stable identity can read as having no data after the replay. Recreate an affected open wave from the same filter if that matters.
To go back, stop the service and restore the backup of /var/lib/ljvmi and /etc/ljvmi/ljvmi.env together.
Query and trends
| Variable | Description |
|---|---|
|
Default page size of the inventory. 100 by default. |
|
The memory budget of the query engine. 16GB by default. It is a buffer budget, not the size of the process, so leave headroom for the JVM and the operating system: on a host with 16 GB of RAM set it to |
|
The size of the query engine’s worker pool. Unset uses the number of CPUs the JVM sees. More workers raise memory demand and contention. |
|
How many read statements run at once. 4 by default. Writes have a separate single slot. |
|
Where a query that does not fit in memory spills to disk. |
|
The bound on that spill. 24GB by default. A query that needs more fails with a temporary-storage error rather than filling the disk. Keep that much free on the data disk on top of the dataset, or lower the bound. |
|
The size of the cache that shares identical answers between users, and the longest age of a shared answer. 256 entries and 600000 (ten minutes) by default. |
Public API limits and tokens
These settings apply to requests that arrive with an API token. See The Integration API.
| Variable | Description |
|---|---|
|
Requests per tenant per minute, counted for token traffic only. 600 by default. |
|
Concurrent requests per tenant that query the dataset. 2 by default. This bounds a handful of simultaneous full-estate reads that would otherwise occupy the query engine while interactive users wait. |
|
The lifetime of a token whose creator did not ask for one. 90 by default. |
|
The longest lifetime a caller may request. 365 by default. A longer request is refused rather than shortened, and there is no token without an expiry. |
The ingestion allowance
| Variable | Description |
|---|---|
|
Discovery units per calendar month. 1,000,000 by default. |
|
Discovery units over the life of the installation. 12,000,000 by default, and it does not reset. |
|
How much larger the ceiling for refresh events is than the monthly allowance. 20 by default. A multiple, so raising the allowance raises it in step. |
|
|
See Quota and Usage for what a unit is and what happens when an allowance runs out.
Licensing alerts
| Variable | Description |
|---|---|
|
How long a new wave watches its machines, in days. 30 by default. The earlier name |
|
How long a machine must have been watched, and quiet, before its wave outcome is typed Uninstall rather than Migration. 6 by default. |
|
Whether the hourly job that records wave outcomes and runs due schedules executes. |
There is no licensing policy to configure. Coverage and migration waves work from the first sign-in, with plain filters on license and vendor, and exclusion groups, which leave installations out of new waves and, by default, out of Coverage, are optional. See Licensing Alerts.
Vulnerability data
Security Exposure compares the installations seen in the last 30 days against the latest quarterly Critical Patch Update and the CISA Known Exploited Vulnerabilities catalog. It needs a vulnerability dataset, delivered as a zip. There are three ways to load one, and the first two make no outbound connection at all.
| Variable | Description |
|---|---|
|
The folder that holds the dataset zips. The application loads the newest zip that validates, keeps it and the previous one, and saves an Admin’s uploads here. Set it to a persistent path outside the release directory, |
|
How often the folder is scanned for a newer zip. 3600000 (one hour) by default. |
|
|
|
Opt in to automatic download. The |
|
How often that URL is checked. 3600000 (one hour) by default. |
The download is the only outbound connection the installation makes on its own account. It uses HTTPS for the metadata, the zip, and every redirect, and it verifies the zip against the SHA-256 the metadata publishes. Proxy and trust store come from the JVM’s standard settings, passed through JAVA_TOOL_OPTIONS, for example -Dhttps.proxyHost, -Dhttps.proxyPort, and -Djavax.net.ssl.trustStore for a mirror with a private CA. Point the URL only at a host you trust: Azul’s CDN, or your own mirror.
Email and the public address
Email transport settings are environment-only. An Admin does not enter a mail server in the web interface: Settings > Email shows the transport details you configure here as read-only, lets an Admin select which configured provider to use (or none), send a test, and edit the message templates. See Email.
| Variable | Description |
|---|---|
|
Set it in every deployment, although the application starts without it. The absolute public address of your installation, without a trailing slash, for example |
|
|
|
The |
|
The SMTP relay host. |
|
The SMTP relay port. 25 by default. |
|
The relay credentials. Omit both for an unauthenticated relay. When you set one, set both. |
A relay that is not on the loopback interface must support STARTTLS with a certificate your installation trusts. Implicit TLS on port 465 is not supported.
Keep the mail passwords in /etc/ljvmi/ljvmi.env with your other secrets, and back that file up separately from /var/lib/ljvmi. The Admin’s choice of provider and any customised templates live in the application database, so they come back with /var/lib/ljvmi.
HTTPS and sessions
The installation requires HTTPS. Choose one of two setups, or the application refuses to start and names both settings:
-
Direct TLS, the recommended setup: set
TLS_KEYSTORE_FILEandTLS_KEYSTORE_PASSWORD. The installation serves HTTPS onHTTPS_PORT, and the plain port redirects there. -
TLS terminated by your reverse proxy: set
ALLOW_PLAIN_HTTP=true. The application serves plain HTTP onPORTand logs a warning at every start. This is a deliberate opt-out. Use it only behind a proxy that terminates TLS, or on a network you trust.
| Variable | Description |
|---|---|
|
The keystore, JKS or PKCS12, with the private key and the certificate chain. Required unless |
|
The password of the keystore, also used for the key. |
|
|
|
The HTTPS listen port of the web interface and the API. 8443 by default. Used only with a keystore. |
|
The plain HTTP listen port. 5173 by default. |
|
What the plain port does once TLS is on: |
|
Start without a keystore and serve plain HTTP on |
|
Marks the session cookie |
|
How long a signed-in session lasts. 30 by default. Shortening it also applies to sessions already issued. |
The installation is not a certificate authority. It never generates, signs, or self-signs a certificate. Use a certificate from your internal CA, issued for the host name your users and Forwarders use, and trusted by their browsers and by the Java runtime of every Forwarder host.
A Forwarder’s access key travels on the check-in channel and is the entire credential. Without HTTPS, the access key and the observations cross your network unencrypted.
The connections that your installation makes itself keep their encryption: STARTTLS to the mail relay, HTTPS for the vulnerability data download, and HTTPS to S3.
Forwarder keys
| Variable | Description |
|---|---|
|
How long an access key may go unused before a nightly pass withdraws it. 365 by default. Unused means no check-in, no status poll, and no delivery, and for a key never used, nothing since registration. The Forwarder then shows as Key expired, and reissuing its key is the way back. |
|
|
|
How long a Forwarder may report one build before the list marks it as possibly outdated. 90 by default, measured from when that build was first reported. |
Secrets
| Variable | Description |
|---|---|
|
Signs the session cookie. Override in any real deployment. |
|
Encrypts the readable copy of each Forwarder access key, which is what lets an Admin reveal one in the web interface. Override in any real deployment, and keep it outside the database’s backup domain: the key becomes recoverable from the database only together with this secret. |
Generate both secrets once, during installation, with openssl rand -base64 48. The bundle ships no default for either, and run.sh refuses to start when one is empty or still holds the placeholder, so an installation cannot start with a value that somebody else knows. Keep both out of version control, and treat them as you treat any other production secret. Do not change them later: a new FORWARDER_KEY_SECRET makes every stored Forwarder key unreadable.
Ports, metrics, and recovery
| Variable | Description |
|---|---|
|
The interface the web interface and the API bind to. |
|
The port serving metrics and health probes. 9000 by default. |
|
The interface the management listener binds to. |
|
How long a polled gauge is cached, so tightening your scrape interval cannot add load here. 60000 by default. Gauges are accurate to within this interval rather than instantaneous. |
|
The shortest interval between recomputations of the inventory count metric. 900000 (15 minutes) by default. |
|
Resets the first Admin’s password at startup. Leave unset. While it is set, every restart resets the password again. See Recovering a Lost Admin Password. |
|
Override the connection settings derived from |