Free O'Reilly E-Book: Learn GitOps & Kubernetes best practices with Argo CD: Up and Running. Download Now →

Free O'Reilly E-Book: Learn GitOps & Kubernetes best practices with Argo CD: Up and Running. Download Now →

Free O'Reilly E-Book: Learn GitOps & Kubernetes best practices with Argo CD: Up and Running. Download Now →

Application Dependencies with Argo CD

Jesse Suen

Kargo Custom Steps
Kargo Custom Steps

By Christian Hernandez and Jesse Suen. Originally published March 2024.
Updated August 2026 for Argo CD v3.5: Progressive Syncs is now Beta, native dependency ordering is under active proposal, and Kargo joins as a fourth pattern.

Since the term GitOps was coined almost a decade ago in 2017, adoption has spread and Argo CD deployments have grown more complex. Organizations that started with a handful of Applications are now running many more: in the 2026 Argo CD user survey, 42% of respondents reported managing more than 500 Applications, and scaling and performance is now the most-cited Argo CD challenge, reaching 50% among organizations running more than 2,000 Applications.

This post tackles a problem that shows up at that scale: how to make one Application wait on another. The same survey names dependency management among the top five missing features, with respondents reporting that sync waves alone are not enough to order deployments across Applications.

This application dependency problem surfaces during bootstrap or disaster-recovery rebuilds, not steady-state operation: a cluster comes up fine in staging, then fails in a fresh environment because something depended on an Application that wasn't there yet. The usual suspects:

  • A mutating webhook that needs to exist before dependent pods start

  • A CRD that needs to be established before its custom resources apply

  • A database that should be healthy before the service in front of it starts taking traffic

Four patterns have developed to handle this, each with different trade-offs depending on the number of Applications involved and whether a promotion pipeline is already part of the setup. This post walks through each pattern, its prerequisites, and where Argo CD's own native answer to this problem stands today.

What Argo CD Sync Waves Can and Cannot Order

Before jumping into the four patterns, it's worth covering how Argo CD thinks about Applications and where sync waves reach their limits.

An Argo CD Application is a collection of Kubernetes resources, such as Deployments, Services, and Ingress objects, treated as a single unit. It is the atomic unit of work in Argo CD.

By default, Argo CD applies the manifests inside an Application as-is, with no ordering guarantees. That becomes a problem when order matters, such as a CustomResourceDefinition needing to exist before its corresponding CustomResource. Sync waves and sync phases solve this within an Application: waves control the order resources apply in, phases control pre-sync and post-sync hooks. A Namespace annotated with sync-wave: "1" applies before a Pod annotated with sync-wave: "2".

apiVersion: v1
kind: Namespace
metadata:
  name: web
  annotations:
    argocd.argoproj.io/sync-wave: "1"
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: web
  annotations:
    argocd.argoproj.io/sync-wave: "2"
spec:
  containers:
    - name: nginx
      image

Full details on sync waves and phases are in the Argo CD documentation.

Argo CD Applications are isolated by design. Application independence is what lets Argo CD manage thousands of Applications without one Application's problems blocking every other sync. A controller that had to track cross-Application relationships natively would be slower and harder to reason about at exactly the scale where it matters most. The trade-off is that dependency ordering has to be built on top of the Application spec rather than declared inside it.

Scope matters here: a sync-wave annotation on an api Application stays scoped to that Application. It does not carry over to when a separate database Application syncs. Ordering across Applications is not something the Application spec currently supports but it's achievable via sync waves on child Applications in an app-of-apps pattern, or via ApplicationSet RollingSync for Applications generated by a single ApplicationSet. Requests for finer-grained ordering go back to 2020. The four patterns below exist specifically to close that gap.

Prerequisites: Configuring Health Checks Before Ordering Applications

Before choosing a pattern, configure three things so that Argo CD reports Application health accurately:

  • Readiness and liveness probes

  • Custom Argo CD health checks

  • Application-level health

Without all three, Argo CD can mark a dependency "Healthy" before the workload behind it can serve traffic, which breaks the ordering guarantee every pattern below relies on.

Kubernetes Readiness and Liveness Probes

Argo CD determines Application health from the collective health of the resources inside it. Liveness probes confirm a resource is running; readiness probes confirm it can accept traffic. Without both configured, Argo CD can mark a resource "Healthy" and "Synced" while it is still initializing.

