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

# Deployment Guide

> Install Bud-Stack from the OCI Helm chart against managed (external) databases and object storage

This guide installs Bud-Stack from the published OCI Helm chart and points it
at managed services you already operate (e.g. Azure Database for PostgreSQL,
Cosmos DB for MongoDB, Azure Cache for Redis, Azure Blob via an S3-compatible
gateway, an external Kafka and ClickHouse). It lists **only the configuration
that is mandatory** for a new setup — defaults are fine for everything else.

<Note>
  If you want an all-in-one cluster with bundled Postgres/Mongo/Valkey/SeaweedFS,
  follow the [Installation Guide](/developer-docs/installation) instead.
</Note>

## Prerequisites

| Requirement                       | Notes                                                                                                                                                                                                                                                                                                                            |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Kubernetes 1.25+                  | With a default `StorageClass` for PVCs                                                                                                                                                                                                                                                                                           |
| `kubectl`, `helm` 3.8+            | Helm must support OCI registries                                                                                                                                                                                                                                                                                                 |
| Dapr 1.16+ installed cluster-wide | Service mesh used by every backend pod                                                                                                                                                                                                                                                                                           |
| Traefik ingress controller        | Chart Ingresses are pinned to `ingressClassName: traefik`.                                                                                                                                                                                                                                                                       |
| TLS strategy                      | `ingress.https`: `disabled` (plain HTTP), `internal` (Traefik/cert-manager in-cluster certs), or `external` (terminated upstream by a LB/WAF).                                                                                                                                                                                   |
| OIDC identity provider            | Any OIDC-compliant IdP works (Keycloak, Auth0, Okta, Azure AD, Google, etc.). Authorization is owned by budapp's Postgres-backed PDP — the IdP only needs to issue ID tokens. The sibling `keycloak` chart (also published at `oci://registry.bud.studio/charts/keycloak`) is a convenient default if you don't already run one. |
| Registry credentials              | Robot account for `registry.bud.studio` (request from `sinan@bud.studio`)                                                                                                                                                                                                                                                        |

## Step 0 — Log in to the chart registry

```bash theme={null}
helm registry login registry.bud.studio -u 'robot$yourname'
# password prompt
```

Quote the username so the shell does not expand the `$`.

## Step 1 — Pull the default values

```bash theme={null}
helm show values oci://registry.bud.studio/charts/bud --version 0.14.5 > values.yaml
```

## Step 2 — Start your secrets file

Pull the chart so you can inspect the secret files it ships:

```bash theme={null}
helm pull oci://registry.bud.studio/charts/bud --version 0.14.5 --untar
```

`bud/secrets.bud.yaml` enumerates the **full set of secret key paths** the
platform needs (`externalServices.*`, `microservices.*`, `daprExtra.crypto`,
`novu.*`); `secrets.demo.yaml` is a minimal example that carries only the
registry pull credentials. Both shipped files are **SOPS-encrypted** (values
appear as `ENC[...]`), so they are not drop-in templates — use
`secrets.bud.yaml` for the key *structure* and supply your own plaintext values
in a separate `secrets.yaml`, following the references in Steps 3–4. Keep
`secrets.yaml` out of version control (or SOPS-encrypt it).

## Step 3 — Wire up external services

All managed services are configured under `externalServices` in `values.yaml`
and the matching credential blocks live in `secrets.yaml`. Provision the
resources below first, then fill in the values.

### PostgreSQL

Create one database per backend service, each with its own user.

| Configure in `values.yaml`                             | Configure in `secrets.yaml`                            |
| ------------------------------------------------------ | ------------------------------------------------------ |
| `externalServices.postgresql.host`                     | `externalServices.postgresql.databases.<svc>.password` |
| `externalServices.postgresql.port`                     | (one entry per service)                                |
| `externalServices.postgresql.databases.<svc>.name`     |                                                        |
| `externalServices.postgresql.databases.<svc>.username` |                                                        |

Required databases: `budapp`, `budcluster`, `budmetrics`, `budmodel`, `budsim`,
`budeval`, `buddoc`, `budprompt`, `budask`, `mcpgateway`, `budpipeline`,
`budcodeinterpreter`.

### MongoDB (Novu)

| Configure in `values.yaml`                | Configure in `secrets.yaml`              |
| ----------------------------------------- | ---------------------------------------- |
| `externalServices.mongodb.endpoint`       | `externalServices.mongodb.auth.password` |
| `externalServices.mongodb.databases.novu` | `novu.externalDatabase.password`         |
| `novu.externalDatabase.host`              |                                          |
| `novu.externalDatabase.database`          |                                          |
| `novu.externalDatabase.username`          |                                          |

