Push CD vs Pull CD
Your CI system holds a kubeconfig for production. It has to, because the deploy step runs
helm upgrade. Now answer this: what is the blast radius of one compromised pipeline dependency? Not the pipeline you wrote, the seventeen actions and containers it pulls in to do its job.
The Concept Explained
The distinction is only about which side initiates the connection, and everything else follows from it.
PUSH CI pipeline ──holds cluster credentials──> cluster API
"I will connect to you and apply these manifests"
PULL cluster agent ──holds repo credentials──> Git repository
"I will connect out, fetch what I should be running, and make it so"
In push, the deployer lives outside the cluster and reaches in. In pull, the deployer lives inside the cluster and reaches out.
That single reversal changes the security model, the network requirements, and whether drift gets corrected. It is the mechanical core of GitOps, and it is why the third principle from Lesson 1.1 says "pulled automatically" rather than "deployed automatically."
Credentials flow toward whoever initiates. In push CD, a system outside your trust boundary holds standing write access to production. In pull CD, that credential does not exist: the cluster holds read access to a repository, and nothing outside needs any access to the cluster at all. You are not adding a control, you are removing the thing that needed controlling.
How It Works
The credential problem, at scale
One pipeline and one cluster looks manageable. The arithmetic is what gets you.
PUSH, 8 teams x 4 environments
32 pipelines, each holding credentials to a cluster
every credential needs rotation, scoping, storage, and audit
every CI runner is a path to production
compromise of the CI platform itself reaches everything
PULL, same estate
32 agents, each holding read-only access to a repository
zero standing cluster credentials outside the clusters
compromise of CI produces a commit, which is reviewable and revertible
The second model does not make CI safe to compromise. It changes what a compromise gets you: the ability to propose a change to a repository, subject to whatever review and branch protection you have, instead of direct authenticated access to the production API server.
This is the same class of mistake as mounting the Docker socket into a build container. In both cases the pipeline is handed a credential that is far broader than the task requires, because the task was described as "deploy the app" rather than "hold permanent administrative access to the cluster." The fix is the same in shape: stop needing the credential, rather than protecting it better.
The network consequence
Push CD requires the pipeline to reach the cluster's API server. For a hosted CI system, that means the API server is reachable from outside your network, or you run self-hosted runners inside it, or you punch a hole and maintain an allow-list of CI egress addresses that changes without warning.
Pull CD inverts this too. The agent makes an outbound connection to Git and to a registry. The API server needs no inbound access from your delivery system at all, which is what makes GitOps the natural fit for private clusters, on-premises estates, edge deployments, and anything an air-gap or a strict security review is involved in.
What push actually gives you that pull does not
An honest comparison, because pull is not strictly better.
Immediacy and certainty. A push pipeline applies now, then reports what happened. Anyone watching the pipeline knows whether the deploy worked within seconds. With pull, the pipeline finishes at the commit, and the result of the deployment surfaces somewhere else, on its own schedule. Teams feel this loss immediately: "the pipeline is green, is it live?" becomes a real question requiring a real answer (Lesson 8.1).
Cross-system orchestration. Some deployments genuinely span systems: run a database migration, then deploy, then invalidate a CDN, then notify a partner. A pipeline can sequence that trivially because it is a script. A reconciler has no natural way to express "and then, elsewhere." Sync hooks (Lesson 4.2) cover part of it and not all of it.
Simplicity for small estates. One team, one cluster, a kubectl apply step: the operational overhead of running and upgrading a reconciler may genuinely exceed its benefit. GitOps pays off with more clusters, more teams, and stricter audit needs. Saying that plainly is more useful than pretending everyone needs it.
The one-shot problem
There is a second difference that gets much less attention than credentials, and it may matter more day to day.
A push pipeline is edge-triggered. It runs when something happens, does its work, and exits. Its relationship with the cluster ends at that moment. If the deployment is later changed, deleted, or partially applied, the pipeline neither knows nor cares. It already succeeded.
A pull agent is level-triggered. It compares continuously. Missing an event costs nothing, because the next comparison catches up regardless of how the difference arose.
push commit ──> apply ──> done. cluster drifts. nothing notices.
pull commit ──> apply ──> compare ──> compare ──> compare ──> ...
drift detected and corrected
This is the same architectural idea Kubernetes itself is built on, and Lesson 1.3 develops it properly. The practical version: a pipeline can tell you a deployment succeeded, and only a reconciler can tell you the cluster is currently correct.
The hybrid everyone actually runs
Push against pull is not a choice you make for the whole system. The standard shape splits by role:
CI (push) build image, test, push to registry, commit new tag to config repo
holds: registry credentials, Git write access
holds NOT: any cluster credential
CD (pull) agent notices the commit, reconciles the cluster
holds: read access to the config repo, in-cluster permissions
CI still "pushes," to a registry and a repository. What it no longer does is touch the cluster. That boundary is the whole change, and the payoff is that the highest-value credential in your organisation stops existing outside the environment it controls.
In Production
- Migration is incremental, and should be. Move one non-critical application to pull, keep the push path for everything else, and delete the pipeline's cluster credentials only when nothing needs them. The dangerous version is a big-bang cutover with a "temporary" kubeconfig kept for emergencies, which then lives forever.
- The remaining credential is Git. You have not eliminated trust, you have relocated it. Branch protection, required reviews, signed commits, and who can approve changes to the config repo are now production access controls. Treat that repository as production infrastructure, because it is.
- Registry credentials still flow inward. The cluster pulls images, so registry access is a real trust relationship, and image tag mutability becomes a security property. Pinning by digest is what makes "what Git says" and "what is running" the same statement.
- Emergency access needs a designed path. With push, the emergency path was "run the pipeline manually." With pull, it must be decided in advance: a break-glass procedure, a documented way to pause reconciliation, and an expectation that changes made outside Git get reconciled back afterwards.
- Audit changes shape. Previously the record of a deploy lived in CI logs, which usually expire. Now it lives in Git history, which does not.
Tradeoffs and Decision Framework
Security against feedback speed. Pull removes standing cluster credentials, and it also removes the tight feedback loop of a pipeline that reports success. You get the first benefit immediately; you must build the second back deliberately.
Reconciliation against orchestration. Reconcilers converge on state and do not sequence work across systems. Multi-step, cross-system releases either use hooks, keep a thin orchestration layer, or split into stages.
Fleet size. At one cluster, push is simpler and the credential exposure is small. At twenty, push means twenty standing credentials and pull means zero, and the argument stops being close.
Regulatory posture. If you must demonstrate that every production change was reviewed and recorded, pull gives you that structurally. Push requires you to prove your pipeline enforces it, which is a much weaker claim to defend in an audit.
Common Mistakes
Adopting pull while keeping the push credentials. Every attack path you were removing is still open, and now you have two delivery mechanisms.
Treating the config repository as ordinary code. It is production access. Unprotected main on the config repo is equivalent to unprotected production.
Leaving a permanent break-glass kubeconfig. A credential that exists is a credential that can be stolen. Prefer a short-lived, audited, deliberately awkward path.
Expecting the pipeline to tell you the deploy succeeded. It cannot any more. It only knows the commit landed. Build sync and health notifications, or people will keep asking whether it is live.
Forgetting the registry. Mutable tags mean the running image can change without any commit. Pin by digest so the declared state is genuinely complete.
Assuming pull removes the need for orchestration. Migrations, data changes, and cross-system steps still need sequencing, and a reconciler does not provide it.