Deep Dive into Argo CD Core Feature Functionalities: GitOps Controller Architecture Explained

Argo CD continuously reconciles Git-stored desired state with live Kubernetes cluster state through a six-stage pipeline involving manifest generation, normalization, diff computation, and wave-based synchronization.

The argoproj/argo-cd repository implements a declarative GitOps controller that automates application deployment and lifecycle management. Understanding its Argo CD core feature functionalities requires examining the controller package and gitops-engine library that orchestrate the reconciliation loop. The controller operates by pulling source repositories, computing diffs against live cluster state, and executing synchronized deployments while respecting ignore differences and sync wave configurations.

Manifest Generation and Repository Resolution

Argo CD begins reconciliation by generating manifests from configured sources. The GetRepoObjs function in controller/state.go coordinates with the repo-server to render templates for Helm, Kustomize, or plain YAML sources.

// Simplified flow from controller/state.go
targetObjs, manifestInfos, revisionsMayHaveChanges, err :=
    m.GetRepoObjs(ctx, app, sources, appLabelKey, revisions,
                noCache, noRevisionCache, project.EffectiveSourceIntegrity(),
                project, true)

Revision resolution occurs through ResolveRevision, which converts ambiguous references like HEAD or master into concrete commit SHAs. When projects enforce source integrity, the UpdateRevisionForPaths RPC narrows revisions to only changes affecting the application's referenced paths.

Target Normalization and Ignore Differences

Before computing diffs, Argo CD applies the ignoreDifferences specification through NormalizeTargetObjects in controller/state.go. This function:

  • Removes ignored fields from live resources before diff calculation
  • Copies ignored fields from live to target objects, excluding the status sub-resource

Configuration example for ignoring deployment annotations:

spec:
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /metadata/annotations

When configured, the diff engine treats changes to metadata.annotations as in-sync, preventing false drift detection on fields managed by external systems.

Live State Retrieval and Permission Validation

The controller retrieves current cluster state via GetManagedLiveObjs from the LiveStateCache:

liveObjByKey, err := m.liveStateCache.GetManagedLiveObjs(destCluster, app, targetObjs)

Resources are filtered against project allow/deny lists through validateSyncPermissions. Any resource not explicitly permitted by the AppProject configuration is excluded from reconciliation to enforce security boundaries.

Diff Computation and Server-Side Reconciliation

Argo CD builds a diff.Config and invokes argodiff.StateDiffs to compute three-way diffs between desired and live states:

diffResults, err := argodiff.StateDiffs(ctx,
    reconciliation.Live, reconciliation.Target, diffConfig)

Server-side diff activates when ServerSideDiff=true, utilizing dry-run kubectl apply operations to calculate changes without API server modification. When structured merge diff is enabled alongside ServerSideApply=true, the controller uses server-side apply mechanics for conflict resolution.

The diff configuration is constructed in controller/state.go around line 470, incorporating ignore differences, resource overrides, and comparison options.

Sync Execution and Wave Orchestration

The SyncAppState function in controller/sync.go orchestrates the actual synchronization:

syncCtx, cleanup, err := sync.NewSyncContext(
    compareResult.syncStatus.Revision,
    reconciliationResult,
    restConfig,
    rawConfig,
    m.kubectl,
    app.Spec.Destination.Namespace,
    opts...)

Sync waves enable sequential resource application through the syncWave annotation. Resources group by wave number (default 0), applying in numerical order with configurable delays. The ARGOCD_SYNC_WAVE_DELAY environment variable controls pauses between waves (default 2 seconds):

import (
    "os"
    "github.com/argoproj/argo-cd/v3/util/settings"
)

// Set a 5-second delay between sync waves
os.Setenv("ARGOCD_SYNC_WAVE_DELAY", "5")

Pruning behavior respects PrunePropagationPolicy and PruneLast settings, ensuring resource deletion occurs according to dependency requirements.

Health Assessment and Status Updates

Post-sync, setApplicationHealth in controller/state.go evaluates resource conditions:

healthStatus, healthMessage, err := setApplicationHealth(
    managedResources, resourceSummaries, resourceOverrides,
    app, m.persistResourceHealth)

