Choosing a GitOps Tool#

The term “GitOps” is applied to everything from a simple kubectl apply in a GitHub Actions workflow to a fully reconciled, pull-based deployment architecture with drift detection. These are fundamentally different approaches. Choosing between them depends on your team’s operational maturity, cluster count, and tolerance for running controllers in your cluster.

What GitOps Actually Means#

GitOps, as defined by the OpenGitOps principles (a CNCF sandbox project), has four requirements: declarative desired state, state versioned in git, changes applied automatically, and continuous reconciliation with drift detection. The last two are what separate true GitOps from “CI/CD that uses git.”

Pull-based reconciliation means a controller running inside your cluster watches a git repository and continuously compares the desired state (what is in git) against the actual state (what is in the cluster). When they diverge, the controller reconciles. This happens regardless of whether a CI pipeline ran. If someone runs kubectl edit and changes a deployment, the controller detects the drift and reverts it.

Push-based deployment means a CI pipeline builds, tests, and then actively pushes changes to the cluster using kubectl apply or helm upgrade. There is no continuous reconciliation. If someone manually changes a resource, the pipeline does not know or care until the next CI run overwrites it.

Both approaches are valid. The question is which tradeoffs matter to your team.

ArgoCD: UI-Centric Pull-Based GitOps#

ArgoCD is a CNCF graduated project that runs as a set of controllers in your cluster. It introduces the Application CRD, which points to a git repository (or Helm chart, or Kustomize directory) and a target cluster/namespace. ArgoCD continuously watches the git source and the live cluster state, showing you the diff in a web UI.

Choose ArgoCD when:

  • Your team wants a visual dashboard showing sync status across all applications
  • You manage multiple clusters from a single ArgoCD instance (hub-and-spoke model)
  • You need visual diffs between desired and live state for troubleshooting
  • You use a mix of Helm charts, Kustomize overlays, and plain YAML manifests
  • You want ApplicationSets to generate Application CRDs from templates (monorepo or multi-tenant patterns)
  • Audit and compliance requirements benefit from a clear UI showing who synced what and when

Limitations: ArgoCD’s single-instance multi-cluster model becomes a bottleneck at scale. Beyond roughly 500 Applications, you need to consider sharding (multiple application controllers) or splitting into multiple ArgoCD instances. The UI is a strength but also a crutch – teams sometimes debug through the UI instead of building proper observability. Resource consumption is non-trivial: the application controller, repo server, and Redis instance all need tuning for large installations.

Flux: CLI-Centric Composable GitOps#

Flux (CNCF graduated) takes a different architectural approach. Instead of a single Application CRD, Flux uses composable CRDs: GitRepository (or OCIRepository, HelmRepository) defines the source, Kustomization defines what to apply from that source, and HelmRelease defines a Helm chart deployment with values. These CRDs compose together, and each is managed by a separate controller.

Choose Flux when:

  • You prefer CLI and API interaction over a web UI
  • You want a native Helm controller that manages HelmRelease CRDs declaratively (including automated chart version bumps)
  • You need SOPS or Mozilla Age integration for encrypting secrets in git
  • The composable CRD architecture maps well to your team’s mental model (sources, kustomizations, and helm releases as separate concerns)
  • You want per-cluster Flux installations rather than a central management plane
  • Image automation is important (Flux can watch container registries and update git with new image tags)

Limitations: No built-in UI. Third-party options like Weave GitOps or the Flux web UI exist but are not as mature as ArgoCD’s dashboard. The composable CRD model has a steeper initial learning curve – you need to understand how GitRepository, Kustomization, and HelmRelease interact. At scale (500+ HelmReleases), the Helm controller can become memory-hungry and slow to reconcile. Debugging reconciliation failures requires reading controller logs rather than clicking through a UI.

Jenkins: Push-Based with Maximum Pipeline Flexibility#

Jenkins is not a GitOps tool. It is a CI/CD automation server that can deploy to Kubernetes as one step in a complex pipeline. It uses a push-based model: a Jenkinsfile defines stages that build, test, and deploy, and Jenkins actively runs kubectl apply or helm upgrade against the cluster.

Choose Jenkins when:

  • You have an existing Jenkins investment with established pipelines and shared libraries
  • You need complex approval gates, manual intervention steps, or multi-stage promotion workflows
  • Deployments span Kubernetes and non-Kubernetes targets (VMs, serverless, bare metal)
  • Your pipeline logic is too complex for declarative GitOps (conditional deployments, integration test gates, canary analysis with rollback)
  • You need the plugin ecosystem (thousands of integrations for notification, testing, artifact management)

