Skip to main content
All articles

GitOps for Kubernetes: ArgoCD vs Flux vs Jenkins X in Production

GitOps principles, ArgoCD app-of-apps pattern, Flux source controllers, drift detection, progressive delivery, and multi-cluster management strategies.

CloudToolStack TeamMarch 11, 202615 min read

GitOps Is Not Just "Git + Kubernetes"

GitOps has become one of the most misused terms in cloud engineering. Teams slap a "GitOps" label on any pipeline that deploys from a Git repository, but actual GitOps is a specific operational model with specific properties. The core principle is that Git is the single source of truth for the desired state of your entire system. A controller running inside your cluster continuously compares the actual state of the cluster against the desired state in Git. When they diverge -- whether because someone deployed manually, because a pod crashed, or because a resource was modified by an operator -- the controller reconciles the difference automatically.

This pull-based model is fundamentally different from traditional CI/CD push-based deployments. In a push model, your CI pipeline authenticates to the cluster and applies changes. In a pull model, the cluster reaches out to Git and applies changes to itself. This distinction matters for security (no cluster credentials in CI), for auditability (every change is a Git commit), and for reliability (the controller continuously reconciles, not just at deploy time).

This guide covers three GitOps tools that actually work in production: ArgoCD, Flux, and Jenkins X. We compare their architectures, deployment models, and real-world operational patterns. The emphasis is on what works for teams running 10 to 200 microservices across multiple clusters, not on toy examples with a single deployment.

ArgoCD: The Kubernetes-Native Application Manager

ArgoCD is the most popular GitOps tool, and for good reason. It provides a declarative, Kubernetes-native approach to application deployment with a powerful web UI, CLI, and API. ArgoCD watches Git repositories (and Helm repositories, and OCI registries) and ensures that the live state of your Kubernetes clusters matches the desired state defined in those repositories.

At its core, ArgoCD introduces the Application custom resource. Each Application specifies a source (a Git repository path, a Helm chart, or a Kustomize overlay) and a destination (a Kubernetes cluster and namespace). ArgoCD periodically polls the source (default every 3 minutes, configurable) or receives webhooks from Git, compares the rendered manifests against the live cluster state, and reports whether the application is in sync, out of sync, or in a degraded state.

The sync process can be automatic (ArgoCD applies changes as soon as it detects drift) or manual (ArgoCD reports the diff and waits for human approval). In production, most teams use automatic sync for non-production environments and manual sync (or automated sync with a sync window) for production. Sync waves and hooks let you control the order of operations -- create namespaces before deploying into them, run database migrations before updating application pods, verify health checks after each wave completes.

The App-of-Apps Pattern

The app-of-apps pattern is how you manage ArgoCD at scale. Instead of creating Application resources manually or via CI scripts, you create a "root" Application that points to a Git directory containing other Application definitions. ArgoCD deploys the root application, which creates child Applications, which in turn deploy actual workloads. This gives you a single entry point that bootstraps your entire cluster.

In practice, the directory structure looks something like this: a root app points to a "cluster-apps" directory containing Application YAML files for each service. Each Application YAML points to the service's own repository (or a subdirectory in a monorepo) and specifies the target cluster and namespace. When you need to add a new service to the cluster, you add an Application YAML to the cluster-apps directory, commit, and ArgoCD picks it up.

The ApplicationSet controller takes this further. Instead of manually creating Application YAMLs, you define an ApplicationSet with generators that produce Applications dynamically. The Git generator creates one Application per directory in a Git path. The Cluster generator creates one Application per registered cluster. The Matrix generator combines generators -- for example, deploying every service in every cluster, or deploying a specific set of services to clusters matching certain labels. ApplicationSets are how you manage deployments across 10, 50, or 200 clusters without maintaining hundreds of Application YAML files.

ApplicationSet for Multi-Cluster

Use the Matrix generator combining a Git directory generator (for services) with a Cluster generator (for target clusters). Add cluster labels like "env: production" or "region: us-east" and filter on those labels. This lets you control which services deploy to which clusters by changing Git files and cluster labels, not by editing hundreds of Application resources.

Flux: The Modular Toolkit

Flux v2 (the current version, completely rewritten from Flux v1) takes a different architectural approach from ArgoCD. Instead of a single monolithic controller, Flux is a set of composable controllers that each manage one concern: source-controller watches Git repositories and Helm repositories, kustomize-controller renders and applies Kustomize overlays, helm-controller manages Helm releases, notification-controller sends and receives webhooks, and image-automation-controller updates Git when new container images are available.

This modularity has real implications. You can install only the controllers you need. If you do not use Helm, you skip helm-controller. If you do not need image automation, you skip image-automation-controller. Each controller is independently configurable, scalable, and upgradeable. The downside is more custom resources to learn: GitRepository, HelmRepository, OCIRepository, Kustomization, HelmRelease, ImageRepository, ImagePolicy, ImageUpdateAutomation -- it is a lot of CRDs.