Individual resource health populates Application.Status.Resources, while aggregated application health (Healthy, Degraded, Missing, or Progressing) updates Application.Status.Health. The evaluation uses gitops-engine health rules customized through resource overrides.

Practical Configuration Examples

Server-Side Apply with Namespace Creation

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: nginx
spec:
  project: default
  source:
    repoURL: https://github.com/example/nginx
    targetRevision: HEAD
    path: ./manifests
  destination:
    server: https://kubernetes.default.svc
    namespace: nginx
  syncPolicy:
    syncOptions:
    - ServerSideApply=true
    - CreateNamespace=true

ServerSideApply=true forces the controller to use Kubernetes server-side apply during sync, while CreateNamespace=true enables automatic namespace provisioning.

Sync Waves with PostSync Hooks

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: multi-wave
spec:
  source:
    repoURL: https://github.com/example/multi-wave
    targetRevision: main
    path: .
  destination:
    server: https://kubernetes.default.svc
    namespace: demo
  syncPolicy:
    syncOptions:
    - PruneLast=true
    - RespectIgnoreDifferences=true
    hooks:
    - exec:
        command: ["/bin/sh", "-c", "echo Post-hook"]
      type: PostSync
      syncWave: 2

The PostSync hook executes after wave 2 completes, while PruneLast=true ensures orphaned resources are deleted following successful wave application.

Ignoring Container Image Changes

spec:
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /spec/template/spec/containers/0/image

This configuration prevents drift detection when container images are updated outside Git, common in CI/CD pipelines that mutate images during build processes.

Key Source Files

File Responsibility
controller/state.go Manifest generation, target normalization, live state loading, diff computation, and health calculation
controller/sync.go Sync operation orchestration, wave handling, server-side apply, and pruning logic
controller/sharding/sharding.go Workload distribution across controller replicas
gitops-engine/pkg/sync Core diff, reconciliation, and resource-level sync primitives
util/argo/diff Three-way diff implementation and configuration
util/settings Centralized configuration for resource overrides and feature flags

Summary

  • Argo CD core feature functionalities center on a deterministic six-stage reconciliation pipeline: manifest generation, target normalization, live state retrieval, diff computation, sync execution, and health assessment.
  • The GetRepoObjs function in controller/state.go handles repository resolution and template rendering, supporting Helm, Kustomize, and raw YAML sources.
  • NormalizeTargetObjects applies ignoreDifferences configurations by selectively copying fields from live to target objects before diff calculation.
  • SyncAppState in controller/sync.go orchestrates wave-based deployments with configurable delays through ARGOCD_SYNC_WAVE_DELAY.
  • Server-side diff and server-side apply provide modern Kubernetes reconciliation mechanisms for complex field ownership scenarios.

Frequently Asked Questions

What is the Argo CD reconciliation loop?

The reconciliation loop is a continuous process where Argo CD compares desired state from Git repositories against live Kubernetes cluster state. According to the argoproj/argo-cd source code, the loop executes through controller/state.go, generating manifests, normalizing targets, computing diffs via argodiff.StateDiffs, and triggering synchronization when drift is detected. The loop runs at configured intervals or when triggered by webhook events.

How does Argo CD handle sync waves?

Sync waves allow sequential resource deployment using the syncWave annotation (default 0). As implemented in controller/sync.go, Argo CD applies resources in numerical wave order, waiting for each wave to complete before proceeding to the next. The ARGOCD_SYNC_WAVE_DELAY environment variable configures pauses between waves, while PruneLast ensures resource deletion occurs after all waves complete successfully.

What is server-side diff in Argo CD?

Server-side diff is a comparison method enabled by ServerSideDiff=true that uses dry-run kubectl apply operations to calculate changes without modifying the API server. Implemented in the diff configuration logic of controller/state.go, this approach provides more accurate results for resources using server-side apply by simulating the actual merge process that would occur during synchronization.

How does ignoreDifferences prevent false drift detection?

The ignoreDifferences specification prevents specific fields from triggering out-of-sync status through NormalizeTargetObjects in controller/state.go. This function removes configured fields from live objects before diff computation and copies current values into target objects (excluding status fields), ensuring the comparison treats ignored changes as in-sync regardless of cluster state mutations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →