Skip to main content

Running AiSOC on one server

This is the path for a server you reach over the network — a VM, a NUC in a rack, a cloud instance — as opposed to make up on a laptop. It is also the usual step before moving to Kubernetes.

Two things differ from a laptop install, and both are deliberate choices you have to make rather than defaults you inherit.

If you would rather watch one being done end to end first, the deployment walkthrough records exactly this path — console published on a LAN address, real threat-intel data, an event pushed through to an AI verdict.

1. Publish the console​

Every host port ships bound to 127.0.0.1. That is correct for a laptop and useless for a server: the stack comes up healthy and nothing outside the machine can reach it, including the browser you intended to use.

One variable changes that:

# .env
AISOC_CONSOLE_BIND_ADDR=0.0.0.0

The console is the only port a browser needs. The bundle it serves calls same-origin paths — /api/v1/alerts, /ws/… — and the Next.js server forwards them to the API, the agents service and the realtime gateway over the internal Docker network. Publishing one port therefore exposes one service, and Postgres, Redis, Kafka and Neo4j stay on loopback with the credentials make env generated for this install.

Publishing the console also switches off anonymous access

docker compose up -d runs production-class: ENVIRONMENT defaults to production and AISOC_DEV_MODE to 0, so a request carrying no bearer token gets a 401. You do not have to set anything for that.

Setting AISOC_CONSOLE_BIND_ADDR=0.0.0.0 reinforces it. Compose passes the published addresses to each service as AISOC_PUBLISHED_BIND_ADDRS, and a service refuses the development auth shim outright when any of them is not loopback — even if someone sets AISOC_DEV_AUTH_BYPASS=1 by hand. A container binds 0.0.0.0 internally and cannot work this out for itself, which is why the addresses are passed in rather than inferred.

This used to be the other way round. ENVIRONMENT defaulted to development here and in .env.example, and in a development-class environment the API resolved an uncredentialed request to a demo user with the admin role whose tenant was the same one make bootstrap put your real account in. Following this page therefore published an anonymous administrator over your own data. If you are upgrading, nothing is required: the default changed. What changed for you is that make up no longer signs you in automatically — use make up-dev on a laptop if you want that back.

docker compose up -d
curl http://<your-host>:3000/api/health # {"status":"ok"}
Put TLS in front of it

0.0.0.0 means every interface the host has. On anything that is not a trusted network, terminate TLS in a reverse proxy (nginx, Caddy, Traefik, Cloudflare Tunnel) and point it at 127.0.0.1:3000 instead of publishing the console directly. Sessions are bearer tokens; over plain HTTP they are readable by anything on the path.

There is a second variable, AISOC_BIND_ADDR, which moves every binding including the datastores. It exists for an isolated network where you want psql from another machine. make env generates a real password for each datastore, so these are not shipped literals any more, but they are now reachable from the network rather than from this host alone. Prefer an SSH tunnel.

Also set the console's own address​

make bootstrap prints the URL to sign in at, and it defaults to http://localhost:3000, which is the wrong instruction on a server:

# .env
AISOC_CONSOLE_URL=http://aisoc.internal:3000

2. Point the console at your services​

If you run the bundled docker-compose.yml unchanged you can skip this — the defaults already name the Compose services.

If your API lives somewhere else — a different Compose project, a separate host, an existing deployment — set the addresses the console proxies to:

# .env
AISOC_API_URL=http://api.internal:8000
AISOC_AGENTS_URL=http://agents.internal:8084
AISOC_REALTIME_URL=http://realtime.internal:4000

These are read when the container starts. You do not need to rebuild the image, and there is no CORS configuration, because the browser never talks to these addresses — only the console's Node process does.

NEXT_PUBLIC_* cannot do this

Next.js inlines every NEXT_PUBLIC_* value into the JavaScript bundle when the image is built. Setting NEXT_PUBLIC_API_URL on a container running a pulled image is read by nothing, changes nothing, and reports nothing.

If you find that advice elsewhere, it predates this page. Use the AISOC_*_URL variables above. To make the browser call a different origin directly you have to rebuild: docker compose build --build-arg NEXT_PUBLIC_API_URL=https://api.example.com web — and then configure CORS on the API, which same-origin proxying exists to avoid.

3. Confirm what the deployment thinks it is​

curl http://<your-host>:3000/api/runtime-config
{ "demoMode": false, "demoModeSource": "runtime", "consoleVersion": "11.1.0" }

demoMode is the deployment's own answer rather than something to infer from the page. demoModeSource says where it came from:

demoModeSourceMeaning
runtimeAISOC_DEMO_MODE is set on the console container.
buildNEXT_PUBLIC_DEMO_MODE was compiled into the image; AISOC_DEMO_MODE is unset.
defaultNeither is set. Not a demo.

A build source on a self-hosted deployment means the image was built as a demo. Set AISOC_DEMO_MODE=false to override it without rebuilding.

consoleVersion is the version the image was built from, which is not necessarily the version of the repository you cloned: latest is a moving tag and can lag a release. If it disagrees with VERSION, pull:

docker compose pull && docker compose up -d

The Compose file uses pull_policy: missing, so an image already on the host is never refreshed on its own — a host that first pulled latest months ago keeps running that build until you pull explicitly.

Moving to Kubernetes​

The Helm chart under infra/helm/aisoc sets the same three upstream addresses on the console Deployment, derived from the release's own Services (<release>-api, <release>-agents, <release>-realtime). Override them through values when the API is outside the release:

services:
web:
env:
API_URL: http://api.aisoc.svc.cluster.local:80

This requires a console image built with the runtime entrypoint (AiSOC 11.1.0 or later). An older image ignores these variables and dials the addresses it was built with — http://api:8000 — which resolves to nothing in a cluster, so the console loads and every panel stays empty.

Ingress hostnames, TLS and replica counts are covered in Kubernetes.