Consider a MySQL StatefulSet deployed without probes: Argo CD marks it healthy as soon as the pod starts, regardless of whether MySQL has finished its setup process or can accept connections yet. Adding probes fixes this:

spec:
  template:
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          livenessProbe:
            tcpSocket:
              port: 3306
            initialDelaySeconds: 12
            periodSeconds: 10
          readinessProbe:
            exec:
              command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
            initialDelaySeconds: 12
            periodSeconds: 10

With these in place, Kubernetes considers the StatefulSet alive once the port accepts connections, and ready once a query succeeds. More detail is available in the Kubernetes probe documentation.

Custom Argo CD Health Checks with Lua

Beyond default Kubernetes health, Argo CD ships built-in health checks for  the resource types listed in the resource_customizations directory,  written in Lua and visible in the Argo CD GitHub repository. Custom resources, particularly ones from Kubernetes Operators, often need a custom health check added through the resource.customizations field in the argocd-cm ConfigMap:

data:
  resource.customizations: |
    cert-manager.io/Certificate:
      health.lua: |
        hs = {}
        if obj.status ~= nil then
          if obj.status.conditions ~= nil then
            for i, condition in ipairs(obj.status.conditions) do
              if condition.type == "Ready" and condition.status == "False" then
                hs.status = "Degraded"
                hs.message = condition.message
                return hs
              end
              if condition.type == "Ready" and condition.status == "True" then
                hs.status = "Healthy"
                hs.message = condition.message
                return hs
              end
            end
          end
        end
        hs.status = "Progressing"
        hs.message = "Waiting for certificate"
        return hs

Full documentation on writing custom health checks is available  Argo CD health check documentation.

Enabling the Application Health Check in argocd-cm 

The health check for the Application CRD itself (argoproj.io/Application) was removed from Argo CD's defaults in version 1.8 (see issue #3781) and has not been restored as a default since. Each pattern below that depends on one Application waiting for another to become healthy, App-of-Apps in particular, requires this health check to be added back manually:

data:
  resource.customizations: |
    argoproj.io/Application:
      health.lua: |
        hs = {}
        hs.status = "Progressing"
        hs.message = ""
        if obj.status ~= nil then
          if obj.status.health ~= nil then
            hs.status = obj.status.health.status
            if obj.status.health.message ~= nil then
              hs.message = obj.status.health.message
            end
          end
        end
        return hs

With probes, resource health checks, and Application health checks all in place, the four patterns below become viable.

Pattern 1: Eventual Consistency with Argo CD Sync Retries

The simplest pattern uses no explicit dependency management at all. It relies on Argo CD retrying until dependent resources succeed, using sync options and a retry policy set directly on the Application manifest:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: simple-go
spec:
  destination:
    name: in-cluster
    namespace: demo
  source:
    repoURL: 'https://github.com/christianh814/simple-go'
    path: deploy/overlays/default
    targetRevision: main
  project: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - Validate=false
    retry:
      limit: 5
      backoff:
        duration: 5s
        maxDuration: 3m0s
        factor: 2

Validate=false disables resource validation, equivalent to kubectl apply --validate=false. Combined with the retry block, Argo CD keeps attempting the sync until it succeeds or the retry limit is exhausted. This effectively handles dependencies by not handling them explicitly, and simply trying again until the dependent resource exists.

The drawback: this only works for dependencies that resolve given enough time, not ones with a hard ordering requirement. An invalid manifest applied repeatedly stays invalid regardless of retry count. Installing Istio and confirming its sidecar injector is running before an application starts is that kind of hard requirement, not something retries can paper over.

Pattern 2: Ordering Applications with App-of-Apps and Sync Waves

App-of-Apps originated as a method for bootstrapping Argo CD itself: a "parent" Application that consists of other Applications, since an Application is, at its core, a Kubernetes CRD. Once the prerequisites above are in place, particularly the Application-level health check, App-of-Apps becomes a direct way to order deployment across multiple Applications using sync waves.

Consider a three-tier application: a database, a backend, and a frontend, deployed in that order. Each child Application gets a sync-wave annotation, with lower numbers taking priority:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: database
  annotations:
    argocd.argoproj.io/sync-wave: "1"
