Install with Docker Compose (single host)¶
The Compose bundle runs the whole of Actian AI Analyst on one Docker host: the server, the web app, Postgres (with pgvector), Keycloak for sign-in, a Caddy reverse proxy and, optionally, the JDBC bridge. It is a supported production target for one host. There is no high availability, there is one Postgres, and your backups are snapshots of the Docker volumes.
For a Kubernetes cluster, use Install on Kubernetes with Helm instead.
Availability
- Who can do this: the operator who runs the Docker host, with shell access to it.
- Deployment: Self-managed deployments only. The Compose bundle always runs air-gapped.
What the Compose bundle sets for you¶
Some settings are fixed in the Compose file and cannot be changed through .env:
| Setting | Value | What it means |
|---|---|---|
| Network posture | Air-gapped | The server refuses vendor-hosted integrations at boot. See Network posture. |
| Entitlements | From the Licence | A missing or invalid Licence stops the server from starting. See Licence. |
| Identity | Bundled Keycloak | Members sign in through the Keycloak that runs in the stack. |
| File storage | Local Docker volume | Uploaded files and attachments are stored on the host. |
| Postgres | max_connections=300 |
Headroom for the server and Keycloak, which share the Postgres container. |
What runs¶
| Service | Role |
|---|---|
proxy |
Caddy. Terminates TLS and is the only container that publishes ports (HTTP_PORT, default 80, and HTTPS_PORT, default 443). It routes /api to the server, /auth/realms, /auth/resources, /auth/admin and /auth/js to Keycloak, and everything else to the web app. Its request size limit is 260 MB (decimal), which is slightly below the 250 MiB (about 262 MB) file upload cap, so an upload right at the cap may be refused by the proxy. |
web |
The web app. Its runtime configuration is rendered from the environment when the container starts. |
server |
The whole backend in one process. |
migrate |
A one-shot schema migration. The server waits for it to finish. |
keycloak |
Sign-in, members and organizations. |
postgres |
The product database and Keycloak's database. |
jdbc-bridge |
Only with --profile jdbc. Needed for Actian-family warehouses. |
Every container except proxy sits on a Docker internal network, which has no route off the host. Out of the box, the server can reach only the stack's own containers. To reach your LLM, warehouses and SMTP relay, see Reach your own services.
Host requirements¶
- An amd64 (x86-64) host. The Release bundle ships amd64 images only.
- Docker Engine 26 or later with Docker Compose v2 (
docker compose). Both must be on thePATH. - 4 CPU and 8 GB RAM recommended.
- A DNS name for the host that your members can resolve. This becomes
PUBLIC_HOST.
Memory limits are set per container: the server is capped at 3 GB (SERVER_MEMORY_LIMIT) and Keycloak at 1 GB (KEYCLOAK_MEMORY_LIMIT). Do not lower the server limit below 2 GB. A 250 MiB CSV upload peaks near 1 GiB in the server while it is converted.
Note
No language model or model weights ship with AI Analyst. Chat needs an OpenAI-compatible API that you run and that supports tool calling. See Configure inference.
Step 1: Run the installer¶
- Copy the Release bundle archive to the host and extract it. Check the archive before you carry it across the air gap, as described in Release bundle.
-
From the extracted bundle directory, run the installer. Pass the directory to install into, or leave it out to use
./ai-analyst:./install.sh /opt/ai-analyst
The installer:
- Runs
verify.sh, which checks the bundle's signatures and checksums offline. It stops at the first mismatch. - Loads every image into Docker.
- Copies the Compose files into the target directory. On a fresh install it creates
.envfrom.env.example. An existing.env,./licenceand./certsare kept. - Pins every
*_IMAGEvalue andAI_ANALYST_VERSIONin.envto the images it just loaded. - Prints the remaining steps.
Tip
Compare the public-key fingerprint that verify.sh prints with the one Actian publishes for the release before you trust a successful run. The key travels inside the bundle it vouches for.
Running a newer bundle's install.sh over the same directory is how you upgrade. See Upgrade and back up.
Step 2: Fill in .env¶
The installer creates .env from .env.example. Edit .env in the target directory and set these values. .env.example ships example values for PUBLIC_HOST, BOOTSTRAP_ORG_NAME and BOOTSTRAP_ADMIN_EMAIL, and the stack starts with them, so you must replace them. The other four values are blank, and the stack refuses to start while any of them is empty.
| Variable | What to set |
|---|---|
PUBLIC_HOST |
Ships with an example value; replace it. The hostname members type in the browser. DNS must resolve it to this host. It cannot be localhost, because containers resolve this name to the proxy. |
BOOTSTRAP_ORG_NAME |
Ships with an example value; replace it. The name of the first organization, created on first boot. |
BOOTSTRAP_ADMIN_EMAIL |
Ships with an example value; replace it. The email address of that organization's first Administrator. |
POSTGRES_PASSWORD |
The Postgres password. It is embedded in a connection URL, so it must be URL-safe. |
WAREHOUSE_CREDENTIAL_ENCRYPTION_KEY |
The key that encrypts stored secrets. At least 16 characters. |
KEYCLOAK_ADMIN_CLIENT_SECRET |
The secret the server uses to manage members and organizations in Keycloak. |
KEYCLOAK_ADMIN_PASSWORD |
The password for Keycloak's own administrator account (admin console at https://<PUBLIC_HOST>/auth/admin/). The user name is admin unless you set KEYCLOAK_ADMIN_USERNAME. |
Generate each secret with:
openssl rand -hex 32
Hex output is URL-safe, which POSTGRES_PASSWORD needs.
Also set these:
BOOTSTRAP_ADMIN_PASSWORD(strongly recommended): the Administrator's temporary password for the first sign-in..env.exampleships a placeholder value, so replace it. If you leave it unset, the Administrator is created with no password and you must set one in the Keycloak admin console. See Identity and first sign-in.LLM_*: your OpenAI-compatible chat API. Without it, members see Chat needs an LLM endpoint until an organization adds its own endpoint under Settings → Inference. See Configure inference.
LLM_PROVIDER=openai-compatible
LLM_BASE_URL=http://llm.example.internal:8000/v1
LLM_API_KEY=<API_KEY>
LLM_MODEL=<MODEL_NAME>
# generic, or reasoning for models that accept reasoning_effort
LLM_CAPABILITY_PRESET=generic
Keep your secrets safe
Store the generated values somewhere safe, together with your backups. Changing them after the first boot locks you out:
- Changing
POSTGRES_PASSWORDlocks you out of the database. - Changing or losing
WAREHOUSE_CREDENTIAL_ENCRYPTION_KEYloses every stored warehouse credential, every API key for an LLM or Embedding endpoint that an organization added under Settings → Inference, and the catalog API secrets stored on connections.
Optional settings¶
| Variable | Purpose |
|---|---|
HTTPS_PORT, HTTP_PORT |
Ports the proxy publishes. Defaults are 443 and 80. |
PUBLIC_ORIGIN |
Set only when HTTPS_PORT is not 443, for example https://analyst.example.internal:8443. |
EMBEDDING_MODEL, EMBEDDING_BASE_URL, EMBEDDING_API_KEY, EMBEDDING_DIMENSIONS |
Your OpenAI-compatible embedding API. Without it, semantic-layer search is lexical only. |
EMAIL_TRANSPORT, EMAIL_FROM, SMTP_HOST, SMTP_PORT, SMTP_TLS, SMTP_USER, SMTP_PASSWORD |
An SMTP relay for product email, such as Scheduled Insights and notifications. SMTP_TLS is starttls (default), implicit (port 465) or none. |
UNSUBSCRIBE_SIGNING_KEY |
Signs the unsubscribe links in notification emails. |
AGENT_STREAM_TIMEOUT_MS |
How long one agent response may take, in milliseconds (240000 when unset). Raise it for self-hosted models that need minutes to answer. |
BOOTSTRAP_SIGNUP_DOMAINS |
Sign-up domains for the first organization, comma-separated. Read only on the boot that creates the organization. See Identity and first sign-in. |
VITE_EDITION_LABEL |
An edition label, for example Enterprise Edition. It adds a line reading "AI Analyst label" to the footer of the organization switcher menu. |
SERVER_MEMORY_LIMIT, KEYCLOAK_MEMORY_LIMIT |
Container memory limits (3g and 1g by default). |
For every variable, see the Configuration reference.
Step 3: Install the Licence¶
Put the signed Licence that Actian sent you in the licence directory of the install, named licence.json:
cp licence.json /opt/ai-analyst/licence/licence.json
To use another directory or file name, set LICENCE_DIR or LICENCE_FILE in .env.
Images from a Release bundle carry Actian's Licence verification key built in, so you do not need a key file. The installer's closing message also mentions licence-public.pem. With Release-bundle images you need only licence.json.
The server reads the Licence once, at startup. After you replace it, restart the server service. See Licence.
Step 4: Set up TLS¶
By default, Caddy issues the site certificate from its own internal certificate authority (CA). This works immediately, but browsers show a certificate warning until that CA's root certificate is trusted on members' machines. To export the root certificate after the first start:
docker compose cp proxy:/data/caddy/pki/authorities/local/root.crt .
For production, use your own certificate:
- Put the certificate (full chain) in
./certs/tls.crtand its private key in./certs/tls.key. -
In
.env, set:CADDY_TLS=/certs/tls.crt /certs/tls.key -
If the certificate chains to a private CA, also put that CA certificate in
./certs/ca.crtand set:SERVER_EXTRA_CA_CERTS=/etc/ai-analyst/certs/ca.crtThe server fetches Keycloak's signing keys through the proxy, so it must trust the proxy's certificate. Without this setting, every API call fails with 401.
This setting also decides which private CAs the server trusts for your LLM, Embedding and warehouse endpoints. It replaces the default Caddy internal CA, so put every CA the server must trust in that one file. See Private certificate authorities.
Step 5: Start the stack¶
From the install directory, run:
docker compose up -d --wait
The first boot takes a minute or two:
- Postgres initializes its databases.
- Keycloak imports the sign-in realm. This happens only when the realm does not exist yet, so later restarts and upgrades keep the changes you make in the Keycloak admin console.
migrateapplies the database schema.- The server starts, and the Operator bootstrap creates the first organization and its Administrator from the
BOOTSTRAP_*values.
When the command returns, open https://<PUBLIC_HOST>/ and sign in as the Administrator with BOOTSTRAP_ADMIN_EMAIL and the temporary password. Keycloak asks for a new password and a name. For details, including inviting members (which needs SMTP), see Identity and first sign-in.
Reach your own services¶
Because the internal network has no route off the host, the server cannot reach your LLM endpoint, warehouses or SMTP relay until you connect them. There are two ways.
Option A: Run the services as containers on this host¶
Run the service (for example, an LLM server) as a container on the same host and attach it to the stack's network, ai-analyst_internal:
docker run -d --name llm --network ai-analyst_internal <your-llm-image>
Then point the stack at it by container name, for example LLM_BASE_URL=http://llm:8000/v1. Nothing else in the stack changes.
Option B: Opt in to your network¶
Start the stack with the customer-network override:
docker compose -f docker-compose.yml -f compose.customer-network.yml up -d --wait
This attaches server, keycloak and jdbc-bridge to one more Docker bridge network (named ai-analyst-customer unless you set CUSTOMER_NETWORK_NAME). That network is routed like any Docker bridge.
Your host firewall now keeps the install air-gapped
With the override, Docker no longer blocks traffic leaving the host. Use the host firewall (for example, the DOCKER-USER iptables chain) to allow traffic out of that bridge only to your own address ranges: your LLM and Embedding endpoints, warehouses and SMTP relay.
Keycloak also needs this route to reach an SMTP relay for invitation and password-reset emails.
Use the same -f options on every later docker compose command for this install.
Private warehouse addresses¶
On a Compose install, warehouse connections may connect to the private ranges 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 and loopback by default. To narrow this, set WAREHOUSE_EGRESS_ALLOW_PRIVATE_CIDRS in .env to a comma-separated list of CIDRs. Once it is set, only the listed private ranges are allowed. The same check applies to LLM and Embedding endpoints that an organization adds under Settings → Inference. This differs from the Helm install, where private ranges are refused until you list them. Link-local addresses are never allowed. See Warehouse connectivity.
Enable the JDBC bridge¶
Actian-family warehouses (Actian, Ingres, Informix and Zen) connect through the JDBC bridge.
-
In
.env, set:JDBC_BRIDGE_URL=http://jdbc-bridge:8080 JDBC_BRIDGE_TOKEN=<generate with: openssl rand -hex 32>The bridge refuses to start without a non-blank token.
-
Start the stack with the
jdbcprofile:docker compose --profile jdbc up -d --waitIf you also use the customer-network override, combine both:
docker compose -f docker-compose.yml -f compose.customer-network.yml --profile jdbc up -d --wait
Troubleshooting¶
| Symptom | Likely cause | What to do |
|---|---|---|
docker compose up stops with set <VARIABLE> in .env |
One of the four secrets is missing or empty. | Set the named variable in .env. See Step 2. |
| The server does not become healthy | The server refused to boot. | Run docker compose ps and docker compose logs server. The log names the offending key. Messages starting NETWORK_POSTURE=air-gapped mean a vendor-hosted setting is present, such as LLM_PROVIDER=anthropic or openai, which are Actian Inference settings. Use openai-compatible with your own API. Messages starting LICENCE_PATH mean the Licence is missing, malformed or not signed by Actian. Missing required env vars lists keys to add. |
| Every API call returns 401 | The server cannot verify Keycloak's tokens. | Check that PUBLIC_HOST is not localhost. With your own certificate from a private CA, check that SERVER_EXTRA_CA_CERTS names that CA. |
| Members see Chat needs an LLM endpoint | There is no deployment default LLM_* and the organization has not added its own endpoint, or the server cannot reach the endpoint. |
Set LLM_* and restart, or add an endpoint under Settings → Inference. Check that the server can reach the endpoint (see Reach your own services). |
| Browsers show a certificate warning | The default internal CA is in use. | Trust the exported root certificate on members' machines, or install your own certificate (Step 4). |
| Inviting members fails | Keycloak has no SMTP relay. | Configure SMTP in Keycloak and use the customer-network override so Keycloak can reach the relay. See Identity and first sign-in. |
To collect information for Actian support, see Diagnostic export.