Open-Source Wikis

/

Argo CD

/

Applications

/

Application controller

argoproj/argo-cd

Application controller

The application controller is the heart of Argo CD. It watches Application and AppProject CRs, reconciles them against live Kubernetes state, drives sync operations, and publishes status into Redis.

Purpose

  • Watch Application CRs across one or more namespaces and clusters.
  • For each Application, retrieve rendered manifests from the repo server, compare against live state, compute diffs, and report status.
  • Execute sync operations (apply, prune, hook lifecycle, retries, self-heal).
  • Maintain cluster informers per destination cluster and a sharded workload distribution.
  • Publish per-app cache entries that the API server reads.

Where it lives

Path Purpose
cmd/argocd-application-controller/commands/argocd_application_controller.go Cobra entry point and flag wiring.
controller/appcontroller.go The reconciliation loop, ~105 KB.
controller/state.go Compares desired vs. live state, orchestrates the comparison pipeline. ~54 KB.
controller/sync.go Sync execution: apply order, hook phases, retries, self-heal.
controller/hook.go PreSync/Sync/PostSync/SyncFail hook handling.
controller/health.go Application health computation from per-resource health.
controller/sharding/ Sharding strategies for distributing apps across replicas.
controller/cache/ Per-controller cache wrappers around Redis.
controller/metrics/ Prometheus metrics.
controller/clusterinfoupdater.go Periodically updates ClusterInfo Secrets.
controller/syncid/ Generates unique sync operation IDs.
controller/hydrator/ Manifest hydrator integration (sources → hydrated branch).
controller/sort_delete.go Resource-deletion ordering helpers.

Reconciliation loop

graph TD
    Watch[Application informer] --> Q[Workqueue + rate limiter]
    Q --> Refresh{Refresh?}
    Refresh -->|hard or soft| Comp[CompareAppState - state.go]
    Comp --> Repo[(Repo server: GenerateManifests)]
    Comp --> Live[(Cluster cache: live state)]
    Comp --> Diff[Compute diff + sync status]
    Diff --> Status[Update Application status]
    Status --> Redis[(Redis cache for API server)]
    Q --> Op{Operation?}
    Op -->|sync| Sync[ExecuteSyncOperation - sync.go]
    Sync --> Hooks[Hook phases - hook.go]
    Hooks --> Apply[Apply manifests via gitops-engine]
    Apply --> Status

Two parallel work queues are maintained: a status processor queue (refresh/compare) and an operation processor queue (active syncs). Their depths are configured by --status-processors and --operation-processors (defaults 20 each in cmd/argocd-application-controller/commands/argocd_application_controller.go).

Refresh cadence:

  • Soft refresh every --app-resync seconds (default 180 = 120 + jitter), reusing repo-server cache.
  • Hard refresh every --app-hard-resync seconds, ignoring caches.
  • Webhook-driven refresh comes in via the API server.

Sync mechanics

controller/sync.go (and the underlying gitops-engine library vendored at gitops-engine/) handle:

  • Apply order — namespaces first, then non-namespaced, then namespaced; with weight overrides via the argocd.argoproj.io/sync-wave annotation.
  • Hook phases — PreSync → Sync → PostSync, with SyncFail on failure. See controller/hook.go.
  • Retry policy — backoff/limit on the Application's syncPolicy.retry.
  • Self-heal — re-sync on drift if selfHeal: true. Exponential backoff is in --self-heal-backoff-* flags.
  • Sync timeout--sync-timeout-seconds aborts long-running operations.
  • Server-side diff/apply — controlled by --server-side-diff-enabled flag and per-app annotations.

Sharding

controller/sharding/ distributes Applications across controller replicas. The strategy is configurable (legacy/round-robin/cluster). Sharding decides which controller pod owns which Application and is keyed off the destination cluster.

Cluster cache

Each registered cluster gets a long-lived informer (util/settings/cluster_informer.go) that mirrors all watchable resources into memory. The controller queries this cache for live state during diffing, avoiding API calls per refresh.

Resource customizations

Custom health and resource actions for arbitrary CRDs live under resource_customizations/<group>/<kind>/. Each may contain health.lua, actions/<verb>.lua, and actions/discovery.lua. The Lua engine is util/lua/lua.go (using gopher-lua).

Manifest hydrator integration

When --hydrator-enabled=true, the controller drives the manifest hydrator pipeline:

  1. Fetch dry sources via the repo server.
  2. Hydrate them locally (util/hydrator/, controller/hydrator/).
  3. Push the hydrated manifests to the commit server, which commits them to a hydrated branch.
  4. Sync the Application from the hydrated branch.

See Manifest hydrator.

Configuration surface (selected flags)

  • --repo-server, --commit-server — peer addresses.
  • --app-resync, --app-hard-resync, --app-resync-jitter — refresh cadence.
  • --status-processors, --operation-processors, --kubectl-parallelism-limit — concurrency.
  • --self-heal-timeout-seconds, --self-heal-backoff-* — self-heal pacing.
  • --application-namespaces — multi-tenant Application discovery.
  • --server-side-diff-enabled — global server-side diff.
  • --otlp-address — tracing.

Entry points for modification

  • New comparison rule → extend controller/state.go.
  • New sync semantics → extend controller/sync.go (or the upstream gitops-engine).
  • New hook annotation → extend controller/hook.go.
  • New sharding strategy → add a strategy under controller/sharding/.
  • New custom health or action → drop a Lua script under resource_customizations/<group>/<kind>/.

See also: features/gitops-sync, primitives/application.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Application controller – Argo CD wiki | Factory