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

# Configure persistent storage

> Configure PVCs for Gitway and bundled datastores, choose a StorageClass or existing claim, and verify persistence and retention.

* **Deployment model**: On-premises
* **Configuration reference**: Qodo Helm chart `7.22.0`.

Prepare persistent storage before installing Qodo. Your platform team owns
the storage driver, StorageClasses, backing volumes, encryption, and backups.
The chart requests storage through PersistentVolumeClaims (PVCs) and mounts
it into the services that need local data.

This page covers the Qodo umbrella chart. The values below target a fresh
installation. For an existing installation, plan storage changes using the
retention, resize, and recovery guidance below.

## Identify the required claims

The standard configuration uses an external PostgreSQL server and creates
three claims. Choosing bundled PostgreSQL adds a fourth. These are the
initial sizes and names for release `qodo` with one replica of each datastore:

| Service | PVC name | Initial size | Mounted path |
| - | - | - | - |
| Gitway repository store | `qodo-gitway-repo-store` | `50Gi` | `/repo-store` |
| RabbitMQ broker | `data-qodo-rabbitmq-0` | `8Gi` | `/var/lib/rabbitmq` |
| Redis append-only data | `data-qodo-redis-0` | `5Gi` | `/data` |
| PostgreSQL, only when enabled | `data-qodo-postgres-0` | `20Gi` | `/var/lib/postgresql/data` |

PostgreSQL holds application databases, including identity and Context Engine
data, whether you use the bundled server or your external database service.

Gitway's PVC is a Helm-managed resource. The datastore PVCs are created by
StatefulSet controllers from their `volumeClaimTemplates`; they are not
standalone PVC documents in `helm template` output. Each StatefulSet Pod
gets its own claim. Increasing a datastore's replica count is not a
supported shortcut to database or broker high availability.

