Skip to content

Self-Hosting on Kubernetes

Run Nanorix inside your own cluster, in your own cloud account or data centre. Regulated data never leaves your infrastructure; the AuditProofs your install emits are byte-equivalent to the ones the hosted service emits, and verify with the same offline verifier.

This page walks the whole path once: sign up → API key → chart → install → run a capsule → get an AuditProof → verify it.

When you want this

Self-hosting is for teams whose regulated data cannot transit a third-party service — data-residency obligations, air-gapped or hospital-owned clusters, or a security review that requires the processing boundary to sit inside your own account. If none of those apply, the hosted service at nanorix.io needs no installation at all: start with the Quickstart.


What you need before you start

Requirement Why Check
Kubernetes 1.28+ chart floor (cgroup v2, seccomp profile, Pod Security admission) kubectl version
cgroup v2 on worker nodes capsule memory and CPU isolation cat /sys/fs/cgroup/cgroup.controllers lists cpu memory io
Linux kernel 5.10+, containerd 1.7+ / CRI-O 1.28+ namespace and seccomp behaviour the capsule runtime relies on uname -r on a node
Swap disabled on any node that will run capsules Nanorix refuses to start on a node with active swap — volatile-memory-only is enforced, not assumed cat /proc/swaps shows only the header line
Helm 3.14+ and cluster-admin the chart installs RBAC and NetworkPolicy helm version --short
Postgres 15 proof and account records bundled sub-chart for evaluation, your own instance for production
A namespace that permits privileged pods see Why the capsule runtime needs privilege kubectl create ns nanorix

Swap is a hard stop, by design

A node with active swap can page capsule memory to disk, which would break the volatile-memory-only property the AuditProof attests to. The API refuses to start rather than emit a proof it cannot stand behind. Managed node pools are usually swap-free already; a laptop-class VM often is not.


1. Get the chart and the image

The Helm chart and the Nanorix container image are distributed through onboarding — there is no public Helm repository or public image registry today. Ask for access at [email protected] and you will receive:

  • the chart bundle (nanorix chart, version 0.3.0 or later), and
  • a pull path plus credentials for the nanorix-api image, or a tarball to mirror into your own registry.

For an air-gapped cluster, mirror the image into your internal registry first and install from there:

crane copy <granted-pull-path>/nanorix-api:<tag> \
           registry.internal.example.com/nanorix-api:<tag>

Then resolve the chart's bundled dependencies once, from a machine with network access:

helm dependency build ./nanorix

2. Create the secrets the chart consumes

The chart never generates key material and never accepts it inline in a values file. Create the secrets yourself, in the namespace you will install into:

kubectl create ns nanorix

kubectl create secret generic nanorix-prod-secrets -n nanorix \
  --from-literal=CAPSULEFILE_MASTER_KEY="$(openssl rand -base64 32)" \
  --from-literal=JWT_SECRET="$(openssl rand -hex 32)"

# External Postgres: the chart reads the DSN from a secret you own.
kubectl create secret generic nanorix-postgres-dsn -n nanorix \
  --from-literal=DATABASE_URL="postgres://user:password@host:5432/nanorix"

Rotate both under your own key-management policy. If you sign with a KMS or HSM you control, see the secrets.signingKey and customer sections of the chart's values.yaml.


3. Write your overrides

Start from the production profile shipped with the chart, values-production-byoc.yaml, and layer your own file on top of it. That profile is the one written for a customer cluster: it turns the capsule runtime on, turns the bundled Postgres off, disables billing call-home, and marks every value you must supply.

# my-overrides.yaml
image:
  repository: registry.internal.example.com/nanorix-api
  digest: "sha256:<the digest you mirrored>"

region: eu-west-1          # your residency label; declared by you, never inferred

database:
  existingSecret: nanorix-postgres-dsn

secrets:
  signingKey:
    existingSecret: nanorix-prod-secrets
  jwt:
    existingSecret: nanorix-prod-secrets
  webauthn:
    rpId: nanorix.internal.example.com
    rpOrigin: https://nanorix.internal.example.com

storage:
  backend: pvc
  pvc:
    size: 100Gi
    storageClass: encrypted-fast

ingress:
  enabled: true
  className: nginx
  host: nanorix.internal.example.com

Why the capsule runtime needs privilege

Capsule execution sets up Linux namespaces, cgroup v2 limits and a private /proc from inside the API container. That needs a privileged security context, and the chart states the requirement rather than hiding it.

The chart's own default cannot run a capsule. values.yaml ships capsuleRuntime.privileged: false, which gives you an API surface you can browse but a capsule that fails during namespace and cgroup setup. values-production-byoc.yaml sets it to true — install with that profile, or set the value yourself:

--set capsuleRuntime.privileged=true

On a cluster enforcing restricted Pod Security Standards, grant the namespace an exception:

kubectl label ns nanorix pod-security.kubernetes.io/enforce=privileged

Leave it false only when you deliberately want an API-only evaluation with no capsule execution.


