Bare metal (JAR + systemd)
Run nivq directly on a Linux host with a Java runtime — no container engine. Install the JAR, configure it, and run it as a systemd service.
If your policy rules out a container engine, nivq runs as a plain executable JAR on a Linux host with a Java 25 runtime. You bring your own PostgreSQL (with pgvector) and Redis/Valkey (see Requirements); nivq is a single process you supervise with systemd. It runs in production mode by default — there's no profile or mode flag to set.
1. Install a Java 25 runtime
Any build works; the official image uses Eclipse Temurin.
# Debian/Ubuntu example — install a JDK/JRE 25 from your distro or Adoptium
java -version # → openjdk version "25"2. Download the JAR and lay out the directories
The JAR lives in the same registry as the container image: ghcr.io/nivorbit/jars/nivq. Log in with oras and pull it:
oras login ghcr.io -u <username> -p <token>
oras pull ghcr.io/nivorbit/jars/nivq:0.2.4 # → nivq-0.2.4.jarNo token yet? Ask the Nivorbit team or email [email protected]. Air-gapped? Get the JAR as a file and copy it to the host — skip this step.
Then create a dedicated user and a place for it:
sudo useradd --system --home /opt/nivq --shell /usr/sbin/nologin nivq
sudo mkdir -p /opt/nivq /etc/nivq
sudo cp nivq-0.2.4.jar /opt/nivq/nivq.jar
sudo chown -R nivq:nivq /opt/nivq /etc/nivq3. Provision the datastores
Point nivq at a reachable PostgreSQL 16+ with the pgvector extension and a Redis 7+ / Valkey. They can be on the same host or managed elsewhere — nivq just needs the connection details, which go in the env file next.
4. Create the environment file
nivq is configured entirely through environment variables. Put them in /etc/nivq/nivq.env (root-owned, readable only by the service user, since it holds secrets):
# datastores
NIVQ_DATASOURCE_URL=jdbc:postgresql://localhost:5432/nivqdb
NIVQ_DATASOURCE_USERNAME=nivq
NIVQ_DATASOURCE_PASSWORD=a-strong-value
NIVQ_REDIS_HOST=localhost
NIVQ_REDIS_PORT=6379
# 32-byte base64 key (AES-256) — back this up; losing it is unrecoverable
NIVQ_ENCRYPTION_KEY_V1=base64-32-bytes-here
# platform-managed LLM
NIVQ_PLATFORM_LLM_PROVIDER=anthropic
NIVQ_PLATFORM_LLM_API_KEY=sk-...
# first sign-in without an IdP — wire OAuth/OIDC later, see Authentication
NIVQ_BOOTSTRAP_ADMIN_USERNAME=admin@example.com
NIVQ_BOOTSTRAP_ADMIN_PASSWORD=a-long-passphrase
# public URLs
BACKEND_URL=https://api.example.com
FRONTEND_URL=https://app.example.com
# optional: seed a licence from a file instead of uploading it later
# NIVQ_LICENSING_LICENSE_FILE=/etc/nivq/license.jwtsudo chown nivq:nivq /etc/nivq/nivq.env
sudo chmod 600 /etc/nivq/nivq.envThe full set of variables — SSO, per-agent LLMs, embeddings, payments, observability — is in Configuration.
5. Run it directly
That's all it takes — nivq runs straight from the JAR:
sudo -u nivq env $(grep -v '^#' /etc/nivq/nivq.env | xargs) \
java -XX:+UseZGC -XX:MaxRAMPercentage=75.0 --enable-native-access=ALL-UNNAMED \
-jar /opt/nivq/nivq.jarThe first boot runs database migrations. In another shell, curl http://localhost:8080/actuator/health returns {"status":"UP"}. This runs in the foreground — perfect for a first check. For an always-on service that restarts on failure and starts with the host, wire up systemd below (Ctrl-C to stop the foreground run first).
6. Run it as a systemd service
Create /etc/systemd/system/nivq.service:
[Unit]
Description=NivQ API
After=network-online.target
Wants=network-online.target
[Service]
User=nivq
Group=nivq
EnvironmentFile=/etc/nivq/nivq.env
ExecStart=/usr/bin/java -XX:+UseZGC -XX:MaxRAMPercentage=75.0 --enable-native-access=ALL-UNNAMED -jar /opt/nivq/nivq.jar
SuccessExitStatus=143
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetThen enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable --now nivq
sudo systemctl status nivq
journalctl -u nivq -f # follow the logs7. Activate the licence
If you didn't set NIVQ_LICENSING_LICENSE_FILE, nivq is activation-pending. Read the fingerprint and upload the licence Nivorbit returns:
curl http://localhost:8080/v1/license/fingerprint # → NIVQ-FP-...
curl -F "[email protected]" http://localhost:8080/v1/license/uploadThe licence is stored in the database, so it survives restarts and JAR upgrades. See Licensing & activation for the full lifecycle.
8. Put TLS in front
nivq listens on plain HTTP on port 8080. Terminate TLS at a reverse proxy (nginx, Caddy) and set BACKEND_URL / FRONTEND_URL to the public HTTPS URLs. To upgrade later, stop the service, swap the JAR, and start it again. See Production hardening for the proxy, backups, and observability.
9. Serve the web client
The JAR is the API only; your users need the web client (the browser UI). On bare metal you have two options:
-
Run the container — if a container runtime is available just for this, run the static UI image pointed at the API:
Shell docker run -d --name nivq-web -p 3000:8080 \ -e NIVQ_API_BASE_URL=https://api.example.com \ ghcr.io/nivorbit/images/nivq-web:0.2.2 -
Serve the static files — no container at all: take the
dist/bundle, drop aconfig/default.jsonbeside it pointing at your API, and serve it from the nginx/Apache you already run. Starter configs ship indeploy/nginx.confanddeploy/apache.conf.
Serve it at exactly the API's FRONTEND_URL so sign-in works. Full details — config fields and the auth wiring — are in Web client.