> ## 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.

# Upgrades and rollback

> Upgrade the control plane, roll out new workspace images, and roll back safely.

An upgrade has two distinct parts: the **control plane**, which Helm manages,
and **existing workspace Deployments**, which Helm does not. Workspace pods are
created by the controller at runtime, so a chart upgrade alone does not change
them.

## Before you upgrade

<Steps>
  <Step title="Back up">
    Take a database backup and confirm you hold `SETTINGS_ENCRYPTION_KEY` and
    `BETTER_AUTH_SECRET`. See [Backup and restore](/operate/backup-restore).
  </Step>

  <Step title="Read the release notes">
    Check for breaking changes and required values between your current version
    and the target.
  </Step>

  <Step title="Capture your current values">
    This preserves settings that are not in your values file:

    ```bash theme={null}
    helm get values r5d-chat -n r5d-chat -o yaml > /tmp/r5d-chat-values.yaml
    ```
  </Step>
</Steps>

## Upgrade the control plane

<Steps>
  <Step title="Update the repository">
    ```bash theme={null}
    helm repo update r5d-chat
    helm search repo r5d-chat --versions | head
    ```
  </Step>

  <Step title="Render and dry-run the exact values">
    ```bash theme={null}
    helm template r5d-chat r5d-chat/r5d-chat --version 0.2.0 \
      --namespace r5d-chat -f /tmp/r5d-chat-values.yaml \
      > /tmp/r5d-chat-rendered.yaml
    kubectl apply --dry-run=server -f /tmp/r5d-chat-rendered.yaml
    ```
  </Step>

  <Step title="Upgrade">
    ```bash theme={null}
    helm upgrade r5d-chat r5d-chat/r5d-chat --version 0.2.0 \
      --namespace r5d-chat -f /tmp/r5d-chat-values.yaml \
      --atomic --timeout 10m

    kubectl -n r5d-chat rollout status deployment/r5d-chat-r5d-chat --timeout=5m
    ```

    `--atomic` rolls the release back automatically if the upgrade fails.
  </Step>

  <Step title="Verify">
    ```bash theme={null}
    kubectl -n r5d-chat get pods
    kubectl -n r5d-chat logs deployment/r5d-chat-r5d-chat --tail=100
    curl -fsS https://chat.example.com/api/health
    ```

    Then sign in, confirm provider settings decrypt correctly, open an existing
    conversation, and start a workspace.
  </Step>
</Steps>

<Note>
  The control plane uses the `Recreate` strategy to stay within namespace pod
  quotas, so expect a brief interruption rather than a seamless rolling update.
</Note>

## Database migrations

Migrations run in an init container before the new application container
starts, guarded by a PostgreSQL advisory lock so concurrent rollouts cannot
race.

<Warning>
  A Helm rollback does not reverse a database migration. If a release includes a
  schema change, rolling back the chart leaves the newer schema in place. The
  application must tolerate that, or you must restore the database from backup.
  Test upgrades in a staging namespace first.
</Warning>

## Roll out existing workspaces

New workspaces use the new image automatically. Existing Deployments keep their
current image until you update them explicitly.

<Steps>
  <Step title="Determine the new workspace image">
    ```bash theme={null}
    WORKSPACE_IMAGE="$(kubectl -n r5d-chat get deployment r5d-chat-r5d-chat \
      -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="WORKSPACE_IMAGE")].value}')"
    echo "$WORKSPACE_IMAGE"
    ```
  </Step>

  <Step title="Update every workspace Deployment">
    This replaces only the container image and preserves each Deployment's
    current replica count, so idle workspaces stay scaled to zero.

    ```bash theme={null}
    kubectl -n r5d-chat get deployment \
      -l app.kubernetes.io/name=r5d-chat-workspace -o name |
    while read -r deployment; do
      kubectl -n r5d-chat set image "$deployment" workspace="$WORKSPACE_IMAGE"
    done
    ```
  </Step>

  <Step title="Wait only for the running ones">
    ```bash theme={null}
    kubectl -n r5d-chat get deployment \
      -l app.kubernetes.io/name=r5d-chat-workspace -o name |
    while read -r deployment; do
      replicas="$(kubectl -n r5d-chat get "$deployment" -o jsonpath='{.spec.replicas}')"
      if [ "${replicas:-0}" -gt 0 ]; then
        kubectl -n r5d-chat rollout status "$deployment" --timeout=10m
      fi
    done
    ```
  </Step>
</Steps>

<Warning>
  Workspace Deployments use `Recreate` because each home volume is
  `ReadWriteOnce`. Active users see a short interruption while their pod
  restarts. The PVC and its contents are preserved. Never delete workspace PVCs
  as part of an image update.
</Warning>

## Roll back

<Steps>
  <Step title="Find the revision">
    ```bash theme={null}
    helm history r5d-chat -n r5d-chat
    ```
  </Step>

  <Step title="Roll back the release">
    ```bash theme={null}
    helm rollback r5d-chat <REVISION> -n r5d-chat --wait --timeout 10m
    ```
  </Step>

  <Step title="Restore the previous workspace image">
    Only if you had already updated existing workspaces:

    ```bash theme={null}
    PREVIOUS=ghcr.io/ricsam/r5d-chat-workspace:0.1.0
    kubectl -n r5d-chat get deployment \
      -l app.kubernetes.io/name=r5d-chat-workspace -o name |
    while read -r deployment; do
      kubectl -n r5d-chat set image "$deployment" workspace="$PREVIOUS"
    done
    ```
  </Step>

  <Step title="Verify the schema is compatible">
    If the failed upgrade applied a migration, confirm the older application
    still works against the current schema. Restore the database from backup if
    it does not.
  </Step>
</Steps>

## Upgrade checklist

<AccordionGroup>
  <Accordion title="Pre-upgrade">
    * Database backup taken and verified
    * `SETTINGS_ENCRYPTION_KEY` and `BETTER_AUTH_SECRET` available
    * Current values exported
    * Release notes reviewed
    * Rendered manifests pass `kubectl apply --dry-run=server`
  </Accordion>

  <Accordion title="Post-upgrade">
    * `/api/health` returns `{"ok":true}`
    * Control-plane logs are free of startup errors
    * Login works, including SSO if enabled
    * Provider settings still decrypt
    * An existing conversation loads
    * A workspace starts and runs a command
    * Existing workspace Deployments were rolled out if needed
  </Accordion>
</AccordionGroup>