4. Install

helm install nanorix ./nanorix \
  --namespace nanorix \
  -f ./nanorix/values-production-byoc.yaml \
  -f my-overrides.yaml

kubectl rollout status deployment/nanorix-api -n nanorix --timeout=5m

Confirm the install is healthy:

kubectl port-forward -n nanorix svc/nanorix-api 8080:8080 &

curl -fsS http://localhost:8080/v1/health
curl -fsS http://localhost:8080/v1/health/ready

/v1/health/ready returning ready confirms the database is reachable and the schema migrations have been applied.


5. Sign up against your own install and get an API key

Your install issues its own API keys. Nothing about the steps below leaves your cluster.

API=http://localhost:8080

curl -fsS -X POST $API/v1/signup \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","jurisdiction":"EU"}'
{
  "api_key": "nrx_live_...",
  "customer_id": "6849eec3-...",
  "email": "[email protected]",
  "tier": "free",
  "limits": {"capsules_per_month": 100, "max_lifetime_seconds": 300, "concurrent_capsules": 3, "rate_limit_rpm": 20},
  "warning": "Store this API key securely — it will not be shown again."
}

jurisdiction is your declaration and selects which regulatory references appear in your AuditProofs. Nanorix stores it and never verifies or overrides it. Accepted values: US, EU, UK, CA, AU, IN, OTHER.

Commercial onboarding — image access, licensing, and billing — runs through Nanorix separately; talk to us before you put production data through a self-hosted install.


6. Run a capsule

KEY=nrx_live_...

# create — data_classification is your declaration, same discipline as jurisdiction
CAP=$(curl -fsS -X POST $API/v1/capsules \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"data_classification":"general","max_lifetime_seconds":300}' \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')

# execute inside the sealed capsule
curl -fsS -X POST $API/v1/capsules/$CAP/exec \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"command":"echo hello from the capsule","timeout":30}'

# destroy — this is what builds and signs the AuditProof
curl -fsS -X DELETE $API/v1/capsules/$CAP \
  -H "Authorization: Bearer $KEY" > destroy.json

Destroy is DELETE /v1/capsules/{id}. The response carries the full record; the AuditProof is its cdp field:

python3 -c 'import json;print(json.dumps(json.load(open("destroy.json"))["cdp"]))' > auditproof.json

Getting data in and out

input_data (up to 512 KB) with input_filename writes a file into the capsule before your command runs; larger inputs go through POST /v1/capsules/{id}/upload, and results come back through GET /v1/capsules/{id}/output. The in-capsule path conventions are not yet documented well enough to copy blind — ask us for the current layout before wiring a real workload, and use the echo command above for your first round-trip.


7. Verify the AuditProof

Verification is offline. It needs no network, no Nanorix account, and none of your data:

nanorix-verify auditproof.json

You should see all 8 destruction steps recompute and the Ed25519 signature check out against the key embedded in the document. That is integrity verification — the artifact has not been altered since it was signed. The full manual algorithm, and SDK equivalents in Python and TypeScript, are in Verification.

Anchoring a self-hosted proof to a published key

There is a second, stronger property: checking the signature against a key Nanorix publishes, rather than the key carried inside the document. That is what --trust-chain does, and it is what lets an auditor verify a proof without trusting the party that handed it to them.

A self-hosted install signs with the key material you supplied it. Until its signing authority and key version are published in a trust-chain manifest an auditor can fetch, that install's proofs will verify for integrity but will not anchor — key resolution fails, and the verifier says so rather than passing quietly. Plan for this before an audit depends on it: talk to us about registering a customer-rooted signing authority for your install, and until then state the integrity verdict for what it is rather than describing it as independently anchored.


Day-2

# upgrade
helm upgrade nanorix ./nanorix -n nanorix \
  -f ./nanorix/values-production-byoc.yaml -f my-overrides.yaml

# roll back
helm history nanorix -n nanorix
helm rollback nanorix <revision> -n nanorix

# scale the API (the capsule runtime is a DaemonSet — one Pod per node, no knob)
kubectl scale -n nanorix deployment/nanorix-api --replicas=3

Back up the proofs. The AuditProof archive is the asset with the longest life — a standard Postgres backup of the proof records covers it, plus a snapshot of the PVC or object-store bucket if you route proof storage there. For multi-year retention obligations, back the bucket with object-lock or the equivalent immutability control in your storage platform.

Uninstall leaves nothing behind except the evidence that is designed to survive:

helm uninstall nanorix -n nanorix
kubectl get pvc -n nanorix          # delete any released volumes

Where this has been run

Nanorix validates this path on a real non-GCP cluster rather than only in a lab: the most recent end-to-end run installed the chart on a managed Kubernetes cluster in fr-par, pulled the image from a private registry, ran a capsule through create → execute → destroy, and produced a signed AuditProof whose 8-step chain and signature verify offline. Verification against a published trust-chain manifest is not yet available for self-hosted signing keys, as described above.


Next

Deployment questions: [email protected].