spec:
  # ...
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: backend
  annotations:
    argocd.argoproj.io/sync-wave: "2"
spec:
  # ...
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: frontend
  annotations:
    argocd.argoproj.io/sync-wave: "3"
spec:
  # ...

The parent Application is an ordinary Argo CD Application, distinguished only by the fact that its resources happen to be other Applications:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: parent
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  source:
    repoURL: 'https://github.com/christianh814/app-of-apps-example'
    path: argocd/applications
    targetRevision: main
  destination:
    name: in-cluster
    namespace: argocd
  project: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    retry:
      limit: 5
      backoff:
        duration: 5s
        maxDuration: 3m0s
        factor: 2

A full working example is available in this repository. Once the parent Application applies, Argo CD deploys the database first, waits for it to report synced and healthy, deploys the backend, waits again, then deploys the frontend. All three ordering guarantees hold only because the Application health check from the prerequisites section is in place; without it, Argo CD has no way to know a child Application is done deploying and will apply all three waves without waiting.

App-of-Apps remains the most direct and predictable way to order Applications relative to each other, and it is still the most-used pattern in the community: roughly 82% of respondents to the 2026 Argo CD user survey report using it in production.

Pattern 3: Health-Gated Rollouts with ApplicationSet Progressive Syncs

App-of-Apps solves ordering, but it doesn't scale gracefully: hand-writing a child Application manifest for every service works fine for three services and turns into real toil past a dozen. ApplicationSets fix the toil, not the ordering. An ApplicationSet is an Application factory, generating many Applications from one manifest through a generator, but ordering those generated Applications was never part of the original design. That gap is exactly what ProgressiveSyncs closes.

ProgressiveSyncs rolls out an ApplicationSet's generated Applications in defined steps, advancing to the next step only once every Application in the current step reports healthy:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: golist
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - srv: database
            path: apps/golist-db/
          - srv: backend
            path: apps/golist-api/
          - srv: frontend
            path: apps/golist-frontend/
  strategy:
    type: RollingSync
    rollingSync:
      steps:
        - matchExpressions:
            - key: golist-component
              operator: In
              values: [database]
        - matchExpressions:
            - key: golist-component
              operator: In
              values: [backend]
        - matchExpressions:
            - key: golist-component
              operator: In
              values: [frontend]
  template:
    metadata:
      name: '{{srv}}'
      labels:
        golist-component: '{{srv}}'
    spec:
      project: default
      source:
        repoURL: 'https://github.com/christianh814/app-of-apps-example'
        targetRevision: main
        path: '{{path}}'
      destination:
        name: in-cluster
        namespace

The result mirrors App-of-Apps: database, then backend, then frontend, each waiting on the previous one's health. The difference is a single manifest generates and orders all three, rather than three separate Application definitions maintained by hand.

Status as of Argo CD v3.5:  ProgressiveSyncs moved from Alpha to Beta in Argo CD v3.3 and is documented as generally stable. It still requires explicit enablement (--enable-progressive-syncs on the ApplicationSet controller) and still carries real edge cases worth knowing about before relying on it in production.

 RollingSync forces autosync off on every generated Application, so syncPolicy.automated set inside the template gets overridden with a controller warning rather than an error. More significantly, RollingSync currently does not trigger a rollout for changes to .spec.sources.valuesObject (inline Helm values), which means an inline values change can bypass the staged rollout and sync every Application at once rather than proceeding step by step. There's also an open request to scope RollingSync to specific classes of change, such as triggering only on Helm chart version bumps and not on ordinary value updates, which is not yet supported.

For teams managing dozens or hundreds of generated Applications across clusters or regions, ProgressiveSyncs remains the most direct way to get staged, health-gated rollout without hand-writing every Application. Teams relying on it in production should account for the valuesObject gap directly, either by avoiding inline values in favor of value files or by testing the rollout behavior for their specific change patterns.

Pattern 4: Dependency-Aware Promotion with Kargo

Kargo, created by Jesse Suen, Alexander Matyushentsev, and Hong Wang, the original creators of Argo CD, offers a fourth pattern, one that folds dependency ordering into a broader promotion pipeline rather than treating ordering as a standalone concern.