Source Controllers

Flux's source-controller is responsible for fetching desired state from external sources. A GitRepository resource tells Flux to watch a specific Git repository branch or tag at a specific interval (default 1 minute). The controller clones the repository, optionally checks GPG signatures on commits, and makes the fetched content available to downstream controllers. A HelmRepository resource watches a Helm chart repository for new chart versions. An OCIRepository resource fetches artifacts from OCI-compliant registries -- this is increasingly important as teams store Kubernetes manifests and Kustomize overlays as OCI artifacts rather than in Git.

The source controller produces Artifact objects that include a checksum. Downstream controllers (kustomize-controller, helm-controller) watch for changes to these artifacts and reconcile only when the artifact changes. This means Flux does not re-apply unchanged manifests on every reconciliation cycle -- it detects that the source has not changed and skips the apply step. This reduces unnecessary API server load on large clusters.

Flux vs ArgoCD: The Real Differences

The most visible difference is the UI. ArgoCD has a polished web dashboard showing application topology, sync status, resource health, and live diffs. Flux has no built-in UI -- it is designed to be operated via kubectl and the Flux CLI. Third-party dashboards exist (Weave GitOps, Capacitor), but none match ArgoCD's UI quality. If your team expects a visual overview of deployment status across clusters, ArgoCD is the stronger choice.

Flux's advantage is its tighter integration with the Kubernetes ecosystem. Flux controllers are CNCF graduated, follow Kubernetes API conventions closely, and work well with other tools in the ecosystem. Flux's dependency management (a Kustomization can depend on another Kustomization, and Flux respects the ordering) is more explicit than ArgoCD's sync waves. Flux's image automation -- watching container registries for new image tags and automatically updating Git -- has no equivalent in ArgoCD without third-party tooling.

For multi-tenancy, Flux is stronger out of the box. Each Flux Kustomization can impersonate a specific service account, ensuring that tenant workloads can only be deployed to their own namespaces. ArgoCD supports multi-tenancy via AppProjects, but the configuration is more complex and error-prone.

Compare managed Kubernetes offerings across clouds

Jenkins X: CI/CD Built on GitOps

Jenkins X is fundamentally different from ArgoCD and Flux. While those two are GitOps controllers that reconcile desired state, Jenkins X is a complete CI/CD platform that happens to use GitOps principles. It bundles Tekton pipelines, Lighthouse for webhook handling, and a GitOps engine (previously Flux, now its own implementation) into an opinionated platform.

Jenkins X automatically creates CI pipelines for your repositories, builds and pushes container images, promotes deployments through environments (dev to staging to production) via pull requests, and manages preview environments for every pull request. The promotion model is the key differentiator: when a new container image is built, Jenkins X creates a pull request to the environment repository updating the image tag. When the PR is merged (automatically for dev, manually for production), the GitOps controller deploys the change.

The honest assessment of Jenkins X: it is the most opinionated of the three and the hardest to adopt incrementally. You cannot easily bolt Jenkins X onto an existing cluster -- it wants to manage the entire development workflow. It is best suited for greenfield projects where the team is willing to adopt the full Jenkins X workflow. For existing clusters with established CI/CD pipelines, ArgoCD or Flux is a far easier adoption path.

Drift Detection and Reconciliation

Drift detection is what separates GitOps from "deploy from Git." A traditional CI/CD pipeline deploys once and forgets. If someone runs kubectl edit on a deployment, or if a Horizontal Pod Autoscaler changes the replica count, or if an admission webhook modifies a resource, the live state diverges from Git and nobody knows.

ArgoCD detects drift by comparing the live state of every managed resource against the rendered manifests from Git. It does a structured comparison, ignoring fields managed by Kubernetes (status, metadata.managedFields, metadata.resourceVersion) and fields that are expected to change (like replica count if an HPA is managing it). You can configure ignoreDifferences to tell ArgoCD to ignore specific fields that should not trigger a sync. Without proper ignoreDifferences configuration, ArgoCD will constantly try to revert changes made by HPAs, VPAs, or other controllers -- this is the most common misconfiguration in ArgoCD deployments.

Flux handles drift detection at the apply level. If drift-detection is enabled on a Kustomization, Flux periodically applies the manifests (even if the source has not changed) to reconcile drift. This is a simpler approach: instead of detecting what changed, it just reapplies the desired state. The tradeoff is that this generates more API server activity and may conflict with controllers that manage specific fields (same HPA problem as ArgoCD, solved by field management annotations).

Drift from HPAs and VPAs

Both ArgoCD and Flux will fight with Horizontal Pod Autoscalers and Vertical Pod Autoscalers if not configured correctly. In ArgoCD, add ignoreDifferences for spec.replicas on Deployments managed by HPAs. In Flux, remove the replicas field from your Git manifests entirely and let the HPA manage it. Do not set replicas in Git if an HPA is supposed to control scaling.

Progressive Delivery

