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.
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"}
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 thisNext.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:
demoModeSource | Meaning |
|---|---|
runtime | AISOC_DEMO_MODE is set on the console container. |
build | NEXT_PUBLIC_DEMO_MODE was compiled into the image; AISOC_DEMO_MODE is unset. |
default | Neither 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.