Kargo introduces a set of concepts: a Warehouse watches sources such as image registries or Git repositories for new artifacts. Freight is a versioned bundle of those artifacts, a new image tag or commit that becomes eligible for promotion. A Stage represents an environment, with a promotion template defining what happens when new Freight arrives.

The dependency-relevant piece is stage ordering. A Stage can declare requestedFreight.sources.stages, requiring Freight to have already passed through an upstream Stage before it becomes eligible for promotion to this one. In practice, this means a backend Stage can require Freight to have already promoted successfully through a database Stage, which is the same ordering guarantee App-of-Apps provides through sync waves and Application health, expressed instead as a promotion pipeline with a UI showing Freight flowing between Warehouses and Stages.

Kargo is the heaviest pattern of the four to adopt. It is a separate system to install and operate, and it is best suited to teams that need a full promotion pipeline (moving changes across dev, staging, and production with image watching and stage gating) rather than teams that only need one Application to wait on another. For more detail on continuous promotion and Kargo's promotion mechanics specifically, see Continuous Promotion with Kargo

Native Dependencies: The dependsOn Effort

Issue #7437, opened in October 2021, asked for a native way to block one Application's sync until another Application is deployed. It sat largely dormant until PR #25282, open since November 2025, introduced sync-groups: resources declare a sync-wave-group and sync-wave-group-dependencies, letting Argo CD build a DAG of dependencies instead of relying on linear wave numbers alone.

apiVersion: v1
kind: Pod
metadata:
  name: label-demo
  labels:
    argocd.argoproj.io/sync-wave: "-1"
    argocd.argoproj.io/sync-wave-group: "2"
    argocd.argoproj.io/sync-wave-group-dependencies: "0,1"

A resource in wave-group 2 waits until every resource in wave-groups 0 and 1 finishes syncing. The scope is still within a single Application, not across separate Applications, but it lays the groundwork for a cross-Application version.

The PR remains alpha, with no merge date, though the author confirmed in mid-2026 that its direction was discussed at an Argo community meeting. Worth tracking, not worth waiting on: the four patterns above remain the way to handle dependency ordering today.

How to Choose Between the Four Argo CD Dependency Patterns

The dependency is soft, a resource that will eventually succeed given retries rather than one with a hard ordering requirement: start with Eventual Consistency. It requires no new tooling and no prerequisites beyond what Argo CD already provides, but it cannot express "this must not deploy before that."

A small, fixed number of Applications need explicit ordering: use App-of-Apps. It is the most predictable of the four patterns and the easiest to reason about, provided the Application health check prerequisite is in place. It does not scale well to dozens of Applications managed by hand.

The same ordering problem exists at scale, across many generated Applications, clusters, or regions: move to ApplicationSets ProgressiveSyncs. Beta status means it is broadly production-viable, but the valuesObject rollout gap is worth testing against directly before relying on it for every kind of change.

Dependency ordering is really one piece of a larger promotion need, image watching, multi-environment gating, and a pipeline view of how changes move from dev to production: adopt Kargo. It is the most capable option and the most infrastructure to run.

Situation

Pattern

Prerequisite

Cost

The dependency is soft and will resolve given retries

Eventual Consistency

None beyond stock Argo CD

Cannot express "this must not deploy before that"

A small, fixed number of Applications need explicit ordering

App-of-Apps

Application health check in argocd-cm

Does not scale past roughly a dozen hand-written manifests

The same ordering problem at scale, across generated Applications or clusters

ApplicationSet Progressive Syncs

Beta feature gate enabled on the ApplicationSet controller

valuesObject rollout gap; test against your change patterns first

Ordering is one piece of a larger promotion need

Kargo

A separate system to install and operate

Most capable option and the most infrastructure to run

Curious about native support landing in Argo CD itself: track, but do not wait on, PR #25282. It shows real movement on a long-standing gap, but it is alpha, scoped to within a single Application so far, and without a release commitment.

Argo CD Application Dependencies in 2026: Where Things Stand

Argo CD still does not manage dependencies between Applications natively. What has changed is the maturity of the tools available to work around it: ProgressiveSyncs is Beta rather than Alpha, Kargo has grown from an early project into a complete promotion layer, and a concrete proposal for native DAG-based ordering is under active discussion rather than sitting idle. Eventual Consistency, App-of-Apps, ApplicationSets ProgressiveSyncs, and Kargo each solve the dependency problem at a different scale and level of investment, and all four remain viable choices today while the native solution continues to develop.

