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

# Autoscale Qodo with KEDA

> Connect your KEDA installation to Qodo Deployments using CPU or RabbitMQ queue metrics, then verify scaling and maintenance behavior.

* **Deployment model**: On-premises
* **Configuration reference**: Qodo Helm chart `7.21.0` and KEDA `2.17.2`.
* **Live validation**: KEDA `2.17.2` on GKE, using bounded CPU and RabbitMQ canaries.

Use your existing KEDA installation to adjust the number of Qodo Pods as load
changes. The Qodo chart can create a `ScaledObject` for each enabled Deployment.
Your cluster team installs and operates KEDA; the Qodo chart does not install it.

The examples below cover CPU scaling for the Platform API and queue scaling
for the Git event review worker. They are starting configurations, not capacity
recommendations. Validate them against your pinned chart, workload, database
connection budget, and model-provider quotas before production use.

## Before you begin

Have your cluster administrator install a KEDA release compatible with your
Kubernetes version before applying the Qodo scaling values. A standard
installation includes the KEDA operator, metrics API server, admission
webhooks with working TLS, and CRDs. Pin the operator chart through your
approved Helm or GitOps process. The Qodo chart creates workload scaling
resources; it does not install these cluster components. For more information,
see [Deploying KEDA](https://keda.sh/docs/2.17/deploy/).

| Prerequisite | Required for | What must be ready |
| - | - | - |
| KEDA operator and CRDs | Both examples | The operator watches your Qodo namespace; `scaledobjects.keda.sh` and `triggerauthentications.keda.sh` are established |
| KEDA metrics API and admission webhooks | Standard KEDA installation | The external-metrics API is available and control-plane traffic can reach the configured metrics and webhook endpoints |
| Kubernetes Metrics Server | CPU scaling | The resource-metrics API and `kubectl top pods` work; selected containers have CPU requests |
| RabbitMQ management API | Queue scaling | The selected queue and virtual host exist; KEDA can resolve and reach the management endpoint and trusts its TLS certificate |
| Broker credential Secret | Queue scaling | The referenced Secret/key exists in the Qodo namespace; the broker accepts the credential and KEDA can read it |
| Namespaced permissions | Creating scaling resources | Your deployment identity can create ScaledObjects and TriggerAuthentications; KEDA can observe workloads and update their scale |
| Scheduling capacity | Additional replicas | Node capacity, namespace quotas, and scheduling rules permit the configured maximum |

Also prepare your pinned Qodo chart, existing customer values, namespace, and
Kubernetes context. Cluster-wide operator/CRD installation belongs to your
platform team; namespaced workload configuration belongs to your Qodo
deployment workflow.

KEDA scales workloads. Your node provisioning and secrets operators have
separate responsibilities. You can use an existing secrets operator to supply
the credentials consumed by Qodo and KEDA. For more information, see [Manage Qodo secrets with External Secrets Operator](https://docs.qodo.ai/on-prem/external-secrets.md).

Select your target explicitly. These examples use namespace `qodo`:

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

kubectl --context "$KUBE_CONTEXT" wait --for=condition=Established \
  crd/scaledobjects.keda.sh crd/triggerauthentications.keda.sh --timeout=120s
kubectl --context "$KUBE_CONTEXT" -n "$KEDA_NAMESPACE" \
  rollout status deployment/keda-operator --timeout=120s
kubectl --context "$KUBE_CONTEXT" -n "$KEDA_NAMESPACE" \
  rollout status deployment/keda-operator-metrics-apiserver --timeout=120s
kubectl --context "$KUBE_CONTEXT" -n "$KEDA_NAMESPACE" \
  rollout status deployment/keda-admission-webhooks --timeout=120s
kubectl --context "$KUBE_CONTEXT" wait --for=condition=Available \
  apiservice/v1beta1.external.metrics.k8s.io --timeout=120s
kubectl --context "$KUBE_CONTEXT" auth can-i create scaledobjects.keda.sh \
  -n "$NAMESPACE"
kubectl --context "$KUBE_CONTEXT" auth can-i create triggerauthentications.keda.sh \
  -n "$NAMESPACE"
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get hpa
```

The commands use the standard Helm Deployment names and namespace `keda`.
Substitute your actual names if your platform team customized them. Confirm
the external-metrics API belongs to your intended KEDA installation, resolve
any failed readiness check, and require `yes` for both permission checks
before proceeding.

For CPU scaling, also verify the separate resource-metrics API:

```bash theme={null}
kubectl --context "$KUBE_CONTEXT" wait --for=condition=Available \
  apiservice/v1beta1.metrics.k8s.io --timeout=120s
kubectl --context "$KUBE_CONTEXT" top pods -n "$NAMESPACE"
```

An installed CRD alone does not establish that the operator, webhook, or
metrics APIs are working.

## 1. Choose a workload and metric

| Workload | Helm values key | Example metric and constraints |
| - | - | - |
| Platform API | `platform` | CPU utilization, with at least one replica available |
| Git event review worker | `pr-agent-litegit` | Backlog in its RabbitMQ subscription queue, including work already delivered but not acknowledged |
| Other application Deployments | The component's alias in your chart | Verify concurrency, shutdown behavior, dependencies, and storage before enabling scaling |
| Git repository gateway | `gitway` | The default repository cache uses one shared `ReadWriteOnce` PVC. Review the cache and storage design with Qodo before scaling across nodes |
| PostgreSQL, Redis, and RabbitMQ | Their respective component keys | Use the database or broker's supported capacity and high-availability procedure |

Keep `minReplicaCount: 1` for the examples. Scaling a webhook receiver or API
to zero requires a separate request activation design. CPU metrics alone also
cannot reactivate a workload with no running Pods.

## 2. Configure CPU scaling

Merge this block into your customer values, or save it as
`keda-cpu-values.yaml` if your installation uses separate override files:

```yaml keda-cpu-values.yaml theme={null}
platform:
  autoscaling:
    enabled: false
  scaledObject:
    enabled: true
    spec:
      minReplicaCount: 1
      maxReplicaCount: 4
      advanced:
        horizontalPodAutoscalerConfig:
          behavior:
            scaleDown:
              stabilizationWindowSeconds: 300
      triggers:
        - type: cpu
          metricType: Utilization
          metadata:
            value: "70"
```

The target is the `qodo-platform` Deployment. The chart omits the
Deployment's `spec.replicas` when `scaledObject.enabled` is true, so Helm does
not continually reset KEDA's desired replica count.

KEDA creates and manages an HPA. Do not attach an independent HPA to the same
Deployment. Setting `autoscaling.enabled: false` keeps the chart's separate
autoscaling flag off; it does not remove an HPA managed elsewhere.

CPU utilization is relative to CPU requests. Check the resources in your
installed values before choosing a threshold. For more information, see [KEDA CPU scaler](https://keda.sh/docs/2.17/scalers/cpu/).

## 3. Configure RabbitMQ queue scaling

Queue scaling is useful when workers spend time waiting for model responses
and CPU does not reflect pending work. Use the RabbitMQ subscription queue
consumed by the selected worker.

The example below targets `qodo-git`, the Deployment behind
`pr-agent-litegit`, and its subscription
`vcs-events.pr-agent-pro-__pass_through__`. Confirm the queue exists in your
broker and release before using it. This is not the queue of the separate
`pr-agent-agentic` Deployment.

Replace `REPLACE_WITH_RABBITMQ_MANAGEMENT_URL` with the management API base
URL reachable from KEDA, including its scheme and port. Do not include
credentials in the URL. For an in-cluster broker, use a namespace-qualified
Service DNS name and include the trailing `/`. KEDA resolves names from its
operator namespace, so a short name that works inside a Qodo Pod can fail
here. For more information, see [DNS for Services and Pods](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/).

Use HTTPS when your broker provides TLS, and configure
the trusted CA in your KEDA installation if needed. The `http` protocol below
selects the management API and also supports an HTTPS URL.

```yaml keda-rabbitmq-values.yaml theme={null}
pr-agent-litegit:
  autoscaling:
    enabled: false
  scaledObject:
    enabled: true
    spec:
      minReplicaCount: 1
      maxReplicaCount: 4
      advanced:
        horizontalPodAutoscalerConfig:
          behavior:
            scaleDown:
              stabilizationWindowSeconds: 300
      triggers:
        - type: rabbitmq
          metadata:
            host: REPLACE_WITH_RABBITMQ_MANAGEMENT_URL
            protocol: http
            vhostName: /
            queueName: vcs-events.pr-agent-pro-__pass_through__
            mode: QueueLength
            value: "10"
            excludeUnacknowledged: "false"
            usernameFromEnv: RABBITMQ_USERNAME
          authenticationRef:
            name: qodo-review-rabbitmq-auth
  extraObjectsDict:
    qodo-review-rabbitmq-auth:
      apiVersion: keda.sh/v1alpha1
      kind: TriggerAuthentication
      metadata:
        name: qodo-review-rabbitmq-auth
      spec:
        secretTargetRef:
          - parameter: password
            name: qodo-rabbitmq-password
            key: RABBITMQ_PASSWORD
```

This uses the existing worker username and broker password. If your broker
policy requires a dedicated monitoring identity, use a separately managed
Secret and configure both `username` and `password` through
`TriggerAuthentication`. Grant that identity the broker permissions required
to read the selected queue through the management API.

The management API includes unacknowledged messages in the queue metric when
`excludeUnacknowledged` is false. This helps account for reviews still being
processed. Preserve the chart's shutdown grace period and test an in-progress
review during scale down. For more information, see [KEDA RabbitMQ Queue scaler](https://keda.sh/docs/2.17/scalers/rabbitmq-queue/).

## 4. Render and apply the configuration

Keep your installation's existing values order. If your bundle provides one
editable values file, merge the selected configuration into that file. If
using override files, place them after the base and customer values.

The following example uses the unpacked chart and separate overrides. Replace
`my-values.yaml` with the customer values file used for your installation.
Select only the `ScaledObject` templates when inspecting the output so the command
does not print the chart's generated credentials:

```bash theme={null}
helm template qodo ./qodo-onprem --namespace "$NAMESPACE" \
  -f onprem-base.yaml -f my-values.yaml \
  -f keda-cpu-values.yaml -f keda-rabbitmq-values.yaml \
  --show-only charts/platform/templates/keda-scaledobject.yaml \
  --show-only charts/pr-agent-litegit/templates/keda-scaledobject.yaml \
  --show-only charts/pr-agent-litegit/templates/extra-objects.yaml
```

Confirm each `scaleTargetRef` matches its intended Deployment and each
`authenticationRef` has a matching resource. Install or upgrade using your
existing approved deployment procedure and the same values. For more
information, see [Deploy Qodo using Helm](https://docs.qodo.ai/on-prem/qodo-on-prem-helm-chart-overview.md).

For GitOps, let one controller own each `ScaledObject` and authentication
resource. Inspect the rendered Deployments to confirm `spec.replicas` is
absent and that no other application continually reapplies a fixed count.

After applying, confirm the `ScaledObject` resources were created and Qodo Pods are running:

```bash theme={null}
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get scaledobjects
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods
```

## 5. Verify scaling with real work

```bash theme={null}
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" \
  get scaledobjects,hpa
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" \
  describe scaledobject qodo-git
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" \
  get deployment qodo-git -w
```

Check that the `ScaledObject` is ready and the generated HPA has a usable
metric. Then:

**Step 1**

Submit representative code review work through your normal Git integration.

**Step 2**

Observe the queue or CPU metric and confirm replicas increase when the target is exceeded.

**Step 3**

Confirm new Pods become ready and reviews complete with the expected findings.

**Step 4**

Let the load fall and confirm replicas return toward the minimum after the stabilization window, including while a review is still running.

**Step 5**

Check database connections, model-provider throttling, Pod scheduling, and review failures throughout the run.

A ready `ScaledObject` or increasing replica count does not establish that
reviews complete correctly. Record the workload, limits, and outcome of this
exercise before promoting the configuration.

## Pause scaling during maintenance

Some Qodo upgrades require services to be stopped temporarily. Pause their KEDA autoscalers before the upgrade so they do not
restart workloads the upgrade intentionally stops. Also pause any GitOps
reconciliation that would remove your maintenance annotations.

For each affected `ScaledObject`, for example `qodo-git`:

```bash theme={null}
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" \
  annotate scaledobject qodo-git autoscaling.keda.sh/paused="true" --overwrite
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" \
  wait scaledobject/qodo-git --for=condition=Paused --timeout=120s
```

For Helm-managed objects, also retain the annotation in
`<component>.scaledObject.annotations` for the maintenance upgrade. For
GitOps, make the pause part of your desired configuration. Verify the KEDA
HPA no longer controls the target before the upgrade starts.

Do not use `paused-replicas` to override the upgrade's replica decisions.
After the upgrade completes successfully and the target Deployments specify
the new image and configuration, remove the maintenance annotation from the owning
configuration and the live object. Inspect the Deployment's image and
configuration first: resume only after confirming the target configuration is applied.
An autoscaled Deployment may remain at zero after the upgrade.

```bash theme={null}
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" \
  annotate scaledobject qodo-git autoscaling.keda.sh/paused-
```

Confirm KEDA recreates its HPA, restores at least the configured minimum, and
reports ready. Wait for the target Pods to roll out, then repeat the real
review checks before ending maintenance.

If the upgrade fails, keep scaling paused and follow the release's recovery
procedure. For more information, see [KEDA pausing autoscaling](https://keda.sh/docs/2.17/concepts/scaling-deployments/#pausing-autoscaling).

## Validation scope

Live development-cluster tests verified CPU and RabbitMQ scale-out and
scale-in, broker authentication through a namespaced Secret, and HPA removal
and recreation during pause/resume. An initial broker DNS failure was
reproduced and corrected with namespace-qualified Service DNS.

These tests used isolated canaries with at most two replicas and a shortened
scale-down stabilization window. They did not scale the shared Qodo
Deployments or run a production review under load. Verify your chosen limits,
shutdown grace period, and end-to-end review behavior before production use.

## Troubleshoot scaling

| Symptom | Check and recovery |
| - | - |
| Helm reports no match for `ScaledObject` | Have your cluster team install the KEDA CRDs and controllers before enabling the values |
| CPU metrics are unknown | Check Metrics Server, Pod CPU requests, and HPA conditions |
| RabbitMQ reports authentication or queue errors | Check the management URL, virtual host, queue name, Secret key, broker permissions, and KEDA's Secret access |
| Broker DNS fails although it resolves inside a Qodo Pod | Use a namespace-qualified Service name: the KEDA operator resolves short names in its own namespace |
| The metric endpoint is unreachable | Check NetworkPolicies, TLS trust, DNS, and access from the KEDA operator |
| The queue is empty but reviews are still running | Include unacknowledged messages through the management API and check worker concurrency and shutdown behavior |
| Desired replicas increase but Pods remain Pending | Check node capacity, namespace quotas, scheduling rules, and storage attachment |
| Replicas keep returning to a fixed value | Check for an independent HPA, a rendered `spec.replicas`, or another controller writing the Deployment |
| Scaling is ready but reviews fail | Check database capacity, model quotas, worker logs, and the end-to-end review result |

## Related resources

* [Manage Qodo secrets with External Secrets Operator](https://docs.qodo.ai/on-prem/external-secrets.md): Supply credentials through your existing secret-management workflow.
* [Monitor Qodo with Prometheus and Grafana](https://docs.qodo.ai/on-prem/monitoring.md): Observe workload behavior while tuning scaling.
* [Deploy Qodo using Helm](https://docs.qodo.ai/on-prem/qodo-on-prem-helm-chart-overview.md): Follow the installation and upgrade procedure for your release.


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