Skip to content

Architecture

Component breakdown, reconciliation model, HA topology, and multi-cluster architecture.

Component Overview

ArgoCD is composed of six core services, each with a distinct responsibility:

Component Role
argocd-server REST/gRPC API server; serves Web UI and CLI; handles SSO via Dex
argocd-repo-server Fetches Git repos and renders manifests (Helm, Kustomize, Jsonnet, plain YAML)
argocd-application-controller Watches K8s clusters; computes drift; drives sync operations
argocd-redis In-memory cache for rendered manifests and live cluster state; speeds up diffs
argocd-dex-server OpenID Connect provider proxy; enables SSO (OIDC, SAML, LDAP, GitHub OAuth)
argocd-applicationset-controller Generates Application resources from templates and generator inputs
argocd-notifications-controller Sends alerts to Slack, PagerDuty, email on sync/health events

System Architecture

graph TB
    subgraph Users["Users & CI/CD"]
        UI["Web UI\n(Browser)"]
        CLI["argocd CLI"]
        CICD["CI Pipeline\n(API caller)"]
    end

    subgraph ArgoCD["ArgoCD Control Plane"]
        Server["argocd-server\n(API + UI)"]
        Dex["argocd-dex-server\n(OIDC/SAML/LDAP)"]
        RepoSvr["argocd-repo-server\n(manifest rendering)"]
        AppCtrl["argocd-application-controller\n(reconciliation)"]
        AppSet["argocd-applicationset-controller"]
        Notif["argocd-notifications-controller"]
        Redis["argocd-redis\n(cache)"]
    end

    subgraph Sources["Git Sources"]
        Git["Git Repository\n(GitHub/GitLab/Gitea)"]
        Helm["Helm Registry\n(OCI or HTTP)"]
    end

    subgraph Clusters["Target Clusters"]
        LocalK8s["In-cluster\nKubernetes API"]
        RemoteK8s["Remote Cluster\nKubernetes API"]
    end

    UI --> Server
    CLI --> Server
    CICD --> Server
    Server --> Dex
    Server --> RepoSvr
    Server --> Redis
    AppCtrl --> RepoSvr
    AppCtrl --> Redis
    AppCtrl --> LocalK8s
    AppCtrl --> RemoteK8s
    RepoSvr --> Git
    RepoSvr --> Helm
    AppSet --> Server
    Notif --> Server

Reconciliation Model

The Application Controller is the heart of ArgoCD. It runs a control loop every 3 minutes (configurable):

sequenceDiagram
    participant AppCtrl as Application Controller
    participant RepoSvr as Repo Server
    participant Redis as Redis Cache
    participant Git as Git Repository
    participant Cluster as Target Cluster

    loop Every reconciliation interval
        AppCtrl->>RepoSvr: Get rendered manifests
        RepoSvr->>Redis: Check manifest cache
        alt Cache miss
            RepoSvr->>Git: git fetch + render (Helm/Kustomize)
            RepoSvr->>Redis: Store rendered manifests
        end
        RepoSvr-->>AppCtrl: Desired state manifests
        AppCtrl->>Cluster: List live resources
        AppCtrl->>AppCtrl: Diff desired vs live
        alt Drift detected
            AppCtrl->>AppCtrl: Mark Application: OutOfSync
            alt AutoSync enabled
                AppCtrl->>Cluster: kubectl apply (server-side)
            end
        else No drift
            AppCtrl->>AppCtrl: Mark Application: Synced ✓
        end
    end

Sync Architecture: Waves and Hooks

ArgoCD applies resources in ordered waves, with lifecycle hooks at each phase:

flowchart LR
    PreSync["PreSync Hooks\n(migrations, prep)"] --> Wave0["Wave 0\n(Namespaces, CRDs)"] --> Wave1["Wave 1\n(ConfigMaps, Secrets)"] --> Wave2["Wave 2\n(Deployments, Services)"] --> PostSync["PostSync Hooks\n(smoke tests, notify)"] --> SyncFail["SyncFail Hooks\n(rollback, alert)"]

    style PreSync fill:#e65100,color:#fff
    style PostSync fill:#2e7d32,color:#fff
    style SyncFail fill:#b71c1c,color:#fff