For more on GitOps best practices generally, the GitOps Best Practices Whitepaper is available for download. The Akuity community Discord also covers tips, tricks, and updates across the Akuity Platform, Argo CD, and Kargo; join at akuity.community.

Additional Resources

  • [Blog Post] Argo CD Architectures Explained: Single vs. Per-Cluster vs. Hybrid

  • [Guide] Continuous Promotion with Kargo 

  • [Blog Post] GitOps Is Incomplete Without Promotion: How Kargo Fixes That

  • [Docs] Argo CD Progressive Syncs documentation

  • [Docs] Argo CD Sync Phases and Waves

  • [GitHub] argoproj/argo-cd Issue #7437, Application dependencies

    Frequently Asked Questions about Argo CD Application Dependencies

    What is the App of Apps pattern?

    The App of Apps pattern is an Argo CD deployment strategy where a parent Application manages other child Applications. Sync waves and Application health checks let teams control deployment order across multiple Applications, ensuring dependencies such as a database before a backend service are respected. It treats a group of Applications as a single unit for deployment and lifecycle management.

    What are sync waves?

    Sync waves control the order in which resources or Applications apply during a sync. Each resource or Application carries a numeric wave value, and Argo CD applies lower-numbered waves first, moving sequentially to higher numbers. This ensures dependencies are respected, such as creating a Namespace before deploying Pods into it.

    What are ApplicationSets ProgressiveSyncs?

    ProgressiveSyncs is a Beta ApplicationSet feature that rolls out generated Applications in defined steps using the RollingSync strategy. Each step only proceeds once every Application in the previous step reports healthy, allowing a single ApplicationSet manifest to manage ordered, multi-environment rollouts without hand-writing individual Applications.

    Does Argo CD support native dependencies between Applications?

    Not yet. Argo CD has no built-in dependsOn field on the Application spec. Issue #7437 tracks the request, and an active proposal (PR #25282) introduces DAG-based sync-wave groups, though it remains alpha and scoped within a single Application. Eventual Consistency, App-of-Apps, ApplicationSets ProgressiveSyncs, and Kargo are the current patterns used to fill this gap.

    How does Kargo handle Application dependencies?

    Kargo enforces ordering through Stage requirements: a Stage can require Freight to have already passed through an upstream Stage before becoming eligible for promotion. This produces the same ordering guarantee as sync waves in App-of-Apps, expressed as a promotion pipeline rather than Application annotations, and is most useful for teams that already need image watching and multi-environment promotion alongside dependency ordering.

Ready to simplify delivery with Akuity?

Deploy, promote, and operate applications reliably, powered by OSS you trust and Intelligence you control.

Ready to simplify delivery with Akuity?

Deploy, promote, and operate applications reliably, powered by OSS you trust and Intelligence you control.

Ready to simplify delivery with Akuity?

Deploy, promote, and operate applications reliably, powered by OSS you trust and Intelligence you control.

Sign Up for Akuity Updates

Practical guidance on MTTR reduction, GitOps at scale, and safe automation, with product updates from the Argo CD and Kargo team.

@2026 Akuity Inc. All rights reserved.

Akuity Inc. 440 N. Wolfe Road, Sunnyvale, CA 94085-3869 US +1-510-771-7837

SOC2 Type 2 Compliant

Sign Up for Akuity Updates

Practical guidance on MTTR reduction, GitOps at scale, and safe automation, with product updates from the Argo CD and Kargo team.

@2026 Akuity Inc. All rights reserved.

Akuity Inc. 440 N. Wolfe Road, Sunnyvale, CA 94085-3869 US +1-510-771-7837

SOC2 Type 2 Compliant

Sign Up for Akuity Updates

Practical guidance on MTTR reduction, GitOps at scale, and safe automation, with product updates from the Argo CD and Kargo team.

@2026 Akuity Inc. All rights reserved.

Akuity Inc. 440 N. Wolfe Road, Sunnyvale, CA 94085-3869 US +1-510-771-7837

SOC2 Type 2 Compliant