### Redis / Valkey

A single Redis instance, multiplexed by logical DB index.

| Configure in `values.yaml`                                         | Configure in `secrets.yaml`        |
| ------------------------------------------------------------------ | ---------------------------------- |
| `externalServices.valkey.host`                                     | `externalServices.valkey.password` |
| `externalServices.valkey.port`                                     |                                    |
| `externalServices.valkey.databases.*` (numeric index per consumer) |                                    |
| `novu.externalRedis.host`                                          |                                    |

The `novu.externalRedis` block references the same instance; only the host is
restated — the password is shared from `externalServices.valkey.password`
via the `<release>-novu-externalredis` Secret.

### S3-compatible object storage

Works with SeaweedFS (the bundled default), AWS S3, Azure Blob via an S3
gateway, or any other S3-compatible store. Create the buckets ahead of time.

| Configure in `values.yaml`                      | Configure in `secrets.yaml`          |
| ----------------------------------------------- | ------------------------------------ |
| `externalServices.s3.endpoint`                  | `externalServices.s3.auth.accessKey` |
| `externalServices.s3.secure` (`true` for HTTPS) | `externalServices.s3.auth.secretKey` |
| `externalServices.s3.buckets.modelRegistry`     |                                      |
| `externalServices.s3.buckets.modelInfo`         |                                      |
| `externalServices.s3.buckets.novu`              |                                      |
| `novu.externalS3.bucketName`                    |                                      |

### ClickHouse

| Configure in `values.yaml`                                                               | Configure in `secrets.yaml`                 |
| ---------------------------------------------------------------------------------------- | ------------------------------------------- |
| `externalServices.clickhouse.host`                                                       | `externalServices.clickhouse.auth.password` |
| `externalServices.clickhouse.port`                                                       |                                             |
| `externalServices.clickhouse.databases.budmetrics` (**must stay `metrics`** — hardcoded) |                                             |
| `externalServices.clickhouse.databases.budeval`                                          |                                             |
| `externalServices.clickhouse.databases.budgateway`                                       |                                             |
|                                                                                          | `externalServices.clickhouse.auth.username` |

### Kafka

| Configure in `values.yaml`                 | Configure in `secrets.yaml`            |
| ------------------------------------------ | -------------------------------------- |
| `externalServices.kafka.endpoint`          | `externalServices.kafka.auth.password` |
| `externalServices.kafka.topics.pubsub`     |                                        |
| `externalServices.kafka.topics.budgateway` |                                        |
|                                            | `externalServices.kafka.auth.username` |

### OIDC identity provider

Any OIDC-compliant IdP is supported — budapp validates ID tokens via
OIDC discovery and stores all authorization state in its own Postgres PDP.
If you don't already operate one, the sibling `keycloak` chart
(`oci://registry.bud.studio/charts/keycloak`) gives you a working Keycloak in one
install.

