Kubernetes
Install 3AM in a Kubernetes cluster with the Helm chart.
The chart runs 3AM as a single-replica StatefulSet (the audit log has one writer) with a persistent volume, and an optional model Deployment that only 3AM can reach. It is hardened by default:
- non-root (uid 65532), read-only root filesystem, all capabilities dropped, seccomp
RuntimeDefault; - no service-account token mounted, and no injected service-link variables;
- a NetworkPolicy that limits who can reach the console.
Tested on Kubernetes 1.34. Expected to work on 1.27+, including EKS, AKS, GKE and Rancher.
1. Make the images available
If your cluster can pull from the registry 3AM gave you access to, set image.repository to it in step 3, and add an
imagePullSecrets entry if it needs credentials.
2. Create the namespace and secrets
kubectl create namespace bank-ops
# the licence, if you already have it (otherwise install it later in the console)
kubectl -n bank-ops create secret generic 3am-licence --from-file=licence.json
# your sealed packs, from the bundle
kubectl -n bank-ops create secret generic 3am-packs --from-file=3am-0.1.0/config/packs/3. Install the chart
helm install 3am 3am-0.1.0/helm/3am-0.1.0.tgz -n bank-ops \
--set image.repository=registry.bank.internal/3am/3am-core \
--set licence.existingSecret=3am-licence \
--set packs.existingSecret=3am-packsKubernetes names can't start with a digit, so resources are named threeam… (for a release called 3am).
4. Open the console and sign in
kubectl -n bank-ops port-forward svc/threeam 8700:8700
kubectl -n bank-ops get secret threeam-admin -o jsonpath='{.data.token}' | base64 -d; echoOpen http://localhost:8700 and sign in with the token. The chart generates it once and keeps it across upgrades.
For a permanent address, enable the ingress (below).
5. Check the install
kubectl -n bank-ops get pods
kubectl -n bank-ops exec statefulset/threeam -- /opt/3am/3am-core preflightValues
| Value | Default | Purpose |
|---|---|---|
image.repository, image.tag, image.digest | localhost/3am-core, 0.1.0, empty | Where to pull 3AM from. Set digest to pin |
imagePullSecrets | [] | Registry credentials |
persistence.size, persistence.storageClass | 20Gi, cluster default | The data volume (audit log, incidents, index) |
licence.existingSecret, licence.key | empty, licence.json | A Secret with the licence. Empty: install it in the console |
packs.existingSecret | empty | A Secret with your sealed packs (*.3pk) |
config.existingConfigMap | empty | A ConfigMap with connectors.json and policy.json, if you manage config as code |
secretMounts | [] | Secrets mounted at /etc/3am/secrets/<name>, referenced from connectors.json as {"file": "…"} |
adminToken.existingSecret | empty | Your own admin token (key token). Otherwise generated once and kept |
ingestToken.existingSecret | empty | Your own ingest token. Otherwise generated once and kept |
model.enabled, model.profile | true, cpu | The on-prem model for incident notes. gpu requests nvidia.com/gpu: 1 |
model.image | docker.io/ollama/ollama:0.35.0 | The model runtime image |
model.pullOnStart | true | Pulls the model on start. Air-gapped: set false and preload the model volume |
service.type, service.port | ClusterIP, 8700 | The console service |
ingress.enabled, ingress.className, ingress.host, ingress.tlsSecret | off | A permanent address with TLS |
networkPolicy.enabled, networkPolicy.allowFromNamespaces | true, [] | Who may reach the console (e.g. [ingress-nginx]) |
resources, nodeSelector, tolerations | requests 250m / 512Mi, limit 2Gi | Scheduling |
pollSeconds | 15 | How often 3AM reads alerts |
Example with an ingress and the GPU model:
helm upgrade --install 3am 3am-0.1.0/helm/3am-0.1.0.tgz -n bank-ops \
--set ingress.enabled=true,ingress.className=nginx,ingress.host=3am.bank.internal,ingress.tlsSecret=3am-tls \
--set networkPolicy.allowFromNamespaces='{ingress-nginx}' \
--set model.profile=gpuThe install fingerprint on Kubernetes
The licence is tied to the cluster through the UID of its kube-system namespace. The console's setup screen
shows it, or read it directly:
kubectl get namespace kube-system -o jsonpath='{.metadata.uid}'; echoOpenShift
The chart runs 3AM as uid 65532. OpenShift's default restricted-v2 policy assigns a random uid instead, so allow
the release's service account to run as a fixed non-root user:
oc adm policy add-scc-to-user nonroot-v2 -z default -n bank-opsOpenShift is expected to work with this step but is not yet part of 3AM's certification runs.
Upgrades and removal
helm upgrade 3am 3am-0.2.0/helm/3am-0.2.0.tgz -n bank-ops --reuse-values
helm uninstall 3am -n bank-ops # the data volume and the generated tokens are kept