Progressive delivery -- canary deployments, blue-green deployments, A/B testing -- is not built into ArgoCD or Flux, but both integrate with progressive delivery controllers.

Argo Rollouts

Argo Rollouts (a separate project from ArgoCD, but designed to work with it) replaces the Kubernetes Deployment resource with a Rollout resource that supports canary and blue-green strategies. A canary Rollout gradually shifts traffic from the old version to the new version based on configurable steps: send 5% of traffic to the canary, wait 5 minutes, analyze Prometheus metrics, send 20% if metrics are healthy, wait 10 minutes, and so on. If metrics degrade at any step, the rollout is automatically paused or rolled back.

Argo Rollouts integrates with multiple traffic management providers: Istio, Linkerd, AWS ALB (via Ingress annotations), Nginx, Traefik, and Ambassador. The Istio integration is the most mature: Argo Rollouts dynamically updates Istio VirtualService weights to shift traffic between the canary and stable ReplicaSets.

Flagger

Flagger is the progressive delivery tool designed for Flux. It works similarly to Argo Rollouts: you define a Canary resource that wraps a Deployment, and Flagger manages the canary analysis, traffic shifting, and promotion or rollback. Flagger supports Istio, Linkerd, App Mesh, Contour, Gloo, Nginx, Skipper, and Traefik. It also integrates with webhook-based load testers (like flagger-loadtester) to generate traffic during canary analysis.

Multi-Cluster Management

This is where GitOps tools face their hardest production challenge. Managing a single cluster is straightforward. Managing 5, 10, or 50 clusters with different configurations, different sets of services, and different promotion schedules requires careful architecture.

ArgoCD supports multi-cluster natively. You register external clusters in ArgoCD, and Applications can target any registered cluster. A common pattern is a "management cluster" running ArgoCD that deploys workloads to all other clusters. ApplicationSets with the Cluster generator make this scalable -- define a template once, and it deploys to every cluster matching your criteria.

Flux's approach to multi-cluster is different. Instead of a central management cluster, Flux recommends running Flux in every cluster and using a shared Git repository structure. Each cluster has its own Flux installation that watches a cluster-specific path in the Git repository, plus shared paths for common configurations. The cluster-specific path overrides shared values where needed. This is more decentralized than ArgoCD's approach, but it means each cluster is self-managing and does not depend on a central controller.

Rollback Strategies

One of the biggest advantages of GitOps is that rollback is a git revert. The desired state of your cluster is in Git, so reverting a Git commit and letting the GitOps controller reconcile is the safest and most auditable rollback mechanism.

In ArgoCD, you can also rollback from the UI or CLI to any previously synced revision. ArgoCD maintains a history of synced revisions per Application. Clicking "Rollback" syncs to a previous revision. However, this creates a divergence: the live state is now at a different revision than the HEAD of the Git branch. ArgoCD will show the application as "out of sync" until someone either pushes a new commit (fixing forward) or reverts the Git commit to match the rollback.

The recommended pattern: always rollback via Git. Push a revert commit. Let ArgoCD or Flux sync the revert. This keeps Git as the source of truth and creates a clear audit trail. Manual rollbacks via the ArgoCD UI are useful in emergencies, but they should be followed by a corresponding Git commit as soon as possible.

Rollback Best Practice

Set up a Slack bot or CI shortcut that creates a git revert commit and pushes it with a single command. When production is on fire, you do not want engineers fumbling with git revert syntax. Make the rollback path as fast and foolproof as possible.

Choosing Between ArgoCD, Flux, and Jenkins X

After running all three in production environments of varying sizes, here is the honest recommendation:

  • Choose ArgoCD if your team values a visual dashboard, you need multi-cluster management from a central control plane, you want the largest community and ecosystem, or you plan to use Argo Rollouts for progressive delivery. ArgoCD is the safe default choice for most organizations.
  • Choose Flux if you prefer a CLI-first, Kubernetes-native approach, you need strong multi-tenancy, you want image automation (automatically deploying new image tags), or you are already invested in the CNCF ecosystem. Flux is lighter-weight and more composable than ArgoCD.
  • Choose Jenkins X only if you are starting a greenfield project, your team is comfortable with opinionated platforms, and you want a complete CI/CD platform rather than just a GitOps controller. For existing clusters, Jenkins X is rarely the right choice.

For most teams, the decision comes down to ArgoCD vs Flux. If you need a UI, choose ArgoCD. If you do not need a UI and prefer maximum flexibility, choose Flux. Both are production-grade, both have active communities, and both will serve you well.

Compare EKS, AKS, GKE, and OKE features and pricing

Written by CloudToolStack Team

Cloud architects with 15+ years of production experience across AWS, Azure, GCP, and OCI. We build free tools and write practical guides to help engineers navigate multi-cloud infrastructure.

Disclaimer: This article is for informational purposes. Cloud services and pricing change frequently; always verify with official provider documentation. AWS, Azure, GCP, and OCI are trademarks of their respective owners.