Kubernetes Deployment
The supported way to run AiSOC on Kubernetes is the Helm chart shipped at infra/helm/aisoc/ in the repo. It deploys every service (api, agents, realtime, mcp, ingest, enrichment, web) plus optional bundled Postgres, Redis, NATS, and OpenSearch via subcharts.
Helm chart (in-repo)
git clone https://github.com/beenuar/AiSOC.git
cd AiSOC
# The chart depends on the Bitnami postgresql and redis charts. A clean
# machine has no repository definitions, so `helm dependency update` fails
# with "no repository definition for https://charts.bitnami.com/bitnami"
# before it starts. This line was missing, which meant the documented
# Kubernetes path failed at its first command.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm dependency update infra/helm/aisoc
helm install aisoc infra/helm/aisoc \
--namespace aisoc --create-namespace \
--set secrets.openai.apiKey=sk-... \
--set postgresql.auth.password=changeme
The image tags come from the chart's appVersion, so there is nothing to
override for a working install. To pin a different release, note that every
service lives under services.<name> — --set api.image.tag=... addresses a
path no template reads and silently changes nothing:
--set services.api.image.tag=v12.0.0 \
--set services.web.image.tag=v12.0.0
Override any of the defaults in infra/helm/aisoc/values.yaml. For production deployments, walk through the Hardening Runbook before exposing the platform on the public internet.
Container images
All images are published to GHCR and Cosign-signed:
ghcr.io/beenuar/aisoc-core-api:v12.0.0
ghcr.io/beenuar/aisoc-agents:v12.0.0
ghcr.io/beenuar/aisoc-realtime:v12.0.0
ghcr.io/beenuar/aisoc-ingest:v12.0.0
ghcr.io/beenuar/aisoc-enrichment:v12.0.0
ghcr.io/beenuar/aisoc-web:v12.0.0
The tags above are an example pinned to a release. The current one is whatever
the chart's appVersion says, and a default install needs no tag at all.
The chart itself is published to an OCI registry from v11.3.0 onward:
helm show chart oci://ghcr.io/beenuar/charts/aisoc
This page previously named oci://ghcr.io/beenuar/aisoc, where no chart has
ever been pushed: the command answered not found. A published command is a
claim like any other, so release.yml now packages, lints and pushes the
chart on every tag, and re-checks that its appVersion names images that
exist. Until the first release carrying that job, install from a checkout as
shown below.
The chart's version, and one coordinate that meant two things
Chart.yaml carries two versions. appVersion is the application, and is
what every unpinned tag: in values.yaml falls back to. version is the
chart's own, and it is what --version selects.
If you pinned --version 5.9.2, pull it again and check what you have.
v12.3.2 published chart 5.9.2 with appVersion: v12.3.2; v13.0.0 bumped
appVersion and left the chart version alone, and helm push overwrote the
existing version rather than refusing it. charts/aisoc:5.9.2 therefore names
v13.0.0 today and named v12.3.2 before 29 September 2026. The replaced
bytes are gone and cannot be restored — the coordinate is honest from 6.0.0
onward, and 5.9.2 stays ambiguous forever.
scripts/check_chart_version.py is what stops the next one. It refuses a
release whose appVersion moved while the chart version did not, refuses a
chart edit with no version bump, and asks GHCR whether the version about to be
pushed already exists holding different content — comparing the unpacked
files, because helm package output is not byte-reproducible. What it does
not decide is whether a bump is a major, a minor or a patch: nothing in a diff
knows whether a renamed values.yaml key breaks your values file, so that
judgement stays with a human.
scripts/check_published_images.py resolves every one of these against GHCR
daily, so a name or tag that stops existing fails a build rather than a
helm install. It asks whether the tag exists and whether the image behind it
holds the version its tag names — not whether the page names the newest
release, which is why this list can lag one and still pass. This list read v5.2.0 until v11.1.0 — a tag no image has
ever carried — and two of the names, aisoc-api and aisoc-mcp, have never
been published at all. The API image is aisoc-core-api; the MCP server ships
inside it rather than as its own image.
Verify a signature before deploying:
cosign verify \
--certificate-identity-regexp '^https://github.com/beenuar/AiSOC' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/beenuar/aisoc-core-api:v12.0.0
Scaling
The chart deploys api, ingest, enrichment, alert-fusion, agents,
web and realtime; ueba, honeytokens and purpleTeam are off by
default. There is no mcp deployment — the MCP server runs inside the API.
kubectl scale deployment aisoc-agents --replicas=3 -n aisoc
kubectl scale deployment aisoc-api --replicas=2 -n aisoc
Horizontal Pod Autoscaler manifests are included in the chart — enable them
with --set services.api.autoscaling.enabled=true (and similarly for
agents, realtime). As above, the services. prefix is load-bearing.
Ingress
The chart ships an Ingress template. Configure your hostnames via values:
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: aisoc.example.com # web (Next.js, port 3000)
paths: [ "/" ]
- host: api.aisoc.example.com # api (FastAPI, port 8000)
paths: [ "/api", "/healthz" ]
- host: ws.aisoc.example.com # realtime (WebSocket + push, port 8002)
paths: [ "/ws" ]
- host: mcp.aisoc.example.com # MCP server (port 8003) — optional
paths: [ "/mcp" ]
tls:
- secretName: aisoc-tls
hosts:
- aisoc.example.com
- api.aisoc.example.com
- ws.aisoc.example.com
- mcp.aisoc.example.com
Network policies
The chart includes opinionated NetworkPolicy resources that restrict each service to only the dependencies it needs (for example, agents can reach Postgres, NATS, and OpenSearch but not the public internet). Enable them with --set networkPolicies.enabled=true. They are off by default to keep first-time installs frictionless, and on for any environment that holds real telemetry.