The Platform API, portal, review workers, LiteGit, Context Engine services,
Qodo Agent Runtime, and Zitadel do not request additional data PVCs in this
configuration. Durable application state lives in the configured databases
and backing services. Secret and ConfigMap mounts are configuration inputs;
`emptyDir` volumes provide temporary files and do not replace durable storage.
For more information, see [Kubernetes volume types](https://kubernetes.io/docs/concepts/storage/volumes/).

If you use external PostgreSQL or the supported external Redis option,
manage that service's storage and backups outside this chart. Initial claim
sizes are not production sizing recommendations. Size Gitway for the number
and size of repositories and their history, then validate I/O latency,
free-space headroom, and growth under representative reviews and indexing.

## Prepare your cluster storage

Choose an existing StorageClass approved by your platform team. Confirm its
CSI driver, capacity, filesystem permissions, availability zones, expansion,
and reclaim policy support the services above.

```bash theme={null}
KUBE_CONTEXT="REPLACE_WITH_YOUR_CONTEXT"
NAMESPACE=qodo

kubectl --context "$KUBE_CONTEXT" get storageclass
kubectl --context "$KUBE_CONTEXT" get storageclass REPLACE_WITH_STORAGE_CLASS -o yaml
```

Create the Qodo namespace through your normal cluster process. Claims and
consumers must use that same namespace. If a StorageClass is omitted, the
cluster's default class is used. Without a default class, the request needs
an explicit class or a compatible pre-provisioned volume. For more information,
see [Default StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/#default-storageclass).

All default claims use `ReadWriteOnce` (RWO), which permits read-write mounts
from one node and can permit several Pods on that node. It is not a
single-writer lock. Keep Gitway at one replica and preserve the chart's
no-surge rollout strategy for its shared repository store. Changing RWO to
`ReadWriteMany` does not establish safe concurrent Gitway access. Review a
multi-replica storage design with Qodo before changing it. For more
information, see [Persistent volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/).

For zonal storage, use the storage driver's scheduling guidance. A class
with `WaitForFirstConsumer` can leave a PVC `Pending` until its consuming
Pod is scheduled. Do not require every claim to bind before creating those
Pods. For more information, see [Storage classes](https://kubernetes.io/docs/concepts/storage/storage-classes/).

## Configure values before the first installation

Use component values: Gitway uses `gitway.pvcs.repo-store`, while bundled
Redis, RabbitMQ, and PostgreSQL use their component's
`statefulSets.volumeClaimTemplates`. There is no shared `global.storage`
switch in this released chart.

### Let the chart create claims

Save this as `storage-values.yaml`. Replace each class placeholder with an
actual StorageClass name; different services can use different classes.
Keep the `data` template names and the Gitway `repo-store` key unchanged.

```yaml storage-values.yaml theme={null}
gitway:
  pvcs:
    repo-store:
      annotations:
        helm.sh/resource-policy: keep
      accessModes:
        - ReadWriteOnce
      storageClassName: REPLACE_WITH_STORAGE_CLASS
      resources:
        requests:
          storage: 50Gi
rabbitmq:
  statefulSets:
    volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          accessModes:
            - ReadWriteOnce
          storageClassName: REPLACE_WITH_STORAGE_CLASS
          resources:
            requests:
              storage: 8Gi
redis:
  statefulSets:
    volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          accessModes:
            - ReadWriteOnce
          storageClassName: REPLACE_WITH_STORAGE_CLASS
          resources:
            requests:
              storage: 5Gi
```

Helm replaces lists rather than appending to them. Preserve the complete
claim-template entry when changing `volumeClaimTemplates`. For more
information, see [Helm values merging](https://pkg.go.dev/helm.sh/helm/v3/pkg/chartutil#CoalesceValues).

The Gitway
annotation in this example tells Helm to retain that claim when a Helm
operation would remove it. It is not present in the default chart. Review
the retention details below before adopting this policy.

### Add storage for bundled PostgreSQL

Use this additional file only when you choose bundled PostgreSQL for a fresh
installation. It enables the server and points applications to it. This is
not a migration from an existing external database.

```yaml bundled-postgres-storage-values.yaml theme={null}
global:
  externalDatabase:
    host: qodo-postgres
    adminUser: postgres
    sslMode: disable
    existingSecret: qodo-db-admin
postgres:
  enabled: true
  statefulSets:
    volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          accessModes:
            - ReadWriteOnce
          storageClassName: REPLACE_WITH_STORAGE_CLASS
          resources:
            requests:
              storage: 20Gi
```

Keep external PostgreSQL for your existing external-database deployment.
Do not apply this optional file just to allocate storage for Gitway.

### Use a customer-managed Gitway claim

Use this alternative when your storage team owns Gitway's PVC lifecycle.
Create the claim independently of the Qodo Helm release, using your storage
controller or approved GitOps workflow. This example is a Kubernetes
manifest, not Helm values:

```yaml gitway-pvc.yaml theme={null}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: customer-gitway-repositories
  namespace: qodo
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: REPLACE_WITH_STORAGE_CLASS
  resources:
    requests:
      storage: 50Gi
```

Point Qodo to that exact claim name with a separate values overlay:

```yaml existing-gitway-claim-values.yaml theme={null}
gitway:
  pvcs:
    repo-store: null
  volumes:
    repo-store:
      persistentVolumeClaim:
        claimName: customer-gitway-repositories
        useShortName: false
```

Apply this overlay after `storage-values.yaml`. The `null` removes the
chart-created claim definition during Helm merging. `useShortName: false`
keeps the customer's claim name exact; otherwise the module can prefix it
with the component's full name. Keep the existing `/repo-store` mount.
For more information about the `null` merge behavior, see
[Remove a default values key](https://v3.helm.sh/docs/chart_template_guide/values_files/#deleting-a-default-key).

Create the claim in the Qodo namespace before installation; a delayed-binding
class can still leave it Pending until Gitway is scheduled.

This example does not transfer ownership or copy data from an existing
Helm-managed claim. On an existing installation, removing its claim from
Helm's desired resources can delete it unless retention was already in the
installed release. Arrange backup, data transfer, ownership, and rollback
before switching claims. Do not apply this alternative as a live conversion.

For a pre-provisioned volume, have your storage administrator prepare the PV
and bind it to the intended claim. Match capacity, access mode, class, and
zone. A classless claim needs `spec.storageClassName: ""`; omitting the field
can select the cluster default. Set `spec.volumeName` when explicitly binding
to a prepared PV. Keep cluster-scoped PV creation in your storage workflow.
For more information, see [Persistent volume binding](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#binding).

## Render and deploy

Use the `onprem-base.yaml` at the root of your release bundle, beside the
`qodo-onprem/` chart directory. If you pulled and unpacked the chart from the
registry, copy its matching base file to that location first:

```bash theme={null}
cp qodo-onprem/onprem-base.yaml ./onprem-base.yaml
```

Render with the same base and customer values used for your installation.
This command selects only the PVC and StatefulSet templates, so it does not
print the chart's generated Secret resources:

```bash theme={null}
helm template qodo ./qodo-onprem --namespace "$NAMESPACE" \
  -f onprem-base.yaml -f my-values.yaml -f storage-values.yaml \
  --show-only charts/gitway/templates/pvc.yaml \
  --show-only charts/rabbitmq/templates/statefulset.yaml \
  --show-only charts/redis/templates/statefulset.yaml
```

Check each requested class, size, access mode, claim name, and mount. For
the customer-managed Gitway alternative, append
`-f existing-gitway-claim-values.yaml`, omit the Gitway PVC `--show-only`
argument, and inspect `charts/gitway/templates/deployment.yaml` instead.
There should be no chart-created Gitway PVC, and its Deployment should
reference `customer-gitway-repositories`.

For bundled PostgreSQL, append
`-f bundled-postgres-storage-values.yaml` and
`--show-only charts/postgres/templates/statefulset.yaml` when rendering.
Then deploy through your normal Helm or GitOps procedure. For a fresh
external-database installation with chart-created claims:

```bash theme={null}
helm upgrade --install qodo ./qodo-onprem \
  --kube-context "$KUBE_CONTEXT" --namespace "$NAMESPACE" \
  -f onprem-base.yaml -f my-values.yaml -f storage-values.yaml
```

Keep any chosen customer-managed claim or PostgreSQL overlay after
`storage-values.yaml`. Follow the remaining installation prerequisites,
including database, identity, and credentials. For more information, see
[Deploy Qodo using Helm](https://docs.qodo.ai/on-prem/qodo-on-prem-helm-chart-overview.md).

## Verify persistence

After the consumers are created, inspect the actual claims and mount events:

```bash theme={null}
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pvc
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" describe pvc qodo-gitway-repo-store
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" describe pod -l app.kubernetes.io/name=gitway
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get statefulset qodo-rabbitmq qodo-redis
```

Use `customer-gitway-repositories` in the describe command if you selected
the customer-managed claim. Include `qodo-postgres` when using the bundled
database. Confirm each expected claim is Bound and consumers are ready.

Verify the application path as well:

1. Run a code review or indexing request against a representative repository.
2. Confirm Gitway mounts the intended claim at `/repo-store` and completes repository access.
3. During planned maintenance, restart Gitway through your approved rollout procedure and confirm it remounts the same claim and repository files remain available.
4. Run another review or indexing request and check completion, errors, disk usage, and latency.
5. Rehearse datastore backup and restore separately; a Bound claim is not proof of a restorable database or broker.

## Retain, resize, and recover

| Storage owner | Removal behavior to plan for |
| - | - |
| Gitway PVC created by the default chart | Helm can remove the claim on uninstall or when it is removed from rendered resources |
| Gitway PVC with the example's `helm.sh/resource-policy: keep` annotation | Helm skips removal; plan ownership and reuse of the retained resource on later installs |
| Datastore PVCs created from StatefulSet templates | The released chart leaves Kubernetes' default retain behavior in effect; verify cluster policies before removal or scaling changes |
| Customer-managed Gitway PVC | Its external controller owns retention and pruning; the Qodo values only reference it |

PVC retention and the backing PV's reclaim policy are separate. A `Delete`
reclaim policy can remove the backing volume when its PVC is deleted.
Neither a keep annotation nor a retained PVC protects against explicit
claim or namespace deletion, storage failure, or an unsuitable GitOps
pruning policy. Encrypt the repository store as sensitive source code and
set backup and restore ownership for every datastore. For more information,
see [Helm resource retention](https://helm.sh/docs/howto/charts_tips_and_tricks/#tell-helm-not-to-uninstall-a-resource)
and [StatefulSet volume retention](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#persistentvolumeclaim-retention).

Choose class and initial size before installation. Existing PVCs cannot be
shrunk or moved to a different StorageClass in place. Expansion requires a
class and driver that support it. Changing a StatefulSet's claim-template
size in values is not enough: Kubernetes normally rejects edits to existing
`volumeClaimTemplates`, and existing claims do not resize from that edit.
Have your storage team plan growth of the actual claims and align future
templates through a reviewed maintenance procedure. Do not uninstall Qodo
or delete claims to force a size change. For more information, see
[Expand persistent volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#expanding-persistent-volumes-claims).

The bundled `values-emptydir.yaml` profile is for disposable evaluation. It
removes persistent storage from Gitway and bundled datastores; their local
data is lost when the Pod is replaced. It is not a production fallback for
failed PVC provisioning. For more information, see
[Clusters without persistent volumes](https://docs.qodo.ai/on-prem/qodo-on-prem-helm-chart-overview.md#clusters-without-persistent-volumes).

## Troubleshoot storage

| Symptom | Check and recovery |
| - | - |
| PVC stays Pending | Inspect claim events, the requested class, provisioner health, quota, capacity, and zone. With delayed binding, check the consuming Pod's scheduling events |
| `FailedMount` or `Multi-Attach` | Check claim namespace/name, node/zone access, and whether an older Pod still has the volume attached |
| Gitway cannot write `/repo-store` | Check filesystem ownership and the Pod's effective security context against your storage driver's requirements |
| Gitway re-fetches repositories after restart | Confirm its mount references the intended persistent claim and an evaluation overlay has not replaced it with `emptyDir` |
| Helm rejects a StatefulSet storage update | Compare the installed claim templates; plan the storage change as maintenance rather than retrying an immutable-field edit |
| Disk is full | Check filesystem/PVC usage and repository growth; plan supported expansion and review cleanup with the service owner |

## Next steps

* [Infrastructure requirements](https://docs.qodo.ai/on-prem/qodo-on-premise/infrastructure-requirements.md): Prepare capacity, database access, and cluster dependencies.
* [Deploy Qodo using Helm](https://docs.qodo.ai/on-prem/qodo-on-prem-helm-chart-overview.md): Apply the full installation and upgrade procedure.
* [Autoscale Qodo with KEDA](https://docs.qodo.ai/on-prem/keda-autoscaling.md): Review workload and shared-storage constraints before enabling scaling.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.