You must pre-create the OAuth clients that the redirect-flow uses
(`budadmin-web`, `budcustomer-web`, `budplayground-web`, plus the
`mcpgateway` client) in your IdP and copy the client secrets into
`secrets.yaml` (see [Application secrets](#step-4-mdash-application-secrets-amp-cluster-config)).

| Configure in `values.yaml`                                               | Configure in `secrets.yaml`                       |
| ------------------------------------------------------------------------ | ------------------------------------------------- |
| `externalServices.oidc.url` (full issuer URL, incl. the realm segment)   | `externalServices.oidc.clients.mcpgateway.secret` |
| `externalServices.oidc.clients.mcpgateway.id` (the mcpgateway client id) |                                                   |

<Note>
  **The issuer is set directly on `externalServices.oidc.url`.** As of chart 0.14.0
  budapp uses `externalServices.oidc.url` as its OIDC issuer verbatim — for a
  default Keycloak that is `https://<auth-host>/realms/bud-keycloak` (the realm
  segment is part of the URL). It must match the keycloak chart's `ingress.host`.
  The `oidc.clients.mcpgateway.*` keys configure the **mcpgateway** OAuth client
  specifically; they do not change budapp's issuer.
</Note>

<Note>
  **Using a non-Keycloak IdP.** Any OIDC-compliant IdP works, but the defaults are
  Keycloak-shaped. To point budapp at Auth0 / Authentik / Cognito / Azure AD,
  override these in `microservices.budapp.env` rather than just setting `.url`:
  `OIDC_ISSUER` (your provider's full issuer URL) and `OIDC_ROLES_CLAIM_PATH`
  (where the IdP encodes roles — Keycloak `realm_access.roles`, Auth0
  `https://yourapp/roles`, Cognito `cognito:groups`). The legacy
  `keycloak.auth.adminUser` / `adminPassword` fields are only consulted when you
  run the bundled Keycloak chart.
</Note>

## Step 4 — Application secrets & cluster config

These are not wired to external services but are required for a new install.
All paths below live in `secrets.yaml` unless noted.

### Registry pull credentials

```yaml theme={null}
registries:
  registry.bud.studio:
    username: robot$yourname
    password: <token>
```

### Per-service secrets you must change

| Path                                                                    | Why                                                                                                                      |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `microservices.global.env.SUPER_USER_EMAIL`                             | First admin login                                                                                                        |
| `microservices.global.env.SUPER_USER_PASSWORD`                          | First admin login                                                                                                        |
| `microservices.budapp.env.AES_KEY_HEX`                                  | Encrypts stored credentials                                                                                              |
| `microservices.global.env.PASSWORD_SALT`                                | Password hashing salt                                                                                                    |
| `microservices.budapp.redirectFlow.sessionSecret`                       | 64-byte HMAC for the BFF session cookie                                                                                  |
| `microservices.budapp.redirectFlow.defaultClientSecret`                 | Must match the Keycloak client `budadmin-web`                                                                            |
| `microservices.budapp.redirectFlow.clientsByHost.*.clientSecret`        | One per frontend host (`budadmin`, `budcustomer`, `budplayground`) — all must exist as confidential clients in the realm |
| `microservices.budnotify.env.STORE_ENCRYPTION_KEY`, `JWT_SECRET`        | Novu wrapper auth                                                                                                        |
| `microservices.mcpgateway.env.*`                                        | MCP gateway admin + JWT + auth-encryption secrets                                                                        |
| `microservices.rsaKeys.privateKey` / `publicKey` / `privateKeyPassword` | Inter-service RSA pair (regen with `openssl genrsa -aes256 -out private_key.pem 2048`)                                   |
| `daprExtra.crypto.symmetricKey` / `asymmetricKey`                       | Dapr crypto-component keys for cluster credential encryption                                                             |
| `novu.store.encryption-key`, `novuExtra.password`                       | Novu admin                                                                                                               |

### Cluster-level config in `values.yaml`

| Path                                 | Purpose                                                                           |
| ------------------------------------ | --------------------------------------------------------------------------------- |
| `global.ingress.hosts.root`          | Base public hostname (e.g. `bud.yourdomain.com`). All sub-hosts derive from this. |
| `ingress.https`                      | `external` (cert-manager / cloud LB), `internal` (in-cluster CA), or `disabled`   |
| `storage.budmodelRegistry.className` | StorageClass for the model registry PVC (e.g. `managed-csi` on Azure)             |
| `storage.budmodelRegistry.size`      | Size the registry PVC for the models you plan to host                             |

## Step 5 — Install

```bash theme={null}
helm install bud oci://registry.bud.studio/charts/bud \
  --version 0.14.5 \
  --namespace bud --create-namespace \
  -f values.yaml \
  -f secrets.yaml
```

## Step 6 — Verify

```bash theme={null}
kubectl -n bud get pods
kubectl -n bud get ingress
```

All pods should reach `Running` within \~5–10 minutes. Backend pods run a
Dapr sidecar so expect `2/2 READY` for most of them.

## Common pitfalls

* **`budmetrics` database name must be `metrics`**;
* **Redirect-flow clients must exist in the realm** before the budapp pod
  boots, otherwise the login flow returns `invalid_client`.
* **`registries.registry.bud.studio` credentials** are required even for
  airgapped clusters that already mirrored the images — the chart still
  creates the imagePullSecret referenced by every Deployment.

## Next steps

<CardGroup cols={2}>
  <Card title="Helm Configuration Reference" icon="gear" href="/developer-docs/helm-configuration">
    Full list of values and overrides.
  </Card>

  <Card title="Installation Guide" icon="download" href="/developer-docs/installation">
    Full in-cluster install with bundled dependency charts (ArgoCD).
  </Card>
</CardGroup>
