Article · part of a guide
The No-Nonsense Guide to ArgoCD
ArgoCD isn't perfect. Here's a no-nonsense guide to its common problems, from sync timeouts to Helm hell, and how to fix them.

Key takeaways
- ArgoCD sync timeouts and high controller CPU usually stem from inefficient manifest generation, API server throttling, or bad caching, so tune controller parameters before adding more resources.
- ArgoCD runs 'helm template' rather than 'helm install' or 'helm upgrade', so pre-install and pre-upgrade hooks run on every sync and must be idempotent, and Helm's lookup function does not work.
- Sync Waves let you order App-of-Apps deployments so dependencies like Prometheus operator CRDs become healthy before dependent apps sync.
- ArgoCD maintainers now recommend against the ArgoCD Vault Plugin; use an operator-based pattern like External Secrets Operator instead to keep ArgoCD ignorant of Vault credentials.
- Passing Terraform outputs into ArgoCD-deployed apps via Parameter Store or commit-back-to-Git works but feels like duct tape; a platform that shares Terraform outputs between workspaces can shrink that handoff.
For a broader view of GitOps tools, see our 2025 comparison.
ArgoCD is the GitOps tool most Kubernetes teams reach for, but the official documentation only tells you how to drive it on a freshly paved road. Out in the real world, where the pavement ends, you'll find potholes, weird engine noises, and a whole lot of community forum posts from people stuck in the mud.
This is a field guide based on what people complain about on Reddit, GitHub issues, and Stack Overflow. It covers the problems that don't make it into the marketing material.
The Sync & Performance Nightmare
The most common sign something’s wrong in ArgoCD land is the dreaded sync timeout or an application controller pegging the CPU. I've seen teams immediately throw more memory and CPU at the problem, and it rarely works.
The cause is almost never raw resources. It’s usually one of these:
- Inefficient Manifest Generation: Your
argocd-repo-serveris choking because your Helm chart is a monster or your Kustomize setup is too complex. - API Server Throttling: The
argocd-application-controlleris hammering the Kubernetes API server too hard, causing it to throttle requests. - Bad Caching: You're not using caching effectively, forcing ArgoCD to re-calculate everything on every reconciliation loop.
Before you scale up, tune the controller. These are the knobs to look at first.
| Parameter | Component | Default | Why You Should Care |
|---|---|---|---|
--status-processors |
application-controller |
20 | Controls how many apps can be reconciled at once. Too low, and things get slow. |
--operation-processors |
application-controller |
10 | Controls how many sync operations can run at once. Increase if syncs are queueing up. |
ARGOCD_K8S_CLIENT_QPS |
application-controller |
50 | Rate limit for talking to the K8s API. If you see throttling, bump this, but watch your API server. |
timeout.reconciliation |
argocd-cm ConfigMap |
120s (+ up to 60s jitter) | How often Argo checks Git. The timeout.reconciliation.jitter (default 60s) adds random delay, so polls average about every 2.5 minutes. If you have thousands of apps, you don't need it checking that often. |
--parallelismlimit |
repo-server |
1 | Concurrent manifest generations. If your repo server is OOM'ing, this is a likely culprit. |
Start here. Tweak these values, watch your metrics, and only then consider giving it more raw power.
Helm Hooks Are a Trap
What trips up more teams than anything else is that ArgoCD doesn't run helm install or helm upgrade. It runs helm template.
So ArgoCD has no concept of an "install" vs. an "upgrade." It's just a "sync." The nasty side effect is that your Helm pre-install and pre-upgrade hooks both run on every sync. If your hooks aren't idempotent, meaning they can run over and over without causing problems, you're in for a world of pain.
The fix is to design your hooks to be harmless on repeated runs. For a one-off job, that means telling ArgoCD to clean up the hook resource after it succeeds.
# In your hook's manifest (e.g., a Job)
apiVersion: batch/v1
kind: Job
metadata:
name: my-presync-db-migration
annotations:
# This is the ArgoCD hook annotation
argocd.argoproj.io/hook: PreSync
# This tells ArgoCD to delete the Job object once the hook succeeds
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: db-migrator
image: my-company/db-migrator:1.2.0
# ... rest of your job spec
restartPolicy: Never
backoffLimit: 1Helm's lookup function is out too. Since helm template runs without cluster access, lookup won't work. You'll have to refactor your charts to pass that data in via values.
The App-of-Apps Spaghetti
The "App-of-Apps" pattern is the standard way to manage complex environments. You have a root app that deploys... other apps. It's a great idea until you have dependencies. What if your monitoring app needs the CRDs from your Prometheus operator app to be deployed first?
By default, ArgoCD syncs them all at once, so nothing guarantees the CRDs exist by the time the monitoring app needs them.
The solution is Sync Waves. It’s an annotation that puts the apps in order. Resources in lower-numbered waves are synced and must become healthy before ArgoCD moves on to the next wave.
# In your Prometheus Operator Application manifest
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prometheus-operator
annotations:
# Wave 0: Deploy the operator CRDs first
argocd.argoproj.io/sync-wave: "0"
# ...
---
# In your Monitoring Application manifest
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: kube-prometheus-stack
annotations:
# Wave 1: Deploy after the operator is ready
argocd.argoproj.io/sync-wave: "1"
# ...Without sync waves, App-of-Apps stops working once your apps depend on each other.
The Terraform-to-ArgoCD Chasm
This is the seam where CI/CD pipelines for Terraform and OpenTofu meet GitOps, and it gets awkward. Before you get here, it helps to settle where Terraform should stop and ArgoCD should take over, which is its own decision we work through in Terraform vs GitOps for Kubernetes. Say you provision an AWS RDS database with Terraform. The Terraform outputs, the database endpoint, username, and a secret ARN, now need to flow into the Kubernetes application that ArgoCD is deploying. How?
This is a classic handoff problem, and the community has come up with some clever, if clunky, workarounds:
- Parameter Store: Your CI pipeline runs Terraform, which writes the outputs to AWS Parameter Store. Then, your ArgoCD application uses the External Secrets Operator to read from Parameter Store and create a Kubernetes secret.
- Commit-Back-to-Git: Terraform uses a GitHub provider to commit a
values.yamlfile containing the outputs directly back into your GitOps repo.
Both of these work, but they feel like duct tape. Either an external store sits in the middle of your process, or your infrastructure tool pollutes your application configuration history.
This is where a more integrated platform makes a lot more sense. In Scalr, for instance, a workspace's outputs can be read by other workspaces through the scalr_outputs data source or remote state sharing, and run triggers start a downstream workspace after an upstream apply. Scalr doesn't deploy the application or drive ArgoCD itself, so you still need a handoff into the cluster, but the outputs come from one governed place. You solve the problem at the architectural level instead of patching over it with clever scripts.
Secrets: Just Stop Using the Vault Plugin
For years, people used the ArgoCD Vault Plugin (AVP) to inject secrets during manifest generation. If you're still doing this, stop. The ArgoCD maintainers themselves now officially recommend against it.
It's a security anti-pattern. Using AVP means your argocd-repo-server needs a credential to your Vault instance. This widens your attack surface. Worse, the rendered manifests, now with plaintext secrets, get stored in ArgoCD's Redis cache.
The modern, secure way is to use an operator-based pattern.
- The Operator: You install something like the External Secrets Operator (ESO) in your cluster.
- The Process: You commit an
ExternalSecretmanifest to Git. This manifest tells ESO where to find the secret in Vault (or AWS/GCP/Azure secret managers). ESO then fetches the secret and creates a native KubernetesSecretobject inside the cluster. - ArgoCD's Role: Your application, managed by ArgoCD, mounts the native Kubernetes
Secretlike it always would.
In this model, ArgoCD is completely ignorant of Vault. It doesn't need credentials, and it doesn't handle plaintext secrets. It just manages the ExternalSecret custom resource, and the operator handles the sensitive work. That's a much cleaner separation of concerns.
Where ArgoCD trips teams up
Most ArgoCD pain comes from a handful of failure modes the docs gloss over. Tune the controller parameters before scaling resources. Design hooks that survive repeated runs, since helm template reruns them on every sync. Use Sync Waves to order dependent apps, and hand secrets to an operator like External Secrets rather than the Vault Plugin. The happy path in the docs won't surface any of these. The community forum threads will.
Key Sources Used:
- Argo CD Official Documentation (argo-cd.readthedocs.io)
- r/ArgoCD & r/kubernetes on Reddit for community-reported issues
- Akuity Blog: How to Integrate Terraform with Argo CD for GitOps Workflows
- Akuity Blog: The 3 Most Common Argo CD Architectures Explained
- Argo CD GitHub Discussions and Issues
About the author

