> ## Documentation Index
> Fetch the complete documentation index at: https://docs.neuraltrust.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Vanilla Kubernetes — Self-hosted deployment

> Complete walkthrough for deploying the full NeuralTrust Platform (Control Plane + Data Plane + TrustGate + Firewall) on any conformant Kubernetes cluster. Covers prerequisites, full values overlay, ingress for every component, cert-manager TLS, air-gapped installs, and verification.

This guide walks you end-to-end through a **fully self-hosted deployment** on any Kubernetes 1.24+ cluster — Control Plane API, UI, and Scheduler run in your cluster alongside the Data Plane, TrustGate, and Firewall.

This is the recommended path for **on-prem, bare metal, air-gapped, and sovereign-cloud** deployments.

For the SaaS-hosted Control Plane alternative, see [Vanilla Kubernetes hybrid](./hybrid).

## What you'll end up with

| Component                             | Location                   | Replicas |
| ------------------------------------- | -------------------------- | -------- |
| Control Plane API, UI, Scheduler      | Your cluster               | 2, 2, 1  |
| Data Plane API, worker, Kafka Connect | Your cluster               | 2, 1, 1  |
| TrustGate admin / gateway / actions   | Your cluster               | 2 each   |
| Firewall gateway + 5 workers          | Your cluster               | 2 + 5    |
| ClickHouse, Kafka, PostgreSQL, Redis  | Your cluster (or external) | 1 each   |

Sizing baseline: \~21–23 vCPU / 45–50 GiB RAM / 80 GiB PVC. See [Image catalog](../images).

## Prerequisites

| Resource                                | Recommended                                                                                        |
| --------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Kubernetes version                      | 1.24+                                                                                              |
| CPU pool                                | **≥ 5 × (8 vCPU / 32 GiB)** for HA. Drop to 4 if Firewall workers run on GPU nodes.                |
| GPU pool *(optional, for GPU Firewall)* | 4 vCPU / 16 GiB + 1 × NVIDIA GPU per node — 5 nodes (one per default Firewall worker)              |
| Sizing baseline                         | \~23.1 vCPU / 61.8 GiB requests / 80 GiB PVC (defaults, CPU Firewall)                              |
| Storage                                 | Default `StorageClass` with `ReadWriteOnce` PVs (SSD-backed recommended for ClickHouse + Postgres) |
| Ingress                                 | NGINX, Traefik, HAProxy, or any conformant controller                                              |
| TLS                                     | cert-manager with Let's Encrypt or internal CA                                                     |
| DNS                                     | A control over a base domain (e.g. `platform.example.com`)                                         |
| Image pull                              | `gcr-keys.json` from NeuralTrust **OR** a mirrored internal registry for air-gapped                |

## Step 1 — Cluster prep

