Why Argo CD repo-server tuning matters for large Helm and Kustomize workloads
When applications take too long to refresh or the repo-server pod is OOM-killed during manifest generation, the first instinct is often to add replicas or memory. That can help, but the repo-server is the component that runs Helm and Kustomize on repository content, so its cost depends on how many manifests are generated at once, how long each tool run lasts, and how much YAML or JSON a single directory-type Application pulls into memory. Those are the levers to examine before adding capacity blindly.
The Argo CD security documentation makes the same point from a risk angle: the repo-server executes Helm and Kustomize against whatever is in the repository, so the size and trustworthiness of that content are part of the operating model. Before changing any setting, confirm which Applications use Helm, Kustomize or plain directories, how large their rendered output is, and whether one monorepo path feeds many Applications. Check the repo-server logs and resource usage during a refresh to see whether the problem is concurrency, a single slow render, or one oversized directory. Those findings determine whether you tune parallelism, timeouts, cache lifetime or directory limits, or scale out.
Tuning is the right first step when load is spiky and per-render cost is high, because it protects the pod while you learn the workload. Scaling replicas is the right step when steady demand exceeds what one repo-server can serve even with sensible limits. Plan to confirm the exact settings, defaults and flags for your Argo CD version against the Argo CD high availability guide before you change them in production.
Repo-server settings and when each one fits
This table maps each supported repo-server setting to the symptom it addresses, what it costs you in return, and what to confirm in your own cluster before changing it. Read it by symptom first: if manifests are being generated too slowly, too many at once, or not at all, find that row and check its trade-off before touching the value.
| Setting | Symptom it addresses | Trade-off | What to confirm in your environment |
|---|---|---|---|
| --parallelismlimit flag | Repo-server pods are OOM-killed when many applications refresh at once, usually after a commit touches many apps | Lower concurrency means slower total refresh time, and applications can queue behind each other | Peak memory per Helm or Kustomize render, how many applications share one repo-server, and whether refreshes cluster at the same time |
| ARGOCD_EXEC_TIMEOUT environment variable | Helm or Kustomize renders are cut off before they finish, showing timeout errors on large charts or overlays | A higher limit lets a runaway render hold a worker longer and delays other applications | Your slowest render time under load, and the default of 90 seconds against your measured times |
| --repo-cache-expiration flag | Repeated renders of unchanged content waste CPU, or cached manifests go stale after a tool or dependency change | Longer caching cuts Git and render work but can hide changes to Helm dependencies or external sources until expiry | Whether your charts pull remote dependencies, and how often you change tool versions, against the 24-hour default |
| reposerver.max.combined.directory.manifests.size in argocd-cmd-params-cm | Directory-type Applications load very large volumes of YAML or JSON and exhaust repo-server memory | A lower cap blocks legitimate large directories, so the application fails until the directory is split or the cap is raised | Total size of plain YAML or JSON directories per Application, and whether those directories should be Helm or Kustomize sources instead |
| Git retry count settings | Intermittent fetch failures from the Git host cause sync errors that clear on the next attempt | More retries hide a failing network or credential problem and delay the error reaching users | Git host rate limits and network path to the repo-server, and whether failures correlate with provider outages |
| Repo-server replica count | Manifest generation backlog grows across many applications even after tuning concurrency and timeouts | Each replica adds memory and CPU cost, and more replicas do not help a single oversized render | Number of applications and repositories, measured queue time and per-pod memory, and whether one large chart dominates load |
| Repo-server resource requests and limits | Pods are throttled or evicted under load, which looks like slow or failed generation | Higher limits reserve more node capacity and can reduce the number of pods that fit a node | Observed peak memory and CPU per pod, and the node capacity your cluster can dedicate to Argo CD |
Matching symptoms to the right setting
Repo-server pods that restart with an out-of-memory status usually fail because too many manifest generations run at once, or because one directory-type Application loads a large volume of YAML or JSON into memory. Limiting concurrency is the first lever, because it lowers peak memory without changing what each generation does.
- Concurrent generations: Set
--parallelismlimiton the repo-server to cap how many manifests are generated at the same time, then compare restart counts before and after. - Directory size: Check whether a directory-type Application is the large one. The
reposerver.max.combined.directory.manifests.sizekey inargocd-cmd-params-cmlimits how much it can load, so confirm the value with your team before lowering it. - Memory limits: Compare the container memory limit with observed usage. A limit set below normal peak usage will restart pods even at a safe concurrency level.
- Replica count: Add repo-server replicas only after confirming that concurrency, not a single oversized application, is the cause.
- Recent changes: Note which repositories gained new Helm charts or Kustomize bases before the restarts began.
Check the repo-server container restart reason, its memory usage over time and the logs around each restart to match a spike to a specific Application.
Timeouts appear when rendering a chart or overlay takes longer than the allowed window, often because of slow dependency downloads, heavy templating or a large number of resources. The first setting to test is the exec timeout, but a longer timeout only helps if the render itself is reasonable.
- Exec timeout:
ARGOCD_EXEC_TIMEOUTsets the Helm or Kustomize timeout, which defaults to 90 seconds. Raise it in small steps and record the render time for the slowest Application. - Chart dependencies: Slow
helm dependencydownloads or remote Kustomize bases often dominate render time. Confirm whether the network path to registries and Git hosts is slow from the repo-server network. - Parallel load: A low concurrency limit can queue renders behind each other, so a timeout may reflect waiting rather than slow tooling.
- Template size: Very large rendered output, such as thousands of resources in one Application, is a sign that the Application should be split.
- Tool versions: Compare the Helm or Kustomize version in the repo-server image with the one your authors use locally.
Check the repo-server logs for the timeout message, the Application conditions that report the failure and the time each render takes in your own local test of the same source.
When many applications refresh slowly at once, the repo-server is usually busy generating manifests that were not needed, or the controller is waiting for repo-server responses. Reducing unnecessary generations and reviewing caching are the first steps, before adding capacity.
- Manifest cache lifetime:
--repo-cache-expirationchanges the 24-hour manifest cache. A longer lifetime reduces regeneration but delays picking up changes that do not move the Git revision, so test it against your change pattern. - Parallelism queueing: If concurrency is capped, refreshes queue behind each other. Compare queue wait signals with the configured limit before raising it.
- Repo-server replicas: Additional replicas help when generation work is spread across many repositories, not when a single large repository dominates.
- Application count: Large fleets may also need controller tuning, so confirm that the repo-server is the bottleneck before changing it.
- Repository layout: Monorepos that regenerate every application on every commit increase load even with a healthy cache.
Check repo-server request latency, controller logs for repeated refresh attempts and the count of concurrent generations during the slow period.
Stale manifests mean Argo CD shows an older rendered result after a commit has landed. The likely cause is the manifest cache or Git fetch behavior, not the target cluster. Check whether the new revision was fetched before you change any cache setting.
- Cache lifetime: A cache that lives up to 24 hours can keep an old render until expiry or until the revision changes. Confirm which revision the Application reports and whether it matches the commit you pushed.
- Git retries: Transient fetch failures can leave the previous revision in place. Review the Git retry settings and the repo-server logs for fetch errors around the commit time.
- Refresh trigger: Confirm whether a webhook or polling interval delivered the change. A missed trigger looks like a stale manifest.
- Tool output: Helm or Kustomize output that does not change with the input can point to a values file or overlay reference that was not updated.
- Path scope: Check that the changed file lies inside the path the Application watches.
Check the revision shown in the Application against the Git history, the repo-server log lines for fetch and render events and the time of the last successful refresh.
Tuning sequence for a live repo-server
This sequence changes one repo-server setting at a time, starting from measured memory and generation time, so each change has a visible effect and a known rollback point. The output is a repo-server that generates manifests within its memory budget and a record of which setting fixed which symptom.
Measure current memory and generation time
The platform engineer records the repo-server's memory use, restart count and OOMKilled events, then reads the generation duration for the slowest Helm or Kustomize Applications from the controller and repo-server logs. Capture the baseline in a change ticket with the current replica count, the repo-server resource requests and limits, and the Argo CD version. Done means you can name the Application that is slowest and the peak memory the pod reaches during a refresh burst. Do not change any setting yet.
Set the parallelism limit against available memory
Set the repo-server's --parallelismlimit flag, which caps how many manifest generations run at once, so that the peak memory of that many concurrent renders fits inside the pod's memory limit. Divide the memory limit by the peak per-render memory you measured in the first step, leave headroom for the cache and the Git process, and apply the value as a single change. Verify by repeating a forced hard refresh across several Applications and confirming no OOMKilled restart appears in the pod status.
Adjust the exec timeout for the slowest chart or overlay
Raise ARGOCD_EXEC_TIMEOUT only if the slowest Helm chart or Kustomize overlay from step one hits the default 90-second timeout, which the high availability guide lists as the default. Change the value in the repo-server environment, keep it as small as the slowest real render allows, and redeploy. Verify that the Application reaches a successful sync with no timeout error in the repo-server log. If the render still runs long, the cause is usually chart size or dependency fetching, so investigate the chart before raising the timeout again.
Tune the manifest cache lifetime
Adjust --repo-cache-expiration only after the parallelism and timeout settings are stable, because the default manifest cache lasts 24 hours and a longer or shorter lifetime changes how often Helm and Kustomize run. Shorten it if Git changes that do not alter rendered output are not being picked up, or lengthen it if the same unchanged charts are regenerated repeatedly. Verify by watching the repo-server cache hit behavior and the generation count over one refresh cycle, and confirm that a real commit still produces a new manifest.
Cap directory manifest size
Set reposerver.max.combined.directory.manifests.size in argocd-cmd-params-cm to a limit that matches the largest plain YAML or JSON directory Application you intend to support, so one oversized directory cannot load unbounded content into repo-server memory. The security documentation describes this setting as the control for directory-type Applications. Confirm the change by syncing a directory Application just under the limit, then by deliberately exceeding it in a test Application and checking that the error names the limit.
Scale repo-server replicas last
Add repo-server replicas only after the per-pod settings are verified, because more replicas multiply memory use and do not fix a single oversized render. Increase the replica count by one, confirm the pods schedule on separate nodes where your cluster allows it, and rerun the baseline measurements from step one. The sequence is complete when the slowest Application meets its target, no pod restarts with OOMKilled, and the change record lists each setting with its before and after value.
Security risks when the repo-server runs Helm and Kustomize
The repo-server turns Git content into Kubernetes manifests by running Helm and Kustomize on that content. The security documentation treats this as a risk to control, and the cards below cover the parts of that risk that change when you tune performance settings.
Untrusted repositories run as tools
Helm and Kustomize execute against whatever a repository contains, so a repository that a team does not control can influence what the repo-server does. Before you raise parallelism or timeouts for a large chart, confirm which repositories Applications can reference and who can change them. Restrict source repositories in each AppProject to an approved list, and review pull requests to those repositories as you would review code that runs in your cluster.
Out-of-tree file access
The security documentation flags access to files outside the Application's own directory as a risk when the repo-server processes manifests. A chart or overlay that references paths above its root can pull content into generation that the reviewer never saw. When you tune manifest generation for a large workload, check that Applications point at narrow paths and that shared files are inside the repository the Application is allowed to read.
Credential exposure in logs
Argo CD redacts cluster, Git and OAuth2 credentials from API responses and logs. That protection covers Argo CD's own output, not everything a chart or plugin prints during generation. When you increase verbosity to diagnose slow renders, review the logs for values pulled from Secrets or templated into manifests, and restrict who can read repo-server logs. Confirm the redaction behavior for your Argo CD version before you rely on it for a log pipeline.
Limits that bound memory
Directory-type Applications load their YAML or JSON into repo-server memory, and the reposerver.max.combined.directory.manifests.size key in argocd-cmd-params-cm caps that load. Raising this limit so a large directory renders can move the failure from an OOM kill to a repo-server that accepts more content than you reviewed. Set the limit to the largest directory you have verified, and document who approved the change.
Timeouts do not replace trust
Raising ARGOCD_EXEC_TIMEOUT above its 90-second default gives a slow Helm or Kustomize run more time to finish, and it gives a hostile or broken chart more time to run. A longer timeout is a performance decision and should not be treated as a trust decision. Keep repository allowlists, project boundaries and RBAC unchanged when you change timeout values, and record the reason for each increase.
Replicas share the same exposure
Scaling repo-server replicas spreads generation work across more pods, but each replica runs the same Helm and Kustomize code on the same repository content. Adding replicas does not reduce the trust exposure, and it multiplies the places where unexpected files or binaries could appear. Keep the repo-server image pinned to a reviewed release, and confirm that the pods running generation have only the network and volume access they need.
Questions teams ask before tuning the repo-server
What drives the cost of tuning the Argo CD repo-server?
The main driver is how much investigation the workload needs before any setting changes. Cost rises with the number of Helm charts and Kustomize overlays, the number of repositories that feed the repo-server, how many clusters and applications depend on them, and whether memory and timeout problems come from one chart or from many. Tuning itself is a small part of the work. The larger share is reproducing slow or OOM-killed renders, measuring them, and confirming that a change fixed the cause without hiding a bad chart. Ask any partner for a quote built from a written scope that lists repositories, tool versions and the failure symptoms observed today. Repo-server settings are one-time changes, but the repo-server still recurs as a cost because new charts and growing repositories change the load over time.
Can our internal platform team tune the repo-server alone?
Often yes, if the team already runs Argo CD in production and can reproduce the problem in a non-production cluster. The settings involved are the parallelism limit, the Helm and Kustomize exec timeout, the Git retry count, the manifest cache lifetime, the directory manifest size limit and the number of repo-server replicas. Each one is documented in the Argo CD high availability and security guides, so the work is mostly measurement and change control. Teams usually need outside help when the cause is a chart that renders too much output, when repository access patterns involve many private Git hosts, or when the same fix must be proven across several environments for an audit. Confirm internally who owns the Argo CD configuration, who can change the argocd-cmd-params-cm ConfigMap and how changes are reviewed before deciding.
How long does repo-server tuning take for complex charts?
Duration depends on chart complexity, the number of repositories and how reproducible the failures are. A small set of charts with clear error messages usually takes less time to tune than a large monorepo where a single commit regenerates many applications. Complex charts with heavy templating, large generated manifests or many dependent subcharts need more render-time measurement before any limit is chosen. Repository count matters because each Git source has its own fetch, cache and retry behavior. We do not publish a duration. A written scope should state which repositories and applications are covered, which environments will be tested and what evidence the team receives at the end, and that scope is what sets the timeline.
What must our team provide before tuning starts?
The work goes faster and costs less when the team supplies the following before the scope is written:
- Representative workloads, including the Helm charts and Kustomize overlays that fail or run slowly, with the Argo CD Application manifests that reference them
- Memory data for the repo-server pods over time and any OOMKilled events from the cluster
- Error messages from the Application controller and repo-server logs, including any timeout text
- Git hosting details: provider, whether repositories are private, the authentication method and any rate limits or network paths the repo-server must cross
- Current values of the repo-server flags and environment variables, and the Argo CD version in use
Ask the partner to confirm how sample data is handled, since charts may contain internal names or values that need redaction.
Should we scale repo-server replicas or tune settings first?
Tune settings first, then scale replicas if the workload still needs more capacity. Adding replicas spreads load but does not fix a chart that uses too much memory on each render, and more concurrent renders can make OOM kills more likely on each pod. Start by measuring memory per render and the duration of slow Helm or Kustomize calls. Then set the parallelism limit so that concurrent generations fit within the memory available per pod, adjust the exec timeout only if renders are legitimately slow, and review the manifest cache lifetime against how often the repository changes. Replica count comes after these values are known, because each replica has its own cache and its own memory budget. Confirm the chosen values against the Argo CD high availability guide for the version you run.
Does tuning the repo-server create security risk we should review?
Yes, and the review should happen before settings change. The Argo CD security documentation notes that the repo-server runs Helm and Kustomize on repository content, so an untrusted repository or out-of-tree file access is a risk to control. Raising the directory manifest size limit or the exec timeout can allow larger or slower inputs to run, which widens what a bad repository can consume. Confirm which repositories are trusted, who can merge to them and whether the repo-server can read files outside the chart path. Keep changes recorded in Git so the reviewer can see the old and new values and the reason for each.
Which repo-server settings control memory and timeouts?
The settings that matter most are the parallelism limit, which caps concurrent manifest generations to help avoid OOM kills, and the ARGOCD_EXEC_TIMEOUT variable, which sets the Helm or Kustomize timeout (the default is 90 seconds). The --repo-cache-expiration flag changes the 24-hour manifest cache. The reposerver.max.combined.directory.manifests.size key in argocd-cmd-params-cm limits how much YAML or JSON a directory-type Application can load into memory. Verify flag names and defaults against the version you run, because the high availability guide is the reference for the values described here.
Limits to confirm in your environment
Repo-server tuning depends on values that differ by Argo CD version, chart size, node capacity and Git provider. The cards below list the settings and limits to confirm in your own environment before you change anything in production, so each adjustment is tested against the workload it is meant to fix.
Flag and variable names
Confirm the exact repo-server flag and environment variable names for the Argo CD version you run, including --parallelismlimit, ARGOCD_EXEC_TIMEOUT and --repo-cache-expiration. Names can change between releases, and a misspelled or ignored setting looks like a working change until the next slow sync. Check the running pod's arguments and environment after every rollout, and record the version alongside each tuning change.
Chart and overlay render time
Measure how long your largest Helm chart or Kustomize overlay takes to render on a quiet repo-server before deciding whether the default timeout is enough. Render time grows with dependency count, template functions and generated resources, so a chart that passes in staging can exceed the limit once production values are added. Raise the timeout only for the workloads that need it, and keep a note of which applications depend on the change.
Memory headroom on nodes
Check that the nodes hosting repo-server pods have enough free memory to absorb a concurrent render at the parallelism limit you choose. Include the directory manifest size limit, since a large plain YAML or JSON directory loads fully into repo-server memory. Set requests and limits from observed peaks under real load, and confirm the scheduler is not packing other memory-heavy pods onto the same nodes.
Git provider rate limits
Confirm the request quotas your Git host applies to the account or app Argo CD uses, since a higher Git retry count or a shorter manifest cache can multiply fetches. Check whether the host counts clones, API calls or both, and whether it applies limits per repository or per token. Pair tuning with webhooks or refresh jitter so caching changes do not turn into throttled polling across many applications.
Cache behavior after upgrades
Expect cached manifests to behave differently after an Argo CD upgrade, because a new version can change how generated output is keyed or refreshed. Plan a controlled refresh of a test application after each version change and compare rendered output before and after. Watch whether the cache lifetime you set still matches the revision and refresh behavior your release notes describe.
Sharding and replica interactions
Confirm how repo-server replica counts interact with application controller sharding, because each controller shard sends its own manifest generation requests. Adding repo-server replicas without checking the controller layout can spread load unevenly or leave a shard waiting on a busy replica. Test one change at a time, and observe queue depth and sync latency on each shard before rolling the setting to production.
What to confirm before changing production settings
Start by confirming which Argo CD version you run and reading the release notes for that exact version before you change any repo-server setting. Argo CD ships a minor release roughly every three months and only the three most recent minor versions receive patch releases, so a tuning guide written against an older flag set can point you at a parameter that has moved, been renamed or changed its default. Check the Argo CD high availability guide against your running version, and confirm how the repo-server flags, the ARGOCD_EXEC_TIMEOUT variable and the reposerver.max.combined.directory.manifests.size key are set in your install, whether through the manifests, Helm values or the argocd-cmd-params-cm ConfigMap. Write down the exact values currently in effect so you can restore them if a change does not behave as expected.
Make the change first in a non-production cluster that carries representative applications. A useful test set includes the largest Helm chart, the heaviest Kustomize overlay, the directory-type Applications with the most YAML, and the repositories that produce the most Git fetches. Synthetic apps with small manifests will not show whether a parallelism limit starves a real workload or whether a longer exec timeout hides a chart that should be fixed. Confirm that the non-production environment matches production in repo-server memory limits, replica count and the Git hosts it talks to, since a tuning result that depends on a different network path or memory ceiling will not carry over.
Record baseline timings before you touch anything. Capture how long a hard refresh takes for each test application, how often the repo-server restarts or is OOM-killed, the frequency of manifest generation errors in the repo-server logs, and the time between a Git commit and a completed sync. Repeat the same measurements after each single change, and change one setting at a time so the effect is attributable. Agree on these measurements with the platform and application owners in writing before the change window, because a faster refresh that hides failed generations is not an improvement.
How DeployOpen works on this
DeployOpen approaches repo-server tuning as a fixed-scope piece of work on your own Argo CD installation, starting from measured generation problems rather than guessed settings. Each change is built in your environment, verified against real applications, documented and handed to your team, with ongoing support available as a retainer.
How we scope
- Review which Helm and Kustomize applications fail, slow down or get OOM-killed, with logs and repo-server metrics supplied by your team.
- Agree in writing which settings change: parallelism limit, exec timeout, Git retry count, manifest cache lifetime and directory manifest size limit.
- Define the replica count and resource limits for repo-server, and which clusters and repositories are inside the engagement.
How we build
- Apply tuning changes through your normal GitOps flow so every parameter lives in version-controlled manifests.
- Test manifest generation for the slowest applications before and after each change, recording durations and memory use.
- Check repository content for untrusted Helm charts and out-of-tree file access, and confirm the repo-server settings match your security review.
How we hand over
- Deliver a written record of each setting, its previous and new value, and the test result that justified it.
- Document how to recognize repo-server saturation, including the signs that point to more replicas instead of higher limits.
- Walk your platform team through the rollback steps and the change process for future tuning.
How we support
- Offer ongoing support as a retainer for Argo CD upgrades, patch releases and changes to manifest generation behavior.
- Re-test generation times after major Helm chart, Kustomize or Argo CD version changes that affect repo-server load.
- Confirm with your team which Argo CD minor versions receive patch releases under the project's release cadence before scheduling work.

