Visit Azul.com Support

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

/var/lib/ljvmi/db/

APP_DB_PATH

The application database.

/var/lib/ljvmi/events/

EVENTS_DB_PATH

The observation dataset. Scratch space for queries that do not fit in memory sits in events/tmp/.

/var/lib/ljvmi/inbox/

INBOX_DIR

The deliveries from your Forwarders.

/var/lib/ljvmi/vuln-data/

VULN_DATA_DIR

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

EVENTS_DB_PATH

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. /var/lib/ljvmi/events/events.duckdb in the example layout. Losing this file means losing the history of your estate.

APP_DB_PATH

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. /var/lib/ljvmi/db/ljvmi in the example layout.

INBOX_DIR

Required. The root under which each Forwarder’s own delivery directory sits, /var/lib/ljvmi/inbox in the example layout. Also what the path handed back at check-in is derived from, so there is no second setting naming that.

INBOX_BACKEND

fs for a local filesystem directory, or s3 for an S3-compatible bucket. Your installation tells each Forwarder which one to use at check-in and at every status poll, so the two ends cannot disagree. An unrecognised value stops the application from starting rather than falling back, because falling back is how a Forwarder ends up writing successfully into somewhere nothing reads.

INBOX_WRITE_PREFIX

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 <prefix>/<tenant>/<forwarder>/. Unset, it hands back the application’s own path under INBOX_DIR, so each Forwarder must see the inbox at exactly that path.

INBOX_RETAIN_AFTER_INGEST

Keep delivered files after their rows reach the dataset. true by default, so the raw deliveries stay available for re-ingesting and debugging, and the inbox grows until you remove files. Set false to remove each file once its rows are recorded, which keeps the inbox a queue.

INBOX_TMP_SWEEP_AGE_MS

The age at which an abandoned partial delivery is swept. 86400000 (one day) by default. 0 disables the sweep.

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

S3_ENDPOINT, S3_REGION, S3_BUCKET

The bucket this installation is bound to. Your installation never creates it. The region is us-east-1 by default.

S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY

Leave both unset to use the ambient credential chain: AWS_* environment variables, then the container credential endpoint. Treat the secret as a production secret of the same class as SESSION_SECRET, and keep it out of the database’s backup domain.

S3_PATH_STYLE

Address the bucket as https://host/bucket/… rather than https://bucket.host/…. On by default, because self-hosted stores such as Silo, RustFS, and Ceph normally want it, and AWS accepts either.

S3_UPLOAD_TTL_SECONDS

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.

S3_MAX_OBJECT_BYTES

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

INGEST_INTERVAL_MS

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.

INGEST_ENABLED

Whether the pass runs at all. true by default. Off leaves deliveries sealed in the inbox and the existing dataset readable, which makes it a way to hold ingestion still without losing anything.

INGEST_MAX_FILES_PER_BATCH

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.

INGEST_DISCOVERY_PAGE_SIZE

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_ENABLED is true, the default, because the ingestion pass acts on the request.

  • INBOX_RETAIN_AFTER_INGEST has been true, 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:

  1. Stop the service and take a backup of /var/lib/ljvmi and of /etc/ljvmi/ljvmi.env. Keep the service stopped.

  2. Edit /etc/ljvmi/ljvmi.env, keeping a copy of the original:

    • Set LICENSING_RESOLUTION_ENABLED=false. This is required: while it is true, 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_UNITS and QUOTA_TOTAL_UNITS enough for the replay. A replay is charged like new data, and QUOTA_ENABLED=false stops refusals but not counting. A spent allowance pauses ingestion.

    • Optionally, raise INGEST_MAX_FILES_PER_BATCH, for example to 500, and shorten INGEST_INTERVAL_MS, for example to 60000, so the replay finishes sooner.

  3. Create the marker reingest.requested in the directory of EVENTS_DB_PATH, as the service user, and start the service:

     
    sudo -u ljvmi touch /var/lib/ljvmi/events/reingest.requested sudo systemctl start ljvmi

    The log shows Re-ingest reset complete, and the marker disappears. Re-ingest requested but refused means LICENSING_RESOLUTION_ENABLED is still true. Re-ingest reset failed means the marker stays and the next pass retries, so fix the cause shown in the log. If the failure is Cannot 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.

  4. Wait for the replay, which can take several passes. It is finished when the ljvmi_inbox_backlog_files metric is 0 for every channel, in a sample taken at least METRICS_GAUGE_TTL_MS (one minute by default) after the reset:

     
    curl -s http://127.0.0.1:9000/q/metrics | grep ljvmi_inbox_backlog_files

    Check the log for rejected deliveries, and the inventory totals against what you expect.

  5. Restore LICENSING_RESOLUTION_ENABLED, INGEST_MAX_FILES_PER_BATCH, and INGEST_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, so QUOTA_TOTAL_UNITS must 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.