Limitations: No drift detection. Jenkins does not know if someone manually changed the cluster after deployment. No continuous reconciliation – the cluster state can drift between pipeline runs. Jenkins itself requires significant maintenance (plugin updates, security patches, controller/agent infrastructure). Credential management for cluster access adds operational burden. Scaling Jenkins agents for parallel builds requires its own infrastructure planning.

GitHub Actions: Push-Based Simplicity#

GitHub Actions provides CI/CD built into GitHub. For Kubernetes deployments, workflows typically build a container image, push it to a registry, and then run kubectl apply or helm upgrade using a kubeconfig stored as a repository secret.

Choose GitHub Actions when:

  • You are a small team with a GitHub-centric workflow
  • Deployments are straightforward (build image, deploy to one or two clusters)
  • You do not want to run additional controllers or servers in your cluster
  • Cost sensitivity – GitHub Actions runners are included with GitHub plans (with usage limits)
  • You want the fastest path from “code merged” to “deployed” without infrastructure overhead
  • Your deployment target is a managed Kubernetes service where push-based is acceptable

Limitations: No drift detection, no continuous reconciliation. Secrets (kubeconfig, cloud credentials) must be stored in GitHub Secrets, which limits rotation and audit capabilities. Complex deployment workflows (multi-cluster, canary, blue-green) require significant custom scripting. GitHub Actions has usage limits on free and team plans. No built-in rollback mechanism beyond re-running a previous workflow.

Comparison Table#

Criteria ArgoCD Flux Jenkins GitHub Actions
Deployment model Pull-based Pull-based Push-based Push-based
Drift detection Yes, continuous Yes, continuous No No
Rollback Sync to previous git commit Sync to previous git commit Re-run previous pipeline Re-run previous workflow
Multi-cluster Yes, from single instance Yes, per-cluster install Yes, via config Yes, via secrets
Secrets handling External (Vault, SOPS via plugin) Native SOPS/Age support Credentials plugin GitHub Secrets
Web UI Full-featured built-in Third-party only Built-in (basic) Built-in (workflow runs)
Helm support Application pointing to chart Native HelmRelease CRD helm CLI in pipeline helm CLI in workflow
Kustomize support Native Native kustomize CLI in pipeline kustomize CLI in workflow
CNCF status Graduated Graduated N/A N/A
Operational overhead Medium (runs in cluster) Medium (runs in cluster) High (Jenkins infrastructure) Low (managed by GitHub)
Learning curve Medium Medium-high High (Groovy, plugins) Low
Best scale point 10-500 Applications 10-500 HelmReleases Unlimited pipelines Simple deployments

The Hybrid Reality#

Most production teams do not pick a single tool. The dominant pattern is:

GitHub Actions (or Jenkins) for CI – build, test, scan, push container image to registry. This is push-based and event-driven, triggered by pull requests and merges.

ArgoCD or Flux for CD – watch the git repository for updated image tags or Helm values, reconcile the cluster to match. This is pull-based and continuous.

The CI pipeline never talks to the cluster directly. It updates a git repository (changing an image tag in a values file or kustomization), and the GitOps controller picks up the change. This separation means CI credentials never include cluster access, and the cluster’s desired state is always represented in git.

Scaling Considerations#

ArgoCD at scale (500+ Applications): The application controller becomes the bottleneck. Mitigations include sharding (running multiple application controllers, each responsible for a subset of Applications), increasing controller replicas with leader election, tuning reconciliation intervals, and using server-side diff instead of client-side. Beyond 1000+ Applications, consider multiple ArgoCD instances split by team or environment.

Flux at scale (500+ HelmReleases): The Helm controller’s memory usage grows with the number of releases and chart complexity. Mitigations include increasing controller resource limits, adjusting reconciliation intervals, using OCIRepository sources instead of HelmRepository (faster pulls), and splitting across multiple namespaces with separate controller shards. The source-controller can also become a bottleneck if it is polling many git repositories frequently.

Migration Path: Push-Based to Pull-Based#

Teams often start with GitHub Actions push-based deployments and later adopt ArgoCD or Flux. A progressive migration looks like this:

  1. Keep CI as-is. GitHub Actions continues to build, test, and push images.
  2. Add GitOps controller alongside push-based deployment. Install ArgoCD or Flux, point it at the same manifests, but set it to manual sync (ArgoCD) or suspended (Flux). This lets you see drift without the controller acting on it.
  3. Switch one application to auto-sync. Pick a low-risk application and let the GitOps controller manage it. Remove the kubectl apply step from the CI pipeline for that application.
  4. Migrate progressively. Move applications one at a time, validating that the GitOps controller handles each correctly.
  5. Remove cluster credentials from CI. Once all applications are managed by the GitOps controller, the CI pipeline only needs to push images and update git – no cluster access required.

This migration typically takes weeks to months depending on the number of applications and team comfort with the pull-based model.