Skip to content

CI Builds It. Who Ships It?

A tangle of pipeline logos handing off a glowing package to the Argo CD octopus logo, which is juggling it among many outstretched tentacle-arms toward a Kubernetes cluster.

For a long time, "deploying to production" meant somebody running kubectl apply from their laptop, on a Friday, with a Slack message that said "pushing now, watching logs." It worked, mostly, the way driving on a spare tire works: technically fine, right up until it very much isn't, and everyone silently agreed not to look too closely at the tread. Nobody was proud of it. Everybody did it anyway, because the alternative involved a change-management form nobody wanted to fill out.

GitOps is the industry's collective confession that this was never fine. It's the moment we admitted the whole thing had been running on kubectl edit, helm upgrade --set image.tag=latest-ish, and one Terraform apply that "should be a no-op" - the software equivalent of holding your exhaust pipe on with a coat hanger and calling it maintenance. Argo CD is the tool that made the confession stick: point it at a Git repo, and it reconciles your cluster to match that repo, continuously, whether you're watching or not. Drift happens, it notices. Someone edits a Deployment by hand at 2am, it politely reverts them. Rollback is git revert, not archaeology.

The wins are real, so let's give them their due:

  • An actual audit trail. "Who deployed this and why" has an answer with a commit hash attached, instead of a half-remembered Slack thread from three weeks ago.
  • Drift detection and self-healing. The cluster's state and the repo's state are supposed to match, and Argo CD tells you the moment they don't - instead of you finding out during an incident.
  • Rollback that isn't a fire drill. Reverting a commit and letting reconciliation catch up is a fundamentally calmer operation than re-running a deploy script and hoping.
  • Declarative state as the actual source of truth, not "the source of truth, approximately, as long as nobody touched anything."

That's the part everybody covers in the conference talk. Here's the part that gets cut for time: Argo CD didn't replace your CI. It just moved the finish line. Most enterprises still haven't gone and looked at what's standing at the new one.

Nobody Ripped Out Jenkins

Walk into almost any large organization doing "GitOps" today and you will still find Jenkins, CircleCI, or GitHub Actions doing exactly what they did five years ago: checking out code, running tests, building an image, pushing it to a registry. Argo CD was never trying to take that job. It doesn't build software, and it isn't asking your CI team to change how they work. What actually shifts is on the Kubernetes side of the fence: the moment a platform engineer used to spend writing deploy scripts and babysitting kubectl apply now gets spent somewhere else entirely - designing what happens the instant that build finishes.

In the old world, CI didn't stop at the build. It kept going and invoked the deploy itself, usually by calling out to something that acted as CD without ever earning the name: a shell script sitting in the repo, a Jenkins post-build step that SSHed into a box, an internal CLI that wrapped kubectl apply with just enough logic to feel official. CI built the thing, then CI (or its script's second cousin) shipped the thing, and the two jobs blurred into one long pipeline nobody fully owned end to end.

In the GitOps world, CI's job gets simpler, not more elaborate: build the image, then write to a Git repo. Bump an image tag, or update a value in a Helm chart. That's it. Argo CD picks it up from there. It sounds like a small change because it is one, mechanically. What it actually does is draw a clean line where there used to be a blur - and most enterprises are still figuring out exactly where to put it.

A few things get real the moment you look closely at that handoff:

Something has to write back to Git, and "something" matters. Argo CD only knows one thing: what's in the repo. It doesn't watch your registry, it doesn't know a new image exists, and it doesn't care that CI just went green. If nobody updates the manifest, Argo CD will happily, correctly, reconcile the cluster right back to the old version, because as far as it's concerned that's still the desired state. So the new image tag has to land in Git before it can land anywhere else - that step is not optional, it's the entire mechanism. The only question is what performs it. Best case, it's your CI pipeline itself opening a PR against the config repo with a scoped, short-lived token - the same system that just ran your tests, explaining exactly why this tag is the one going out, with a review gate built in. Decent case, it's a well-known tool like Argo CD Image Updater watching the registry and committing new tags on its own - honest about what it's doing, just one more standing service with write access to production config, and no CI system in the loop to explain itself. Worse case, it's a CI step with a Git token running yq against a values file and pushing straight to main, no review, no pause, no PR. Worst case - and this is the one that should keep you up at night - it's a handoff to some custom internal tool a departed engineer wrote two years ago, that nobody fully understands, that does something with a webhook and a PUT request and appears to work. That last option exists at more companies than anyone wants to admit, usually named something like deploy-sync-v2-FINAL, with a README that says "TODO: document this."

Config repo vs. app repo stops being a style preference and becomes an architecture decision. Once multiple CI pipelines from multiple teams are all writing back to the GitOps repo, you need real answers to who owns which paths, what happens on merge conflicts between an automated commit and a human PR, and whether a bot account with write access to your GitOps repo is itself a security boundary worth taking seriously. (It is.)

Compliance processes don't know what to do with a green checkmark from a bot. Plenty of large organizations still require someone specific, by name, to sign off before a production change ships. GitOps can absolutely support that - a PR against the config repo, a required reviewer, a merge as the approval gate - but that only works if someone deliberately designed it that way. Bolt an automated image-updater onto a repo that also needs a change-advisory board sign-off, and you've quietly built an approval bypass, not a pipeline improvement. This is precisely where big-company GitOps rollouts stall: not on installing Argo CD, which takes an afternoon, but on redesigning the CI-to-CD handoff so it satisfies the same governance the old imperative process had, without recreating the imperative mess GitOps was supposed to end.

Designing the Handoff on Purpose

If you're rolling this out for real, the handoff deserves the same design attention as the reconciliation loop itself:

  • Default to CI opening a PR against the GitOps repo, not a standing service polling your registry. Argo CD Image Updater is fine for a side project, but think about what it actually is: another always-on component with write credentials to your production config, watching your registry independently of the CI system you already log, gate, and trust. A CI step that opens a PR with a scoped, short-lived token keeps the artifact and its promotion in one auditable place - the same system that ran your tests knows exactly why this tag is the one going out - and it gives you a review gate for free, instead of bolting one on later. Reserve Image Updater for the narrow case where speed matters more than traceability and nobody's going to ask an auditor about it. Whichever you pick, pick one - every additional writer to that repo is another thing that can silently break, and another thing to explain to compliance.
  • Separate "CI produced an artifact" from "that artifact is approved to ship." An image landing in a registry is not the same event as that image being promoted to production. Make the promotion step - the actual Git write Argo CD reacts to - a deliberate, visible action, whether that's an automated policy or a human merging a PR.
  • Give the bot account boundaries. Scope its write access to exactly the paths it needs, log everything it does, and treat its commits as attributable, auditable events - not an exemption from the audit trail you just built everything else around.
  • Write down what your compliance process actually requires, then map it onto specific Git operations - PR review, required approvers, branch protection - instead of assuming "we use GitOps now" satisfies an auditor who's going to ask "show me the approval for this specific change."

None of this is exotic. It's the same rigor you already apply to CI, applied to what happens the moment CI hands off - the part that used to be a custom script, a shaky invocation, and a prayer.

GitOps didn't fire your CI system. It gave it a cleaner handoff to a partner who actually keeps its promises - but only if somebody designs that handoff on purpose. Otherwise you'll end up with a bespoke handoff mechanism called deploy-sync-v2-FINAL, stitched together over a long weekend by whoever was on call, running in production right now, answerable to no one.