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 (
nanorixchart, version0.3.0or later), and - a pull path plus credentials for the
nanorix-apiimage, 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:
Then resolve the chart's bundled dependencies once, from a machine with network access:
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:
On a cluster enforcing restricted Pod Security Standards, grant the namespace an exception:
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:
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:
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¶
- Quickstart — the same lifecycle against the hosted service
- Verification — every way to verify an AuditProof, including the manual algorithm
- AuditProof Specification — the complete artifact format
- Security Architecture — what the isolation boundary is made of
- API Reference — every endpoint your install serves
Deployment questions: [email protected].