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

# Install with Helm

> Install a self-hosted r5d.chat from the public Helm repository at charts.r5d.dev or from the GHCR OCI registry.

This page installs the **self-hosted** edition into your own cluster. If you
would rather not operate it, the [managed service](/editions) runs the same
application at r5d.chat.

The chart and both container images are public, so no registry credentials and
no account are required. The chart is published two ways on every release; both
serve the identical package.

<Tabs>
  <Tab title="Helm repository">
    A classic HTTP Helm repository at `charts.r5d.dev`. It supports
    `helm search` and version discovery.

    ```bash theme={null}
    helm repo add r5d-chat https://charts.r5d.dev
    helm repo update
    helm search repo r5d-chat --versions
    ```
  </Tab>

  <Tab title="OCI registry">
    The same chart as an OCI artifact in GHCR. No repository to add, but you
    must know the version you want.

    ```bash theme={null}
    helm show chart oci://ghcr.io/ricsam/charts/r5d-chat --version 0.1.0
    ```
  </Tab>
</Tabs>

## Install

<Steps>
  <Step title="Create the application Secret">
    Production installations should use an existing Secret rather than putting
    credentials in a values file. It must contain:

    | Key                       | Required        | Purpose                                       |
    | ------------------------- | --------------- | --------------------------------------------- |
    | `DATABASE_URL`            | Yes             | PostgreSQL connection string                  |
    | `BETTER_AUTH_SECRET`      | Yes             | Session signing key, 32+ characters           |
    | `SETTINGS_ENCRYPTION_KEY` | Yes             | Encrypts provider credentials, 32+ characters |
    | `SETUP_CLAIM_TOKEN`       | Recommended     | Gates first-administrator setup               |
    | `POSTGRES_PASSWORD`       | With bundled DB | Password for the bundled StatefulSet          |
    | `AWR_BASE_URL`            | Optional        | Enables web research tools                    |
    | `AWR_API_KEY`             | Optional        | Enables web research tools                    |

    See the [Quickstart](/quickstart#create-the-namespace-and-secrets) for a
    generation script.
  </Step>

  <Step title="Write your values file">
    ```yaml theme={null}
    publicUrl: https://chat.example.com
    existingSecret: r5d-chat-secrets

    ingress:
      enabled: true
      className: traefik
      host: chat.example.com

    postgresql:
      storageClass: rook-ceph-block

    workspace:
      storageClass: rook-ceph-block
      storageSize: 10Gi
    ```

    To publish through a Cloudflare Tunnel instead of an inbound Ingress, create
    the connector token Secret and enable `cloudflared`. Configure the tunnel's
    public hostname in Cloudflare to point at the control-plane Service,
    `http://r5d-chat-r5d-chat.r5d-chat.svc.cluster.local:80`.

    ```yaml theme={null}
    cloudflared:
      enabled: true
      tokenSecret:
        name: cloudflared-tunnel
        key: token
    ```

    Every available key is documented in [Chart values](/install/values).
  </Step>

  <Step title="Validate the exact rendered output">
    Always render and server-dry-run the values you are about to apply.

    ```bash theme={null}
    helm template r5d-chat r5d-chat/r5d-chat \
      --namespace r5d-chat -f values.yaml > /tmp/r5d-chat.yaml
    kubectl apply --dry-run=server -f /tmp/r5d-chat.yaml
    ```
  </Step>

  <Step title="Install">
    <CodeGroup>
      ```bash Helm repository theme={null}
      helm install r5d-chat r5d-chat/r5d-chat \
        --namespace r5d-chat --create-namespace \
        -f values.yaml --wait --timeout 10m
      ```

      ```bash OCI registry theme={null}
      helm install r5d-chat oci://ghcr.io/ricsam/charts/r5d-chat \
        --version 0.1.0 \
        --namespace r5d-chat --create-namespace \
        -f values.yaml --wait --timeout 10m
      ```
    </CodeGroup>
  </Step>

  <Step title="Verify">
    ```bash theme={null}
    kubectl -n r5d-chat rollout status deployment/r5d-chat-r5d-chat
    kubectl -n r5d-chat get pods,pvc,ingress
    curl -fsS https://chat.example.com/api/health
    ```
  </Step>
</Steps>

## Image versions

Chart versions and image versions are released together. When `image.tag` and
`workspaceImage.tag` are empty, the chart uses its own `appVersion`, so
installing chart `0.3.0` runs the images published for `0.3.0`.

Override the tags only when you deliberately want a different build:

```bash theme={null}
helm upgrade --install r5d-chat r5d-chat/r5d-chat \
  --namespace r5d-chat -f values.yaml \
  --set-string image.tag=deploy-20250219-abc1234 \
  --set-string workspaceImage.tag=deploy-20250219-abc1234
```

<Card title="Publishing pipeline" icon="box" href="/reference/releases">
  How images and chart versions are built and published.
</Card>

## Private registries

The published images are public, so a pull secret is **not** required for a
normal installation. This section applies only when you mirror the images into
a private registry of your own.

Create a pull secret and reference it in two places: the control-plane pods use
`imagePullSecrets`, and dynamically created workspace pods use
`workspace.imagePullSecret`.

```bash theme={null}
kubectl -n r5d-chat create secret docker-registry ghcr-pull \
  --docker-server=ghcr.io \
  --docker-username="<github-username>" \
  --docker-password="<token-with-read:packages>"
```

```yaml theme={null}
imagePullSecrets:
  - name: ghcr-pull
workspace:
  imagePullSecret: ghcr-pull
```

<Warning>
  Setting only `imagePullSecrets` is a common mistake. The control plane will
  start correctly while every user workspace fails with `ImagePullBackOff`,
  because workspace pods are created by the controller at runtime and are not
  part of the chart's rendered output.
</Warning>

## External PostgreSQL

The bundled database is convenient for evaluation. For production, run
PostgreSQL 17 separately:

```yaml theme={null}
postgresql:
  enabled: false
```

Put the external connection string in the `DATABASE_URL` key of your Secret and
make sure network policy allows the control plane to reach it.

## What the chart creates

| Resource                          | Notes                                                               |
| --------------------------------- | ------------------------------------------------------------------- |
| Deployment                        | Control plane, `Recreate` strategy, migrations in an init container |
| Service                           | ClusterIP on port 80 targeting container port 3000                  |
| Ingress                           | Only when `ingress.enabled` is true                                 |
| Secret                            | Only when `existingSecret` is empty                                 |
| ServiceAccount, Role, RoleBinding | Namespace-scoped permissions to reconcile workspaces                |
| StatefulSet + headless Service    | Only when `postgresql.enabled` is true                              |
| NetworkPolicy                     | Two policies, for the control plane and for workspaces              |

<Note>
  The control plane uses `Recreate` rather than a rolling update to avoid surge
  pods, which keeps it within tight namespace pod quotas. Expect a brief
  interruption during upgrades.
</Note>

## Uninstall

`helm uninstall` removes chart-managed compute and routing. It is not a tenant
data purge.

```bash theme={null}
kubectl -n r5d-chat get pvc
helm uninstall r5d-chat --namespace r5d-chat
kubectl -n r5d-chat get all,pvc,secret
```

Workspace PVCs carry keep semantics and survive. Delete them only after
confirming your backup and retention decision, as covered in
[Backup and restore](/operate/backup-restore).
