What is GitOps, and how is it different from push-based CI/CD?
GitOps is a way of operating infrastructure and applications in which the desired state is declared in a version-controlled repository, usually Git, and software agents continuously pull that state and reconcile the live system to match it. In push-based CI/CD, the pipeline applies changes to the cluster itself, so the pipeline needs cluster credentials and nothing corrects changes made outside it. On Kubernetes, the most widely used GitOps agents are Argo CD and Flux.
The four GitOps principles
The CNCF’s OpenGitOps project defines GitOps through four principles:
| Principle | Meaning |
|---|---|
| Declarative | The desired state of the system is expressed declaratively, for example as Kubernetes manifests. |
| Versioned and immutable | The desired state is stored in a way that enforces immutability and versioning and keeps a complete history. |
| Pulled automatically | Software agents pull the desired state from the source on their own. |
| Continuously reconciled | Agents observe the actual state and keep applying the desired state. |
The principles name a “source”, not Git specifically. Many teams store the rendered state as OCI artifacts in a container registry, and the model stays the same.
Pull-based vs push-based delivery
| Push-based CI/CD | GitOps (pull-based) | |
|---|---|---|
| Trigger | A pipeline run | A change in the repository, detected by polling or a webhook |
| Who applies changes | The CI system, with kubectl or Helm | An agent with access to the cluster |
| Cluster credentials | Stored in or issued to CI | Stay inside the cluster |
| Manual changes (drift) | Persist until the next deploy | Detected and, if configured, reverted |
| Audit trail | CI logs | Git history and pull requests |
| Rollback | Rerun an older pipeline | Revert a commit |
GitOps does not replace CI. Pipelines still build, test and push images. The difference is the last step: instead of calling the cluster, CI updates a version in the configuration repository, by commit or pull request, and the agent applies it. The guide to deploying to Kubernetes from a pipeline covers the push-based path.
How reconciliation works
A GitOps agent runs a loop. It fetches the repository at a configured revision, renders the manifests with plain YAML, Kustomize or Helm, compares them with the objects in the cluster and applies the difference. It then reports whether the application is in sync and healthy.
The same loop handles drift. If someone runs kubectl edit on a managed Deployment, the agent sees that the live object no longer matches Git. Depending on its settings, it flags the difference or overwrites it on the next reconciliation. This is the same desired-state model the Kubernetes control plane uses for Pods and Deployments, applied one level higher.
Repository layout
Two layouts are common:
- Application and configuration repositories. Source code lives in one repository; Kubernetes manifests for every environment live in another. CI writes the new image digest to the configuration repository. This keeps deploy history separate from code history and lets you restrict who can change production.
- Environment directories. A shared base with one overlay per environment, such as
stagingandproduction. Promoting a release means changing the version in the next environment’s directory through a pull request.
Avoid one branch per environment. Branches drift apart over time, and merges between them carry unrelated changes.
Secrets in GitOps
Plaintext secrets must never go into Git, even into a private repository. Two patterns work with GitOps:
- Encrypted secrets in Git, with tools such as Sealed Secrets or SOPS. The cluster holds the decryption key. The Sealed Secrets tutorial walks through this setup.
- References to an external secret manager, with the External Secrets Operator. Git stores only the name of the secret; the operator fetches the value.
The article on managing Kubernetes secrets compares both approaches.
Multi-cluster and multi-cloud GitOps
GitOps scales to many clusters because each cluster only needs to know which path of which repository to follow. There are two models. A central agent can manage many clusters with stored credentials, as Argo CD often does, or an agent in every cluster can pull its own configuration, which is the usual Flux setup. Generators, such as Argo CD ApplicationSets, create one application per cluster from a template. The comparison of Argo CD and Flux covers how the two agents differ.
When GitOps is overkill
GitOps adds a controller to operate, a second repository to maintain and a delay between merge and deploy. It may not be worth it when:
- You run one cluster with a handful of services, and a pipeline that runs
helm upgradeis easy to understand. - Releases depend on ordered imperative steps, such as database migrations that must finish before new Pods start, and you have not modeled them as hooks or Jobs.
- The team is new to Kubernetes manifests, so a broken sync loop adds another failure mode to debug.
In those cases, a push-based pipeline with OIDC authentication and rollout verification is a reasonable place to start. Moving to GitOps later only changes the last step of the pipeline.