Key Takeaway
Argo CD 3.5, released on 4 August 2026, adds opt-in mTLS for repo-server traffic, Source Integrity commit verification configured per AppProject, Helm 4 rendering and concurrent ApplicationSet updates. Before upgrading from 3.4, audit plain-HTTP OCI repositories, SSH known_hosts, UI extensions, custom gRPC clients and any GnuPG signatureKeys.
The upgrade I would have been caught by is the quiet one. A Helm chart pulled from an internal registry over plain HTTP renders fine on 3.4, and on 3.5 it stops, because Helm 4 no longer assumes what Helm 3 did. Nothing in the release headline mentions it.
This post reads the v3.5.0 release page, the 3.4 to 3.5 upgrade notes and the new mTLS and Source Integrity documentation, then lays out what changed, what breaks, and the order I would roll it out in. I have not run 3.5 in production, so every behaviour below is attributed to the document that states it.
What 3.5 actually ships
The v3.5.0 release is dated 4 August 2026 and the page lists 855 commits since the previous tag. Container images are signed with cosign, and provenance is generated for images and CLI binaries at SLSA Level 3. The features list includes mTLS in the repo-server (#26715), a Helm 4 migration (#28076), ApplicationSet concurrency when managing applications (#26642), impersonation for server operations (#26898) and Git tag path prefixes (#27290).
Three maturity changes matter more than any single feature. The documentation entries promote the Source Hydrator to beta (#28229), impersonation to beta (#27576) and ApplicationSet in any namespace to stable (#27417). Stable is the one that lets you stop treating a multi-tenant ApplicationSet layout as an experiment.
InfoQ's write-up, published on 26 June while 3.5 was still a release candidate, adds the new ApplicationSet UI with list, filter and detail views and a Preview Apps tab. That detail comes from InfoQ, not from the release page I read, so treat it as reported rather than verified.
The upgrade notes: what to check before 3.5
The 3.4 to 3.5 upgrade page splits its content into breaking changes, behavioural changes and deprecations. I turned them into a table of who is affected and what to do. If a row says nobody is affected, you can skip it, but you have to know which row you are.
| Change in 3.5 | Who is hit | What to do |
|---|---|---|
| Helm 4 renders charts | Plain-HTTP OCI repos and OCI dependencies | Re-register with the insecure-oci-force-http option |
| React 19 UI | Teams with installed UI extensions | Rebuild extensions with react/jsx-runtime externalised |
| Event gRPC methods return an Argo CD EventList | Custom gRPC clients and generated REST clients | Regenerate clients; REST paths and JSON are unchanged |
| Impersonation covers all API server operations | Anyone using destinationServiceAccounts | Grant those service accounts get, patch, list and create where needed |
| SSH host keys from argocd-ssh-known-hosts-cm | Repos without explicit credentials relying on a mounted known_hosts | Move the host keys into the ConfigMap |
| Per-application CSS class gets a user-app- prefix | Custom UI CSS targeting application names | Add the prefix to selectors |
Notice what is absent. The upgrade page lists no entries under API changes or security changes, and no item on the release page is labelled breaking. The risk is concentrated in behaviour, which is exactly the kind that a green rollout hides until the first sync.
Helm 4 and the plain-HTTP OCI trap
Argo CD 3.5 bundles Helm 4. The upgrade notes call out the change that bites: Helm v4 requires an explicit plain-HTTP flag for plain HTTP OCI registries, and Helm v3 accessed OCI dependencies without registration, which no longer works. Existing repositories need the insecureOCIForceHttp field set to the string true in the repository Secret, or the CLI 3.5 flag with upsert.
# Re-register a plain-HTTP OCI chart repo for Helm 4 (Argo CD CLI 3.5).
# Without --insecure-oci-force-http, Helm 4 will not fall back to HTTP.
# Do NOT also pass --insecure-skip-server-verification: the upgrade notes say
# Helm 4 then drops --plain-http and fails with
# "http: server gave HTTP response to HTTPS client". No workaround is listed.
argocd repo add registry.internal:5000/charts \
--type helm --name internal-charts --enable-oci \
--insecure-oci-force-http --upsertA spec.source.helm.version value such as v3 is ignored once you are on 3.5, and Helm 4 renders the chart anyway. You do not have to remove it, but it is now a comment that lies, so I would clean it up in the same pull request that audits your charts for Helm 4 differences.
The documented limitation is the nasty part. Combining the skip-server-verification flag with the force-HTTP flag on the same chain makes Helm 4 drop its plain-http option, and the result is the error http: server gave HTTP response to HTTPS client. The notes state there is no workaround when both are required.
Repo-server mTLS, and what it does not do
Repo-server mTLS is opt-in and off by default. Create a Secret named argocd-repo-server-mtls holding client-ca.crt, client.crt and client.key. Argo CD mounts it at /app/config/reposerver/mtls in the relevant pods, and mTLS switches on once the files exist. The repo-server generates an ephemeral client certificate for its own liveness check, so probes need no change.
# The shared Secret from the 3.4 to 3.5 upgrade page. mTLS turns on
# automatically once these files exist at /app/config/reposerver/mtls.
apiVersion: v1
kind: Secret
metadata:
name: argocd-repo-server-mtls
namespace: argocd
type: Opaque
stringData:
client-ca.crt: |
<PEM-encoded CA certificate that signed the client certs>
client.crt: |
<PEM-encoded shared client certificate>
client.key: |
<PEM-encoded private key for the shared client certificate>
# Deprecated 3.4 way vs the 3.5 way, per the mTLS page:
# Wrong: --repo-server-strict-tls (works in 3.5 with warnings)
# Right: --repo-server-ca-cert-path=/app/config/server/tls/ca.crtRead the server-side paragraph of the mTLS page twice. The repo-server loads one CA pool and accepts any client whose certificate chains to it. It does not check Subject or SAN. So mTLS here authenticates callers but does not authorise them: the application controller and a stray pod holding the same shared certificate look identical. Per-component authorisation needs your own layer, such as an Envoy sidecar or a gRPC interceptor.
There is also a flag migration. The repo-server-strict-tls flag is deprecated on argocd-server, the application controller, the applicationset controller and the notifications controller. Point repo-server-ca-cert-path at /app/config/server/tls/ca.crt and keep the volume mount. The old flag still works in 3.5 with warnings, and the notes say it may be removed in v3.6 or later.
To verify mTLS is really enforced, look for the log line Generated ephemeral health-check client certificate in the repo-server, then confirm that a client without a certificate fails the TLS handshake. Both checks come straight from the mTLS page; a rollout that only checks pods are Ready proves neither.
Source Integrity replaces GnuPG signatureKeys
Source Integrity is configured in each AppProject under spec.sourceIntegrity. If the criteria are not met, the application does not sync and reports a ResourceComparison error. Each policy has a list of repo URL globs, where a leading exclamation mark negates a pattern, and a gpg block with a mode and a list of trusted key IDs. Only one policy applies to a given repository, and a source matching no policy is not verified.
The mode decides how much history you are betting on. The table shows the three values the documentation defines.
| gpg mode | What is verified | Where it hurts |
|---|---|---|
| none | Nothing. Unsigned commits and invalid signatures are accepted | It is an explicit opt-out, so it hides intent from reviewers |
| head | Only the target revision: the tag signature for an annotated tag, else the commit signature | Matches the legacy signatureKeys behaviour, so it is the safe migration target |
| strict | The target revision and every ancestor | Old unsigned commits or retired keys block syncs; seal commits are the documented fix |
# AppProject: start in head mode, the closest match to legacy signatureKeys.
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: payments
namespace: argocd
spec:
sourceIntegrity:
git:
policies:
- repos:
- url: "https://github.com/my-group/*"
- url: "!https://github.com/my-group/ignored.git" # leading ! excludes
gpg:
mode: head # none | head | strict
keys:
- "D56C4FCA57A46444" # key ID from the docs example
# Import the public key first; the repo-server keyring is rebuilt from
# argocd-gpg-keys-cm on restart, so manual edits inside the pod are lost.
# argocd gpg add --from ./release-key.asc
# Do not keep .spec.signatureKeys here: the two cannot be used together.Migration is mechanical. Remove spec.signatureKeys, define a policy with url star, mode head and the same key IDs, and you have the legacy behaviour. If sourceIntegrity is absent but signatureKeys is set, Argo CD converts it automatically, but the docs recommend migrating by hand because signatureKeys will be removed, and the two cannot coexist in one project.
Impersonation, ApplicationSet concurrency and the UI
Impersonation now applies to every API server operation, where previously it covered sync only. Viewing, patching, deleting, listing events, reading pod logs and running resource actions all use the service account from the AppProject destinationServiceAccounts. If those accounts were scoped to the verbs a sync needs, the UI will start returning permission errors after the upgrade.
ApplicationSet concurrency comes from pull request 26642. It lets the controller update the Applications it manages in parallel, where it used to go one at a time, which the PR says could take many minutes for hundreds of applications. The flag is concurrent-application-updates with an environment variable ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_APPLICATION_UPDATES. The default of 1, which keeps the sequential behaviour, is stated in the PR review summary rather than in code I read.
# ApplicationSet controller: the flag and env var named in PR 26642.
# The PR review summary gives the default as 1 (sequential). Step up slowly.
env:
- name: ARGOCD_APPLICATIONSET_CONTROLLER_CONCURRENT_APPLICATION_UPDATES
value: "4" # example only: my own starting point, not a measured valueRaising it is a trade against your API server and the repo-server. I would step it up in small increments while watching reconcile latency, not set a large number because the option exists. I have no measurement of the right value and will not invent one.
Where it can go wrong
Source Integrity has documented limits that matter for a real fleet. Only Git sources are supported, so Helm and OCI sources cannot be verified. Signature verification is not supported for ApplicationSets populated by the git generator when the project field is templated. And with source integrity enforced, argocd app sync with the local flag no longer works, which removes a debugging habit many teams rely on.
The keyring is the second trap. The keyring inside repo-server pods is rebuilt from the argocd-gpg-keys-cm ConfigMap on restart, so manual changes in the pod are lost, and if the keyring falls out of sync the documented fix is to restart the repo-server pods. A key import can also take time to propagate across the cluster, which looks exactly like a signature failure.
Do not start in strict mode on a long-lived repository. The documentation warns that full-history verification can fail on older unsigned commits or retired keys. Start with head, then add a seal commit before you move a repo to strict.
Two source conflicts are worth knowing about. InfoQ describes Source Integrity as sourceIntegrity.required on the Application spec with an app set flag, while the Argo CD documentation puts it in the AppProject under spec.sourceIntegrity. I followed the documentation. The upgrade page also names Helm 4.2.0 in one section and 4.2.1 in another without reconciling them, so check the version your image actually reports.
What to do on Monday
Do the audit on 3.4 first, because every item is checkable before you change anything. This is the order I would follow.
- Search every Application and repository Secret for plain-HTTP OCI registries and OCI dependencies, and decide whether to move them to HTTPS or register them with the force-HTTP option.
- List AppProjects that still use signatureKeys and write the equivalent head-mode policy, so the migration is a reviewed diff rather than an automatic conversion.
- Find the deprecated repo-server-strict-tls flag in your manifests and replace it with repo-server-ca-cert-path before it disappears.
- Check destinationServiceAccounts RBAC for the verbs the UI and API now use, and move any known_hosts you mounted into argocd-ssh-known-hosts-cm.
- Upgrade a non-production instance, enable mTLS there, confirm a certificate-less client is refused, and only then plan production.
The commands below cover the audits that can be scripted. Adjust the namespace to yours.
# 1. AppProjects still on the deprecated GnuPG config
kubectl -n argocd get appprojects -o json \
| jq -r '.items[] | select(.spec.signatureKeys) | .metadata.name'
# 2. Applications that pin a Helm version (ignored on 3.5, Helm 4 renders)
kubectl get applications -A -o json \
| jq -r '.items[] | select(.spec.source.helm.version != null)
| [.metadata.namespace, .metadata.name, .spec.source.helm.version] | @tsv'
# 3. The deprecated strict-TLS flag in your workloads
kubectl -n argocd get deploy,sts -o yaml | grep -n -- '--repo-server-strict-tls'
# 4. Which Helm version the repo-server image reports (the notes say 4.2.0 and 4.2.1)
kubectl -n argocd exec deploy/argocd-repo-server -- helm versionMy rule for this release: the features are optional, the behaviour changes are not. Treat Helm 4, impersonation scope and SSH host keys as the real upgrade, and adopt mTLS and Source Integrity afterwards, in head mode, one project at a time.
Sources and further reading
- GitHub: Argo CD v3.5.0 release notes
- Argo CD docs: upgrading from 3.4 to 3.5
- Argo CD docs: mutual TLS (mTLS) for repo-server
- Argo CD docs: Source Integrity verification overview
- Argo CD docs: Source Integrity for Git GnuPG
- GitHub: pull request 26642, concurrency when managing applications
- InfoQ: Argo CD 3.5 internal mTLS and Source Integrity

