Argo CD — Architecture#
Architectural style#
Distributed GitOps Control Plane — Multi-Service Monolith (single-binary deployment)
Argo CD is a Kubernetes-native GitOps continuous delivery controller. Its architecture is
microservices in spirit, monolith in deployment: the code is split across five independently
deployable services, but all services compile into a single binary and share a common codebase.
At runtime each pod selects its role by binary name or ARGOCD_BINARY_NAME environment variable.
The overall pattern is a control loop / reconciliation architecture (characteristic of Kubernetes operators) layered on top of a service-oriented internal API where services communicate over gRPC. There is no shared database; Kubernetes Secrets and ConfigMaps serve as the persistent store, and Redis provides the shared cache layer.
Evidence: cmd/main.go switch-dispatches to NewCommand() for each service; server/server.go
wires gRPC services behind a cmux multiplexer; controller/appcontroller.go implements the
standard Kubernetes controller work-queue pattern.
Component diagram (textual)#
┌──────────────────────────────────────────────────────────────────┐
│ User-facing surfaces │
│ Web UI (React) │ argocd CLI (cobra) │ CI/CD (REST/gRPC) │
└──────────────────┬───────────────────────────────────────────────┘
│ HTTPS (gRPC-Gateway + grpc-web)
┌──────────────────▼───────────────────────────────────────────────┐
│ argocd-server (API Server) │
│ gRPC services: Application, Project, Cluster, Repository, │
│ Session, Settings, Account, Notification, ApplicationSet, … │
│ RBAC (Casbin) • OIDC/Dex SSO • JWT session management │
│ Webhook receiver (GitHub, GitLab, Bitbucket) │
└──────┬───────────────────────┬──────────────────────────────────┘
│ gRPC │ k8s watch/write (CRDs)
┌──────▼──────────┐ ┌──────▼─────────────────────────────────┐
│ argocd-repo- │ │ argocd-application-controller │
│ server │ │ Kubernetes controller (work-queue) │
│ │ │ - AppStateManager: desired vs live diff │
│ Renders K8s │◄───┤ - gitops-engine: diff + apply │
│ manifests from │ │ - Sharding: distributes clusters │
│ Git/Helm/ │ │ - Hydrator: write-back to Git │
│ Kustomize/OCI/ │ └──────┬─────────────────────────────────┘
│ CMP sidecar │ │ k8s apply (kubectl/client-go)
└──────▲──────────┘ ▼
│ gRPC Target Kubernetes Clusters
┌──────┴──────────┐
│ argocd-cmp- │ ┌─────────────────────────────────┐
│ server (sidecar)│ │ argocd-applicationset- │
│ Plugin sandbox │ │ controller │
└─────────────────┘ │ Generates Applications from │
│ templates (Git, List, Cluster, │
┌──────────────────┐ │ SCM, PullRequest generators) │
│ argocd- │ └─────────────────────────────────┘
│ notification │
│ controller │ ┌─────────────────────────────────┐
│ (triggers, │ │ argocd-commit-server │
│ templates, │ │ Hydration write-back to Git │
│ subscriptions) │ │ (dry-run → commit manifests) │
└──────────────────┘ └─────────────────────────────────┘
Shared infrastructure:
┌─────────────────────────────────────────────────────────┐
│ Redis (cross-service cache: app state, repo manifests, │
│ session tokens, cluster info) │
│ Kubernetes Secrets/ConfigMaps (util/db — the "DB") │
│ argocd-dex (embedded OIDC provider, optional) │
└─────────────────────────────────────────────────────────┘Core components#
API Server#
- Package:
server/ - Responsibility: Single entry point for all external traffic. Hosts ~15 gRPC service
implementations (one per resource domain), serves the React UI as embedded static assets, and
acts as an HTTP/REST gateway via
grpc-gateway. Handles authentication (JWT, OIDC via Dex), authorization (Casbin RBAC), and incoming Git webhooks. - Key types:
ArgoCDServer(main struct,server/server.go),ArgoCDServerOpts,ArgoCDServiceSet,rbacpolicy.RBACPolicyEnforcer - Dependencies: Kubernetes client-go (watches Application/AppProject CRDs), Redis client,
repo server gRPC client (
reposerver/apiclient),util/db,util/settings,util/session,util/rbac,util/oidc, Dex,server/extension.Manager - Transport:
soheilhy/cmuxmultiplexes gRPC and HTTPS on the same port;grpc-webwraps gRPC for browser clients;grpc-gatewayprovides REST transcoding.
Application Controller#
- Package:
controller/ - Responsibility: The GitOps engine. Implements the Kubernetes controller pattern: watches
Application and AppProject CRDs, computes desired state (via Repo Server), computes live state
(via Kubernetes API +
gitops-engine/pkg/cache), diffs them (viagitops-engine/pkg/diff), and applies resources when sync is requested. Manages multiple independent work queues for refresh, comparison, operation execution, project refresh, and hydration. - Key types:
ApplicationController(controller/appcontroller.go),AppStateManager(interface for desired/live state comparison),statecache.LiveStateCache(Kubernetes resource cache),sharding.ClusterShardingCache(horizontal scaling across controller replicas) - Dependencies:
gitops-engine(diff, cache, health, sync),reposerver/apiclient,commitserver/apiclient,util/db,util/settings, Kubernetes work-queue rate limiter
Repository Server#
- Package:
reposerver/repository/ - Responsibility: Stateless rendering service. Given a Git URL + revision + path + tool parameters, it renders Kubernetes manifests. Supports: plain YAML, Helm, Kustomize, Jsonnet, OCI artifacts, and Config Management Plugins (delegated to CMP sidecar). Maintains a local Git clone cache on disk and a Redis cache of rendered manifests.
- Key types:
RepoServerService(reposerver/repository/repository.go),reposerver/cache.Cache(Redis-backed manifest cache) - Dependencies:
go-git(Git operations), Helm SDK, Kustomize exec,google/go-jsonnet,cmpserver/apiclient(plugin delegation),gitops-engine/pkg/utils/kube,util/git,util/helm,util/kustomize,util/oci
CMP Server#
- Package:
cmpserver/plugin/ - Responsibility: Config Management Plugin sidecar. Runs in a separate container alongside
the repo server. Receives rendering requests over a Unix domain socket gRPC connection. Executes
user-defined
generatescripts in a sandboxed environment. Allows arbitrary tools (Helm wrappers, custom generators, kpt, etc.) to be added without modifying the repo server. - Key types:
Service(cmpserver/plugin/), plugin discovery viaplugin.yaml
gitops-engine (embedded sub-module)#
- Package:
gitops-engine/pkg/(vendored sub-directory) - Responsibility: The core sync and diff engine, originally a separate public module. Contains:
pkg/cache— Kubernetes cluster resource cache;pkg/diff— three-way strategic-merge diff;pkg/health— resource health assessment (Lua + built-in rules);pkg/sync— sync wave execution, hook lifecycle (PreSync, Sync, PostSync, SyncFail), resource pruning, apply logic. - Key types:
cache.ClusterCache,diff.DiffResult,health.HealthStatus,sync.SyncContext - Dependencies:
k8s.io/client-go,k8s.io/apimachinery,gopher-lua(health Lua)
ApplicationSet Controller#
- Package:
applicationset/ - Responsibility: Implements the ApplicationSet CRD — a “factory” that generates many Application CRDs from a template and a set of generators (Git directory listing, static list, cluster enumeration, SCM provider APIs, pull requests, matrix/merge combinators).
- Key types:
ApplicationSetReconciler, generatorGeneratorinterface with 8+ implementations - Dependencies:
controller-runtime(reconcile loop),util/db, API server gRPC client
util/db — Kubernetes-native storage#
- Package:
util/db/ - Responsibility: Argo CD has no external database.
util/dbimplements aArgoDBinterface that stores all configuration (clusters, repositories, credentials, certificates, GPG keys) as Kubernetes Secrets and ConfigMaps in the installation namespace. Read/write is via the standard Kubernetes client-go API, making the store cluster-RBAC-compatible. - Key types:
ArgoDB(interface),db(struct implementing ArgoDB)
Data flow#
Sync flow (the canonical GitOps operation)#
1. TRIGGER: Git commit → webhook → API Server → enqueues Application for refresh
2. DESIRED STATE: Application Controller dequeues app
→ calls Repo Server gRPC GenerateManifests(repo_url, revision, path, tool_params)
→ Repo Server: git fetch + render (Helm/Kustomize/CMP/YAML)
→ returns []Manifest (rendered Kubernetes objects)
3. LIVE STATE: Application Controller reads live cluster state
via gitops-engine/pkg/cache (SharedIndexInformer per target cluster)
→ returns current resource tree
4. DIFF: gitops-engine/pkg/diff computes three-way diff
(last-applied annotation vs desired vs live)
→ produces list of create/update/delete operations
5. STATUS: Controller patches Application.status (Synced/OutOfSync, health)
→ status visible in UI and via API
6. SYNC (if auto-sync or manual trigger):
gitops-engine/pkg/sync applies resources to target cluster
in wave order, respecting sync hooks (PreSync → Sync → PostSync)
→ kubectl apply / server-side apply via client-go
7. POST-SYNC: hook Jobs run, notifications emitted, commit server
(if hydration mode) writes resolved manifests back to GitRequest flow (UI/CLI → API)#
Browser/CLI → HTTPS → cmux → grpc-web or gRPC-gateway (HTTP/1.1) → gRPC handler
→ JWT auth interceptor (util/session)
→ RBAC interceptor (Casbin policy enforcer)
→ service handler (e.g. server/application.Server.Sync)
→ patches Application CRD operation field
→ Application Controller picks up the operation via informer eventInitialization / Bootstrap#
Entry point: cmd/main.go — switches on binary name, calls the appropriate NewCommand().
Server bootstrap sequence (argocd-server):
cobra.Command.Run() {
1. Parse flags + env vars (listenPort, repoServerAddress, dexServerAddr, etc.)
2. Construct Kubernetes clients (kubeclientset, appclientset, dynamicClient)
3. Load TLS configuration (cert pool from cluster secrets or filesystem)
4. Construct gRPC client for repo server (apiclient.NewRepoServerClientset)
5. Populate ArgoCDServerOpts struct (manual DI — no framework)
6. server.NewServer(ctx, opts, appsetOpts) {
- NewSettingsManager → InitializeSettings (reads argocd-cm ConfigMap)
- initializeDefaultProject (creates default AppProject if absent)
- NewClusterInformer, SharedInformerFactory for App/AppProject/AppSet
- NewSessionManager (JWT), NewEnforcer (Casbin RBAC), NewDB
- extension.NewManager (proxy extension manager)
returns *ArgoCDServer
}
7. server.Init(ctx) — starts informers, waits for cache sync
8. server.Run(ctx, listeners) {
- cmux.New(listener) multiplexes gRPC + HTTP on same TCP port
- grpc.NewServer with interceptor chain:
otelgrpc (tracing) → prometheus metrics → auth → recovery → logging
- registers all gRPC service implementations
- registers grpc-gateway HTTP mux (REST transcoding)
- serves static UI assets
- goroutines: settings watcher, OIDC state refresh, token cleanup
}
}Controller bootstrap sequence (argocd-application-controller):
cobra.Command.Run() {
1. Kubernetes clients + rate limiter config from flags
2. NewApplicationController(namespace, settingsMgr, ...) — manual DI
3. appcontroller.Run(ctx, statusProcessors, operationProcessors) {
- RegisterClusterSecretUpdater
- stateCache.Init() + Run() (per-cluster SharedIndexInformers)
- N goroutines for processAppRefreshQueueItem (status reconciliation)
- M goroutines for processAppOperationQueueItem (sync execution)
- goroutines for project refresh, comparison type queue, hydration queue
- blocks on <-ctx.Done()
}
}Dependency injection pattern: Manual wiring throughout — no Wire, Dig, or Fx. All components
receive their dependencies as constructor parameters. The ArgoCDServerOpts struct and
NewApplicationController(...) signature act as the “composition root”. This is typical for
CNCF-era Go projects predating DI framework adoption.
Configuration#
Argo CD uses three-tier configuration:
CLI flags (Cobra): Per-service runtime parameters — ports, addresses, timeouts, feature flags. Flags are defined in
cmd/<service>/commands/and parsed at startup. Many flags have corresponding environment variable overrides viautil/env.ParseNumFromEnv/ParseBoolFromEnv/StringFromEnv.Kubernetes ConfigMaps and Secrets (operator configuration):
argocd-cm— main settings (OIDC config, resource exclusions, helm values, app health lua)argocd-rbac-cm— Casbin RBAC policy and default roleargocd-secret— admin password hash, TLS cert, OIDC client secretargocd-ssh-known-hosts-cm— Git SSH known hostsargocd-tls-certs-cm— custom TLS certificates for Git repos Managed byutil/settings.SettingsManager(watches ConfigMaps via informers; live-reloads settings without restart).
Redis (runtime cache): Not configuration per se, but stores: rendered manifest cache (repo server), live cluster state snapshots (controller), session tokens, app state cache. Cache key structure uses
util/cachewith namespaced keys per project/app/cluster.
Key design decisions#
Single binary, multiple personas — One Docker image contains all services. The active service is selected by binary symlink or
ARGOCD_BINARY_NAME. This simplifies image distribution and version consistency, but means all service code is compiled into every pod even when unused. Tradeoff: operational simplicity over resource efficiency.Kubernetes as the database — All persistent state lives in Kubernetes Secrets and ConfigMaps via
util/db. This eliminates the need to manage an external database, integrates seamlessly with Kubernetes RBAC, and makes Argo CD self-contained within a cluster. Tradeoff: no query capability, no transactions; configuration management becomes Kubernetes resource management.gitops-engine as an embedded sub-module — The diff, sync, and cache engine was originally a separate public module (
github.com/argoproj/gitops-engine) intended for reuse across the GitOps ecosystem. It was reabsorbed into the monorepo to avoid cross-repo versioning friction. The subdirectory retains its own module path in import statements but is built from local source. Tradeoff: development velocity over ecosystem sharing.cmux: gRPC + HTTP on a single port —
soheilhy/cmuxinspects the first bytes of each TCP connection to route to the gRPC server or the HTTP/REST gateway. This simplifies Kubernetes Service and Ingress configuration (one port for both CLI and browser traffic) at the cost of a thin multiplexing layer.grpc-webwraps gRPC for browser clients that cannot use HTTP/2.Lua scripts for extensible health assessment — Rather than requiring Go code contributions for every third-party CRD, Argo CD evaluates
resource_customizations/Lua scripts to determine resource health and status. Thegopher-luainterpreter is embedded ingitops-engine/pkg/health. This enables a community-contributed library of 100+ CRD health rules (Cert-Manager, Istio, Karpenter, etc.) without recompilation. Tradeoff: Lua scripting adds runtime overhead and a second language in the codebase.