Variable Description

JVMI_DEFAULT_LIMIT

Default page size of the inventory. 100 by default.

EVENTS_DB_READ_MEMORY_LIMIT

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 8GB. Despite its name it governs ingestion as well as reads.

EVENTS_DB_READ_THREADS

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.

EVENTS_DB_MAX_CONCURRENT_QUERIES

How many read statements run at once. 4 by default. Writes have a separate single slot.

EVENTS_DB_TEMP_DIRECTORY

Where a query that does not fit in memory spills to disk. tmp/ next to the DuckDB file by default, so /var/lib/ljvmi/events/tmp in the example layout, and the spill lands on the data disk.

EVENTS_DB_MAX_TEMP_DIRECTORY_SIZE

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.

EVENTS_DB_RESULT_CACHE_ENTRIES, EVENTS_DB_RESULT_CACHE_TTL_MS

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

LJVMI_PUBLIC_API_RATE_PER_MINUTE

Requests per tenant per minute, counted for token traffic only. 600 by default.

LJVMI_PUBLIC_API_MAX_CONCURRENT_HEAVY

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.

LJVMI_AUTH_API_TOKEN_DEFAULT_DAYS

The lifetime of a token whose creator did not ask for one. 90 by default.

LJVMI_AUTH_API_TOKEN_MAX_DAYS

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

QUOTA_MONTHLY_UNITS

Discovery units per calendar month. 1,000,000 by default.

QUOTA_TOTAL_UNITS

Discovery units over the life of the installation. 12,000,000 by default, and it does not reset.

QUOTA_PATCH_MULTIPLIER

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.

QUOTA_ENABLED

true by default. Off counts without enforcing, which lets you watch what a tenant would spend before a limit starts biting.

See Quota and Usage for what a unit is and what happens when an allowance runs out.

Licensing alerts

Variable Description

LICENSING_WATCH_DAYS

How long a new wave watches its machines, in days. 30 by default. The earlier name LICENSING_CHECK_BACK_DAYS is still read when LICENSING_WATCH_DAYS is not set.

LICENSING_UNINSTALL_CONFIDENCE_MONTHS

How long a machine must have been watched, and quiet, before its wave outcome is typed Uninstall rather than Migration. 6 by default.

LICENSING_RESOLUTION_ENABLED

Whether the hourly job that records wave outcomes and runs due schedules executes. true by default.

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

VULN_DATA_DIR

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, /var/lib/ljvmi/vuln-data in the example layout. Copy a zip into this folder yourself, or upload one under Settings > Vulnerability Data.

VULN_DATA_SYNC_INTERVAL_MS

How often the folder is scanned for a newer zip. 3600000 (one hour) by default.

VULN_DATA_ENABLED

true by default. Set false to stop the periodic folder scan and the periodic download. The active dataset is still restored at startup and Check now still works.

VULN_DATA_METADATA_URL

Opt in to automatic download. The https URL of a metadata document that names the latest dataset. Empty by default, and while it is empty the installation makes no outbound request for vulnerability data. A value that is not an absolute https URL stops the application from starting.

VULN_DATA_DOWNLOAD_INTERVAL_MS

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