Wave ordering uses the annotation: argocd.argoproj.io/sync-wave: "2"

ApplicationSet Architecture

ApplicationSet generates multiple Application resources from a single template:

Generator Mechanism Example Use Case
Git Directory One app per subdirectory in a Git path Monorepo — each team folder becomes an app
Git File JSON/YAML config files drive app parameters Environment-specific config per file
Cluster One app per registered ArgoCD cluster Fleet deployment — same app to all clusters
Matrix Cartesian product of two generators All clusters × all environments
Merge Combine multiple generators with overrides Base config + per-cluster patches
Pull Request One app per open PR in a repository Preview environments per PR

High Availability Topology

For production deployments, ArgoCD scales each component independently:

graph TB
    LB["Load Balancer / Ingress"] --> Svr1["argocd-server (replica 1)"]
    LB --> Svr2["argocd-server (replica 2)"]

    Svr1 --> RepoSvr1["argocd-repo-server (replica 1)"]
    Svr1 --> RepoSvr2["argocd-repo-server (replica 2)"]
    Svr2 --> RepoSvr1
    Svr2 --> RepoSvr2

    subgraph Controller["Application Controller (Sharded)"]
        Shard0["Shard 0\n(clusters 0-49)"]
        Shard1["Shard 1\n(clusters 50-99)"]
    end

    Redis["Redis / Redis Sentinel\n(HA cache)"]

    Svr1 --> Redis
    Svr2 --> Redis
    Shard0 --> Redis
    Shard1 --> Redis

Scaling guidelines: - argocd-server: 2+ replicas; stateless; horizontally scalable - argocd-repo-server: 2+ replicas; CPU-bound by rendering; scale with repo + app count - argocd-application-controller: 1 per shard; enable sharding for 50+ clusters - argocd-redis: Use Redis Sentinel or Redis Cluster for HA; single point of failure without it

Multi-Cluster Architecture

ArgoCD manages remote clusters via bearer token or kubeconfig credentials stored as K8s Secrets in the argocd namespace:

graph LR
    subgraph Mgmt["Management Cluster"]
        ArgoCD["ArgoCD Control Plane"]
    end
    subgraph Prod["Production Clusters"]
        ProdUS["prod-us\n(remote cluster)"]
        ProdEU["prod-eu\n(remote cluster)"]
    end
    subgraph Dev["Dev Clusters"]
        Dev1["dev-1\n(remote cluster)"]
    end

    ArgoCD -->|"ServiceAccount\nbearer token"| ProdUS
    ArgoCD -->|"ServiceAccount\nbearer token"| ProdEU
    ArgoCD -->|"kubeconfig\n(in-cluster)"| Dev1

Cluster credentials are stored as Secret resources with label argocd.argoproj.io/secret-type: cluster.

Repo Server: Manifest Rendering

The repo server isolates manifest rendering in a separate process to prevent privilege escalation:

  • Clones repositories into a local cache (/tmp/repo-server/cache)
  • Runs rendering (Helm, Kustomize, Jsonnet) in sandboxed subprocesses
  • Caches rendered output in Redis with a TTL based on the revision
  • Supports Config Management Plugins (CMP) via sidecar containers for custom renderers

Storage Model

ArgoCD is largely stateless — all persistent state lives in Kubernetes:

Data Storage
Application specs Application CRDs in argocd namespace
Cluster credentials Secret resources in argocd namespace
Repository credentials Secret resources in argocd namespace
RBAC policies ConfigMap (argocd-rbac-cm)
Runtime cache Redis (ephemeral; rebuilt on restart)
Audit logs Kubernetes API server audit log

How It Works

Reconciliation engine, sync phases, ApplicationSet generators, and drift detection.

Reconciliation Loop