CEO at Scalr
Sebastian Stadil is the CEO of Scalr with 15+ years of DevOps experience. He started with AWS in 2004 and advised early Microsoft Azure and Google Cloud.
Part of this guide
25 sheets
CI/CD and GitOps for Terraform & OpenTofu
- Migrating GitHub Actions from an S3 Backend to Scalr
- Terraform Testing: terraform test, tofu test, Terratest
- Custom Hooks for Terraform and OpenTofu: Pre-Plan, Post-Apply, and the Hooks Registry
- Integrating Terraform with Backstage
- Top 10 Continuous Delivery Tools
- Top 10 GitOps Tools: A Comprehensive Guide
- Everything you need to know about using Terraform or OpenTofu with Slack
- Using Terraform with GitLab
- Key DevOps Metrics You Should Be Tracking
- Terraform and OpenTofu with Dependabot
- Terraform Notifications in Microsoft Teams
- The Complete Guide to DevOps Monitoring Tools: Choosing the Right Solution for Your Infrastructure
- Top Jenkins Alternatives & Specialized IaC Tools
- Why You Should Use Dependabot with Terraform and OpenTofu
- Mastering Terraform at Scale: A Developer's Guide to Reliable Infrastructure
- Integrating Terraform Events w/ AWS EventBridge
- How to use the Azure DevOps Terraform Provider
- Deploying your infrastructure with Scalr and GitHub Actions
- Terraform Configuration Ingestion
- Setting up Scalr & Azure DevOps Part 2 - Add Azure credentials
- Setting up Scalr & Azure DevOps Part 3 - Execute Your Terraform Code and Create a Workspace
- Setting up Scalr & Azure DevOps Part 1 - Picking a Workflow
- Terraform Cost Estimation in 2021: The Definitive Guide