Same as hybrid — see [Vanilla Kubernetes hybrid › Step 1](./hybrid#step-1-cluster-prep). Add cluster components:

* Ingress controller (NGINX/Traefik/HAProxy).
* cert-manager (or your own cert workflow).
* Storage class with `ReadWriteOnce`.
* (Bare metal) MetalLB or an external LB.
* (GPU Firewall) NVIDIA device plugin.

## Step 2 — Namespace and image pull secret

```bash theme={null}
kubectl create namespace neuraltrust

kubectl create secret docker-registry gcr-secret \
  --docker-server=europe-west1-docker.pkg.dev \
  --docker-username=_json_key \
  --docker-password="$(cat path/to/gcr-keys.json)" \
  --docker-email=admin@neuraltrust.ai \
  -n neuraltrust
```

For air-gapped installs, mirror the chart images first (see [Air-gapped](#air-gapped-installs)).

## Step 3 — Write your values overlay

Save as `my-values.yaml`:

```yaml theme={null}
# Self-hosted deployment on vanilla Kubernetes
global:
  platform: "kubernetes"
  domain: "platform.example.com"
  storageClass: "longhorn"               # or your cluster default
  autoGenerateSecrets: true

# Control Plane in your cluster
neuraltrust-control-plane:
  controlPlane:
    enabled: true                         # ← key difference from hybrid
    components:
      api:
        enabled: true
        ingress:
          enabled: true
          className: "nginx"
          annotations: &nginx
            cert-manager.io/cluster-issuer: "letsencrypt-prod"
            nginx.ingress.kubernetes.io/ssl-redirect: "true"
      app:
        enabled: true
        ingress:
          enabled: true
          className: "nginx"
          annotations: *nginx
      scheduler:
        enabled: true
        ingress:
          enabled: true
          className: "nginx"
          annotations: *nginx
  infrastructure:
    postgresql:
      deploy: true

# Data Plane
neuraltrust-data-plane:
  dataPlane:
    enabled: true
    components:
      api:
        ingress:
          enabled: true
          className: "nginx"
          annotations: *nginx

# TrustGate
trustgate:
  enabled: true
  global:
    env:
      SERVER_BASE_DOMAIN: "platform.example.com"
  ingress:
    controlPlane:
      className: "nginx"
      annotations: *nginx
    dataPlane:
      className: "nginx"
      annotations: *nginx
    actions:
      className: "nginx"
      annotations: *nginx

# Firewall
neuraltrust-firewall:
  firewall:
    enabled: true

# Infrastructure
infrastructure:
  clickhouse:
    deploy: true
    persistence:
      storageClass: "longhorn"
      size: 200Gi
  kafka:
    deploy: true
```

### Using external infrastructure

```yaml theme={null}
neuraltrust-control-plane:
  infrastructure:
    postgresql:
      deploy: false
  controlPlane:
    components:
      postgresql:
        secrets:
          host: "<external-postgres>"
          port: "5432"
          user: "neuraltrust"
          password: ""
          database: "neuraltrust"

infrastructure:
  clickhouse:
    deploy: false
    external:
      host: "your-tenant.clickhouse.cloud"
      port: "8443"
      user: "neuraltrust"
      password: ""
      database: "neuraltrust"
  kafka:
    deploy: false
    external:
      bootstrapServers: "<bootstrap>:9092"
```

<Note>
  For **ClickHouse Cloud**, see the [native-port caveat](../feature-flags#required-clickhouse-cloud-caveat). For external Kafka with SASL, see [Authentication for external Kafka](../feature-flags#authentication-for-external-kafka).
</Note>

## Step 4 — Install

```bash theme={null}
helm upgrade --install neuraltrust-platform \
  oci://europe-west1-docker.pkg.dev/neuraltrust-app-prod/helm-charts/neuraltrust-platform \
  --version <VERSION> \
  --namespace neuraltrust \
  -f my-values.yaml

kubectl get pods -n neuraltrust -w
```

## Step 5 — DNS

Get the ingress controller's external IP / hostname and add A / CNAME records:

| Host                                  | Component               |
| ------------------------------------- | ----------------------- |
| `app.platform.example.com`            | Control Plane UI        |
| `api.platform.example.com`            | Control Plane API       |
| `scheduler.platform.example.com`      | Control Plane Scheduler |
| `data-plane-api.platform.example.com` | Data Plane API          |
| `admin.platform.example.com`          | TrustGate admin         |
| `gateway.platform.example.com`        | TrustGate proxy         |
| `actions.platform.example.com`        | TrustGate actions       |

## Step 6 — First login to the Control Plane

```bash theme={null}
kubectl logs -n neuraltrust deploy/control-plane-app -c init-db | grep -i bootstrap
```

Sign in at `https://app.platform.example.com`, configure SSO ([Platform › SSO](/platform/sso)), rotate the bootstrap admin password, configure LLM providers and policies.

## Step 7 — Send traffic through TrustGate

Point your AI applications at `https://gateway.platform.example.com`.

## Verification

```bash theme={null}
kubectl get pods -n neuraltrust
kubectl get ingress -n neuraltrust -o wide
kubectl get certificate -n neuraltrust

curl https://api.platform.example.com/health
curl https://app.platform.example.com
curl https://data-plane-api.platform.example.com/health
curl https://gateway.platform.example.com/__health
```

## Upgrading

```bash theme={null}
helm upgrade neuraltrust-platform \
  oci://europe-west1-docker.pkg.dev/neuraltrust-app-prod/helm-charts/neuraltrust-platform \
  --version <NEW_VERSION> \
  --namespace neuraltrust \
  -f my-values.yaml
```

Watch the `init-db` init container on the new CP UI pod for Prisma migration errors.

## Air-gapped installs

For fully disconnected clusters:

### 1. Mirror images

```bash theme={null}
# Get the full image list for your values
helm template neuraltrust-platform \
  oci://europe-west1-docker.pkg.dev/neuraltrust-app-prod/helm-charts/neuraltrust-platform \
  --version <VERSION> \
  -f my-values.yaml \
  | yq '.. | select(has("image")) | .image' \
  | sort -u
```

Mirror each image to your internal registry using `crane`, `skopeo`, or `docker pull/push`.

### 2. Point the chart at your mirror

```yaml theme={null}
global:
  imageRegistry: "registry.internal.example.com/neuraltrust"
```

The chart strips the default GCP prefix and prepends yours.

### 3. Forward proxy (if applicable)

```yaml theme={null}
global:
  proxy:
    enabled: true
    httpProxy: "http://proxy.corp.example:3128"
    httpsProxy: "http://proxy.corp.example:3128"
    noProxy: "localhost,127.0.0.1,.cluster.local,.svc"
```

### 4. Internal CA for TLS

Bring your own certificates instead of Let's Encrypt:

```yaml theme={null}
neuraltrust-data-plane:
  dataPlane:
    components:
      api:
        ingress:
          tls:
            secretName: "wildcard-platform-example-com"
```

Pre-create the Secret containing the CA-signed certificate.

### 5. Firewall model weights

The Firewall workers fetch models from Hugging Face on first start. For air-gapped:

* Pre-bake models into a custom Firewall image, OR
* Mirror `huggingface.co` internally and set the Firewall `HUGGINGFACE_HUB_ENDPOINT`, OR
* Provide a `HUGGINGFACE_TOKEN` and allow egress just to Hugging Face.

See [Firewall deployment › Air-gapped](../firewall) for details.

## Migration to hybrid

```yaml theme={null}
neuraltrust-control-plane:
  controlPlane:
    enabled: false
```

Enroll the existing Data Plane with NeuralTrust SaaS — see [Vanilla Kubernetes hybrid › Step 6](./hybrid#step-6-enroll-the-data-plane-with-neuraltrust-saas).

## Troubleshooting

| Symptom                         | Likely cause                                 | Fix                                                 |
| ------------------------------- | -------------------------------------------- | --------------------------------------------------- |
| CP UI blank                     | API URL wrong                                | Verify `api.<domain>` ingress and config            |
| Login fails                     | DB migration failed                          | `kubectl logs -c init-db` on CP UI pod              |
| Scheduler not running jobs      | Can't reach Data Plane API                   | Verify `data-plane-api.<domain>` and TLS            |
| `PVC` stuck `Pending`           | No default storage class                     | Mark one default and re-apply                       |
| cert-manager challenge stuck    | DNS not resolving or unreachable for HTTP-01 | `kubectl describe challenge`                        |
| `ImagePullBackOff` (air-gapped) | Registry mirror missing images               | Re-run image mirror; confirm `global.imageRegistry` |

## Related guides

* [Hybrid deployment on vanilla Kubernetes](./hybrid) — Control Plane on SaaS
* [Vanilla Kubernetes overview](./overview) — cluster prerequisites
* [Deployment models](../deployment-models) — hybrid vs self-hosted comparison
* [Image catalog](../images) — what runs in self-hosted mode, plus mirroring
* [Secrets management](../secrets) — auto-generation, External Secrets Operator
* [Firewall deployment](../firewall) — GPU workers, air-gapped configuration
* [Configuration scenarios](../configuration) — external infrastructure modes