sequenceDiagram
    participant Git as Git Repository
    participant RepoSvr as Repository Server
    participant AppCtrl as Application Controller
    participant Cluster as Target Cluster

    loop Every 3 minutes (configurable)
        AppCtrl->>RepoSvr: Fetch latest manifests
        RepoSvr->>Git: git clone/pull (or cached)
        RepoSvr->>RepoSvr: Render: Helm template / Kustomize build
        RepoSvr-->>AppCtrl: Desired state (manifests)
        AppCtrl->>Cluster: Get live state (kubectl get)
        AppCtrl->>AppCtrl: Diff: desired vs live
        alt Drift detected
            AppCtrl->>AppCtrl: Mark: OutOfSync
            alt Auto-sync enabled
                AppCtrl->>Cluster: Apply manifests (kubectl apply)
            end
        else No drift
            AppCtrl->>AppCtrl: Mark: Synced ✓
        end
    end

Sync Waves & Hooks

flowchart LR
    PreSync["PreSync\n(DB migrations, \nconfig setup)"] --> Sync["Sync\n(main resources)"] --> PostSync["PostSync\n(smoke tests,\nnotifications)"]
    PreSync -.->|"Wave 0"| NS["Namespace"]
    PreSync -.->|"Wave 1"| CM["ConfigMaps"]
    Sync -.->|"Wave 2"| Deploy["Deployments"]
    Sync -.->|"Wave 3"| SVC["Services"]
    PostSync -.->|"Wave 4"| Test["Test Jobs"]

    style PreSync fill:#e65100,color:#fff
    style Sync fill:#1565c0,color:#fff
    style PostSync fill:#2e7d32,color:#fff

ApplicationSet Generators

Generator Use Case
Git Directory One app per directory in monorepo
Git File Config-driven from JSON/YAML in Git
Cluster Deploy same app to all registered clusters
List Static list of environments
Matrix Cross-product of two generators
Merge Combine multiple generators
Pull Request Preview environments per PR
SCM Provider Auto-discover repos from GitHub/GitLab org

Drift Detection Mechanism

ArgoCD compares two states to detect configuration drift:

  1. Desired state: Rendered manifests from Git (Helm template, Kustomize build, or raw YAML)
  2. Live state: Current resources in the target cluster (fetched via kubectl get --show-managed-fields)

The diff is computed using a three-way merge strategy, similar to kubectl apply. ArgoCD ignores: - Runtime-injected fields (e.g., kubectl.kubernetes.io/last-applied-configuration) - Status subresources - Fields managed by other controllers (via metadata.managedFields)

Drift status is displayed as: Synced (no diff), OutOfSync (diff detected), or Unknown (comparison error).

Sources


Benchmarks

Scope

Performance characteristics, scaling limits, and resource consumption profiles.

Sync Performance

Scenario Apps Clusters Avg Sync Time P95 Sync Time Notes
Small deployment 50 3 2-5s 8s Single controller
Medium deployment 200 10 5-15s 30s Controller sharded x2
Large deployment 1000 50 10-30s 60s Controller sharded x4
Monorepo (10k files) 100 5 30-60s 120s Sparse checkout recommended

Resource Consumption

Controller Memory Scaling

The application controller's memory grows linearly with the number of managed resources:

Managed Resources Controller Memory Controller CPU
1,000 ~256Mi ~200m
5,000 ~1Gi ~500m
20,000 ~4Gi ~2000m
50,000 ~8Gi ~4000m

Repo Server Performance

Operation Small Chart Large Chart (500+ templates) Monorepo
Helm template < 1s 5-15s N/A
Kustomize build < 1s 2-5s 10-30s
Git clone (cold) 1-3s 1-3s 30-120s
Git fetch (warm) < 1s < 1s 2-5s

Scaling Limits

Dimension Tested Limit Recommended Limit Bottleneck
Applications per controller ~5,000 2,000 Memory + reconciliation
Clusters per instance ~200 100 API server connections
Concurrent syncs 50 (default) 20-30 Repo server CPU
Webhook events/sec ~100 50 Redis throughput
ApplicationSets ~500 200 Generator evaluation time

Community Benchmarks

  • Intuit manages 3,000+ applications across multiple clusters using controller sharding
  • Argo Project's official scalability testing targets 1,000 apps / 100 clusters per instance
  • Red Hat OpenShift GitOps (ArgoCD-based) recommends max 300 apps per non-sharded instance

Sourcing Status

Unsourced Performance Data

The performance numbers in this document are estimated from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.

Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.

Sources