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

# Deploy Qodo with Argo CD

> Configure deterministic Helm rendering, external credentials, ordered migrations, and verified Argo CD upgrades.

* **Deployment model**: On-premises
* **Configuration reference**: Qodo Helm chart `7.23.0`; the explicit Argo CD profile first shipped in `7.17.0`
* **Python requirement**: 3.11 or later

Use the chart's Argo CD profile when Argo CD owns Qodo's deployment lifecycle.
The profile keeps rendering repeatable, uses externally managed internal
credentials, and orders databases, queues, migrations, and workloads.

Argo CD uses Helm to render manifests. It does not run `helm upgrade`, read
live Secrets through Helm `lookup`, or advance Helm's release revision.
These differences require both chart values and an Application policy.
For more information, see [Helm](https://argo-cd.readthedocs.io/en/stable/user-guide/helm/).

| Starting point | Procedure |
| - | - |
| New installation | Prepare the external dependencies, then follow steps 1–5 |
| Existing Helm installation | Preserve credentials and release identity before adoption. See [Move an existing Helm installation](#move-an-existing-helm-installation). |
| Argo CD previously generated Qodo credentials | Recover stable credentials before another sync. See [Recover from earlier configuration issues](#recover-from-earlier-configuration-issues). |

## Before you begin

Prepare:

* Your pinned Qodo chart release, its `values-argocd.yaml`, `verify-external-secrets.py`, and `argocd-upgrade-check.py`
* Python, `kubectl`, Helm, and an authenticated Argo CD CLI
* An existing Qodo namespace and an approved Argo CD project and destination
* External PostgreSQL, including the database permissions and extensions required by your release
* A ready external Secret controller or approved Secret provisioning process
* Registry, Git, ingress, TLS, identity-provider, and model-provider access for your installation

Bundled PostgreSQL is unsupported in this profile: it cannot start before
the database migration stage on a first sync. Bundled RabbitMQ is required.
Redis can be bundled or externally configured. Prepare storage and any
selected cluster operators before deployment. For more information, see
[Infrastructure requirements](https://docs.qodo.ai/on-prem/qodo-on-premise/infrastructure-requirements.md).

Use the scripts and chart from the same release. Keep all actual credentials
in your approved Secret system. A licensed chart download can contain
injected registry credentials, so keep it out of Git.

The examples use Application and Helm release name `qodo-prod`, Qodo
namespace `qodo`, and Argo CD namespace `argocd`. Replace them consistently.
For a new installation, choose a unique Application identity and use the
same name for `helm.releaseName`. Existing installations retain their Helm
release name.

## 1. Prepare the values files

Keep your customer configuration in your deployment repository, for example
`environments/production/my-values.yaml`. Merge the following into that file
and retain your other installation settings:

```yaml my-values.yaml (database and broker settings) theme={null}
global:
  baseDomain: qodo.example.com
  externalDatabase:
    host: postgres.example.com
    adminUser: qodo_admin
    existingSecret: qodo-db-admin
    sslMode: require
postgres:
  enabled: false
rabbitmq:
  enabled: true
```

Replace the database host and administrator username with your approved
database configuration. `qodo-db-admin.POSTGRES_PASSWORD` must contain that
user's password. Retain any release-specific database role settings.

The chart includes `values-argocd.yaml` with these contents:

```yaml values-argocd.yaml theme={null}
global:
  deploymentMode: argocd
  generatedSecrets:
    render: false
```

Use the file from your selected chart release as the last values layer,
after the base, customer, and optional integration overlays. This mode
externalizes Qodo's internal credentials; it does not create them or connect
your Secret manager automatically. For more information, see
[Manage Qodo secrets with External Secrets Operator](https://docs.qodo.ai/on-prem/external-secrets.md).

If you use Artifactory, prepare its pull Secret and add your
`artifactory-values.yaml` before `values-argocd.yaml`. For more information,
see [Pull Qodo images and Helm charts through JFrog Artifactory](https://docs.qodo.ai/on-prem/artifactory-registry.md).

If you override Platform's rule embedding settings, use literal `value`
entries for `platform.env.DYNACONF_RULES__AI__SIMILARITY__EMBEDDING_MODEL_NAME`,
`platform.env.DYNACONF_RULES__AI__SIMILARITY__EMBEDDING_DIM`, and
`platform.env.DYNACONF_RULES__AI__SIMILARITY__EMBEDDING_SEND_DIMENSIONS`.
Keep your existing model and dimension settings. Their migration runs before
workload ConfigMaps and Secrets are updated, so `valueFrom` is unsupported
for these non-secret settings. Embedding API credentials remain Secret
references.

## 2. Provision credentials before the first sync

Your external Secret process must create these fixed names in the Qodo
namespace. The Argo CD profile omits them from the chart render:

| Secret | Required keys |
| - | - |
| `qodo-db-admin` | `POSTGRES_PASSWORD` |
| `qodo-zitadel-secrets` | `ZITADEL_MASTERKEY`, `ZITADEL_DB_PASSWORD` |
| `qodo-platform-jwt` | `DYNACONF_AUTH__OIDC__JWT_ENCRIPTION_KEY` |
| `qodo-qodo-platform-api-key` | `DYNACONF_AUTH__QODO_PLATFORM__API_KEY`, `QODO_PLATFORM__API_KEY`, `DYNACONF_AUTH__SERVICE_API_KEYS` |
| `qodo-litegit-master-secrets` | `LITEGIT_MASTER_KEY`, `LITEGIT_MASTER_API_KEY` |
| `qodo-rabbitmq-password` | `RABBITMQ_PASSWORD` |
| `qodo-llm-gateway-key` | `LLM_GATEWAY_API_KEY` |
| `qodo-pr-agent-secrets-toml` | `.secrets.toml` |

The Platform bearer must equal `qodo-svc-onprem-` followed by the raw
Platform key. The service allowlist must contain that bearer, and the
`[qodo_platform].api_key` in the TOML file must match it. The packaged
preflight checks these relationships without printing credentials.

Provision your delivery path's image-pull credentials and the required
provider and TLS Secrets too. The internal inventory above does not replace
those prerequisites. Keep external Secret resources in their own deployment
lifecycle so they are ready before Qodo's migrations start.

Set your target explicitly, then check the inventory from the extracted
release:

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

python3 qodo-onprem/verify-external-secrets.py \
  --context "$KUBE_CONTEXT" --namespace "$NAMESPACE"
```

For an existing installation, also preserve `qodo-zitadel-generated` and add
`--require-zitadel-generated`. A new installation does not have that runtime
Secret until provisioning completes. Do not rename fixed targets through
values or regenerate encryption keys against an existing database.

## 3. Configure repository access and the Application

The following example uses the licensed OCI Helm registry and a separate
Git repository for customer values. Argo CD must authenticate to both sources.
Configure Git access through your platform team's existing process.

For the chart repository, have that process create the equivalent of this
Secret in the Argo CD namespace. Fill credentials privately and keep the
populated file out of Git:

```yaml qodo-chart-repository.yaml theme={null}
apiVersion: v1
kind: Secret
metadata:
  name: qodo-chart-repository
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
type: Opaque
stringData:
  type: helm
  url: artifacts-self-hosted.qodo.ai
  enableOCI: "true"
  username: REPLACE_WITH_QODO_ACCOUNT_EMAIL
  password: REPLACE_WITH_REPLICATED_LICENSE_ID
```

This example uses Helm's OCI integration. The repository URL identifies the
registry host without `oci://`, and the Application's `chart` contains the
full repository path. Together they identify
`oci://artifacts-self-hosted.qodo.ai/codium-stack/qodo-onprem/qodo-onprem`.
For more information, see [Declarative setup](https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#helm-chart-repositories).

If Artifactory proxies the chart, set `url` to your Artifactory hostname and
use your Artifactory account and access token. In the Application, set
`repoURL` to the same hostname. With repository-path access, add the
repository key to the start of `chart`, for example
`qodo-helm-virtual/codium-stack/qodo-onprem/qodo-onprem`. For more
information, see [Pull the Helm chart through Artifactory](https://docs.qodo.ai/on-prem/artifactory-registry.md#pull-the-helm-chart-through-artifactory).

Save this as `qodo-application.yaml`, or use the downloadable example from
[the Application example](https://docs.qodo.ai/examples/on-prem/qodo-application.yaml).
Replace the repository, project, chart version, values commit, names, and
destination with your approved configuration:

```yaml qodo-application.yaml theme={null}
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: qodo-prod
  namespace: argocd
spec:
  project: default
  sources:
    - repoURL: artifacts-self-hosted.qodo.ai
      chart: codium-stack/qodo-onprem/qodo-onprem
      targetRevision: REPLACE_WITH_PINNED_CHART_VERSION
      helm:
        releaseName: qodo-prod
        valueFiles:
          - onprem-base.yaml
          - $values/environments/production/my-values.yaml
          # Add optional customer overlays before values-argocd.yaml.
          - values-argocd.yaml
    - repoURL: https://git.example.com/platform/qodo-deployments.git
      targetRevision: REPLACE_WITH_PINNED_VALUES_COMMIT
      ref: values
  destination:
    name: REPLACE_WITH_REGISTERED_CLUSTER_NAME
    namespace: qodo
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jqPathExpressions:
        - '.spec.template.metadata.annotations."qodo.ai/revision"'
        - '.spec.template.metadata.annotations."qodo.ai/zitadel-idp-config"'
        - '.spec.template.metadata.annotations."qodo.ai/ca-bundle-checksum"'
        - '.metadata.annotations."qodo.ai/consumer-prepared-upgrade"'
        - '.spec.template.metadata.annotations."qodo.ai/consumer-preparation-recovery"'
  syncPolicy:
    syncOptions:
      - RespectIgnoreDifferences=true
      - PruneLast=true
```

The `$values` prefix refers to the Git source with `ref: values`; its path
starts at that repository's root. The profile files without `$values` come
from the chart. Pin both sources, and verify the chart version is available
in your delivery channel. For more information, see
[Multiple sources for an Application](https://argo-cd.readthedocs.io/en/stable/user-guide/multiple_sources/).

This example leaves automated sync, self-heal, and automatic pruning off.
Choose the registered destination cluster that matches the context used by
the verification scripts.
`PruneLast=true` controls pruning order when pruning is requested; it does
not enable pruning. The ignored fields are annotations maintained by Qodo's
Jobs. `RespectIgnoreDifferences=true` preserves them during sync as well as
diff comparison. For more information, see
[Sync options](https://argo-cd.readthedocs.io/en/stable/user-guide/sync-options/).

Keep Application identities unique across controllers targeting the cluster.
If your controller uses instance-label tracking, an Application name that
differs from the Helm release name can rewrite chart selectors. Have its
owner configure appropriate resource tracking before adoption. Namespace
selection alone does not isolate resource ownership or pruning. For more
information, see [Resource tracking](https://argo-cd.readthedocs.io/en/stable/user-guide/resource_tracking/).

### ApplicationSet deployments

Put the Application's `sources`, `ignoreDifferences`, and `syncPolicy` under
`spec.template.spec` in your ApplicationSet. Keep its generated Applications
manual during the checks below. Disable automation in the ApplicationSet
template; changing only the generated Application can be overwritten by its
controller. Inspect the actual generated Application before syncing. For more
information, see [Automated sync policy](https://argo-cd.readthedocs.io/en/stable/user-guide/auto_sync/).

## 4. Render and check before syncing

Render the same chart and ordered values you configured in the Application.
For the standard extracted chart, the command is:

```bash theme={null}
umask 077
helm template qodo-prod ./qodo-onprem --namespace "$NAMESPACE" \
  -f qodo-onprem/onprem-base.yaml \
  -f my-values.yaml \
  -f qodo-onprem/values-argocd.yaml > qodo-argocd-rendered.yaml
```

Add optional overlays before the final profile file. If your bundle puts
`onprem-base.yaml` at the top level, use that path instead. Keep rendered
output private: licensed image-pull Secrets can still appear. Confirm the
internal credential Secrets from step 2 are absent and the intended registry,
namespace, and Secret references are correct.

Create or update the Application through your approved GitOps process, then
refresh and inspect its desired state:

```bash theme={null}
ARGO_APP=qodo-prod
ARGO_NAMESPACE=argocd
HELM_RELEASE=qodo-prod
CHART_VERSION="REPLACE_WITH_PINNED_CHART_VERSION"

argocd app get "$ARGO_APP" --hard-refresh
argocd app diff "$ARGO_APP"
python3 qodo-onprem/argocd-upgrade-check.py pre \
  --context "$KUBE_CONTEXT" --namespace "$NAMESPACE" \
  --argo-namespace "$ARGO_NAMESPACE" --app "$ARGO_APP" \
  --release "$HELM_RELEASE" --first-install
```

Use `--first-install` only when no Qodo workloads have been installed. For
adoption or upgrades, omit it so the check compares running consumers with
their Secrets and tests broker and gateway authentication.

Require `GO` before proceeding. The checker refuses a running or queued
operation, automated sync, stale comparison, rendered internal credentials,
unprotected tracked Secrets, and invalid live credentials. It also rejects
`ApplyOutOfSyncOnly=true`, which can leave a migration-paused Deployment at
zero replicas instead of restoring its configured count.

## 5. Run a full sync and verify the result

Use a full Application sync, with automated reconciliation still disabled:

```bash theme={null}
argocd app sync "$ARGO_APP"
python3 qodo-onprem/argocd-upgrade-check.py post \
  --context "$KUBE_CONTEXT" --namespace "$NAMESPACE" \
  --argo-namespace "$ARGO_NAMESPACE" --app "$ARGO_APP" \
  --release "$HELM_RELEASE" --revision "$CHART_VERSION"
```

The chart orders resources as follows:

| Stage | Work |
| - | - |
| Sync wave -3 | Static configuration, RBAC, image-pull prerequisites, bundled RabbitMQ, and bundled Redis |
| Sync wave -2 | Queue topology checks and database initialization |
| Sync wave -1 | Application database migrations |
| Sync wave 0 | Application workloads and Zitadel provisioning |
| PostSync | Revision stamping and required rollouts |

Do not skip hooks or selectively sync resources to bypass a failed stage.
Selective sync skips PostSync hooks. For more information, see
[Sync phases and waves](https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/).

Identical chart and values inputs keep the same migration Job names. Chart,
configuration, or migration-script changes produce new names so Jobs run
again. Rotating the licensed pull credential without changing its delivery
mode does not rename the Jobs.

On a first install, revision stamping causes one additional rollout after
initial readiness. Wait for it to settle. The post-check requires the actual
sync revision, its setup/migration Jobs, and Deployment/StatefulSet rollouts
to succeed. Then verify SSO, Git provider connectivity, a real review using
your configured model, and the provider webhook path.

## Move an existing Helm installation

Plan the ownership transfer with your platform team and rehearse it against
a restored copy before production:

1. Preserve the existing release name, namespace, customer values, and datastore backups. Use that release name in the Application and checker examples.
2. Import the existing credential bytes into the external Secret system, including Zitadel's master key, LiteGit's vault key, the database password, and `qodo-zitadel-generated`. Confirm reconciliation preserves the bytes.
3. Run `verify-external-secrets.py --require-zitadel-generated` and the live `pre` check without `--first-install`. Fix credential mismatches before continuing.
4. Freeze direct Helm upgrades. Keep automated sync, self-heal, and pruning off; inspect the Application diff and tracked resources. Do not run `helm uninstall` as an adoption step.
5. Run the full sync and post-check in the agreed maintenance window. Only one deployment controller should write the workloads.

Before allowing pruning, protect any previously tracked credential Secret
with this annotation through its approved owner:

```yaml credential-secret-metadata.yaml (merge into each tracked credential) theme={null}
metadata:
  annotations:
    argocd.argoproj.io/sync-options: Prune=false
```

Merge this with any existing sync options. Inspect `argocd app resources` and
the pruning dry run; every proposed deletion must belong to the intended
installation. Require the external Secret owner to preserve those resources,
and keep `PruneLast=true`. Enable self-heal only after confirming the generated
Application has the annotation policy from step 3. For more information, see
[Deploy Qodo using Helm](https://docs.qodo.ai/on-prem/qodo-on-prem-helm-chart-overview.md#argo-cd-/-applicationset-external-postgresql).

## Upgrade, rollback, and credential rotation

For every upgrade, disable automation in the owning Application or
ApplicationSet, finish or terminate the current operation, pin both desired
sources, refresh, and run the live `pre` check. Sync only after `GO`, then run
`post` with the expected chart version and verify application behavior.

A rollback can require restored database state, broker topology, and the
same credential bytes. Changing the Git revision alone does not reverse
those changes. Before retrying a version after rollback, run `pre` again:
`jobs.will_run` identifies completed Jobs that would otherwise be reused
against the restored state. Follow its exact Job list and the release's
recovery procedure before retrying.

External Secret rotation does not automatically restart consumers that use
environment variables or `subPath` mounts. Coordinate a rollout of all
affected consumers after synchronization. The PostSync revision roller is
not a Secret-rotation controller. Preserve encryption keys during an
ownership-only handoff. For more information, see
[Manage Qodo secrets with External Secrets Operator](https://docs.qodo.ai/on-prem/external-secrets.md#verify-rotation-and-recovery).

## Recover from earlier configuration issues

| Symptom or earlier issue | Cause and next action |
| - | - |
| Credentials change on each render; failures appear after restart | Argo CD rendered generated credentials without Helm's live lookup. Pause sync, restore the original credential bytes, reconcile the external Secret source, and validate consumers before retrying. Ignoring Secret `/data` does not prevent writes through `stringData`. |
| Migration waits for a broker or RBAC that never appears | Legacy Helm hook mapping can put migration ahead of its own prerequisites. Use the explicit profile and its ordered Sync waves; do not bypass the migration. |
| RabbitMQ reports HTTP 401, or Zitadel reports decryption errors | The Secret differs from server state or the original encryption key. Repeated syncs cannot repair this. Recover the matching bytes and follow the release's server-side recovery procedure. |
| Migration Jobs do not rerun after rollback | Content-derived Job names already exist. Run `pre` and resolve `jobs.will_run` before another full sync. |
| Repeated drift or restarts after sync | Argo CD is overwriting Job-managed annotations. Check `ignoreDifferences` and `RespectIgnoreDifferences` on the actual Application. |
| Sync reports success for an older operation or revision | Refresh and inspect operation history; terminate a stuck operation before retrying. Use the Argo CD CLI with pinned sources and verify with `post`. |

If credentials may already have been overwritten, stop automatic
reconciliation and use the release's recovery procedure with Qodo. Restore
Zitadel and LiteGit encryption keys exactly; generating replacements can
make persisted data unreadable. For more information, see
[Moving an Argo CD install off generated credentials](https://docs.qodo.ai/on-prem/qodo-on-prem-helm-chart-overview.md#moving-an-argo-cd-install-off-generatedsecrets-render-true).

Some older Argo CD controllers could reuse stale operation state when a
sync was started by writing the Application's `operation` field. Have your
controller owner verify the upstream fix and prefer the normal CLI/UI
sync path. For more information, see
[Correctly set operationState values on retry](https://github.com/argoproj/argo-cd/pull/28778).

## Next steps

* [Manage Qodo secrets with External Secrets Operator](https://docs.qodo.ai/on-prem/external-secrets.md): Configure credential ownership, synchronization, and rotation.
* [Configure Kubernetes add-ons with values files](https://docs.qodo.ai/on-prem/kubernetes-add-ons.md): Choose scaling, ingress, and provider overlays before the final Argo CD profile.
* [Helm chart configuration reference](https://docs.qodo.ai/on-prem/on-prem-helm-chart-reference.md): Review the values for your selected release.


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