LJVMI_WEB_PUBLIC_BASE_URL

Set it in every deployment, although the application starts without it. The absolute public address of your installation, without a trailing slash, for example https://intelligence.example.com. Invitation and reset links are built from it. Without it, those links fall back to what the proxy or the request reports, and the self-service "Forgot password" link on the sign-in page stays off.

LJVMI_EMAIL_PROVIDER

smtp. The default for a tenant that has not saved a choice. Unset means links are handed over manually until an Admin selects a provider.

LJVMI_EMAIL_FROM_ADDRESS

The From address of outgoing mail. Required for SMTP.

LJVMI_EMAIL_SMTP_HOST

The SMTP relay host.

LJVMI_EMAIL_SMTP_PORT

The SMTP relay port. 25 by default.

LJVMI_EMAIL_SMTP_USERNAME, LJVMI_EMAIL_SMTP_PASSWORD

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_FILE and TLS_KEYSTORE_PASSWORD. The installation serves HTTPS on HTTPS_PORT, and the plain port redirects there.

  • TLS terminated by your reverse proxy: set ALLOW_PLAIN_HTTP=true. The application serves plain HTTP on PORT and 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

TLS_KEYSTORE_FILE

The keystore, JKS or PKCS12, with the private key and the certificate chain. Required unless ALLOW_PLAIN_HTTP=true. Keep it outside the release directory and outside /var/lib/ljvmi, readable by the service user only: a key inside the data directory means one copy of a backup yields both your estate and the ability to impersonate your installation. A PEM certificate and key convert with openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out ljvmi.p12.

TLS_KEYSTORE_PASSWORD

The password of the keystore, also used for the key.

TLS_KEYSTORE_TYPE

JKS or PKCS12. Needed only when the file name doesn’t end in .jks, .p12, or .pfx.

HTTPS_PORT

The HTTPS listen port of the web interface and the API. 8443 by default. Used only with a keystore.

PORT

The plain HTTP listen port. 5173 by default.

HTTP_MODE

What the plain port does once TLS is on: redirect to HTTPS, keep serving with enabled, or close with disabled. redirect by default, and enabled with ALLOW_PLAIN_HTTP=true. A Forwarder doesn’t follow redirects, so keep enabled while any Forwarder still uses an http:// address.

ALLOW_PLAIN_HTTP

Start without a keystore and serve plain HTTP on PORT. false by default. Exactly true or false: any other value stops the application from starting. Set next to a keystore, it keeps the plain port serving and the session cookie not Secure, with a warning.

COOKIE_SECURE

Marks the session cookie Secure. true by default, and false by default with ALLOW_PLAIN_HTTP=true. Behind a reverse proxy that browsers reach over HTTPS, set it to true.

SESSION_MAX_AGE_DAYS

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

LJVMI_FORWARDERS_KEY_MAX_IDLE_DAYS

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.

LJVMI_FORWARDERS_KEY_EXPIRY_ENABLED

true by default. Set false to stop that pass. The application announces it at startup when it is off.

FORWARDER_VERSION_STALE_DAYS

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

SESSION_SECRET

Signs the session cookie. Override in any real deployment.

FORWARDER_KEY_SECRET

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

HOST

The interface the web interface and the API bind to. 0.0.0.0 by default.

MANAGEMENT_PORT

The port serving metrics and health probes. 9000 by default.

MANAGEMENT_HOST

The interface the management listener binds to. 127.0.0.1 by default, which serves a Prometheus on the same host. The port has no authentication and the metric set describes your estate, so when you open it to a Prometheus on another host, set the interface it reaches and restrict the port with a firewall to your monitoring network. See Monitoring and Health.

METRICS_GAUGE_TTL_MS

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.

METRICS_INVENTORY_TTL_MS

The shortest interval between recomputations of the inventory count metric. 900000 (15 minutes) by default.

ADMIN_PASSWORD_RESET

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.

DB_URL, DB_USER, DB_PASSWORD

Override the connection settings derived from APP_DB_PATH. They are for development.