Deploying Containers from CI/CD
Once a container image is built, scanned, and pushed to a registry, the pipeline's final job is to get that image running in a target environment — a Kubernetes cluster, a serverless container platform like AWS Fargate or Cloud Run, or a simpler VM-based Docker host. How this happens varies enormously across organizations, ranging from a pipeline directly issuing imperative commands against an API, to a fully declarative GitOps model where the pipeline's only job is to update a manifest and a separate controller handles the actual rollout. Understanding these patterns, and their tradeoffs around auditability, blast radius, and rollback speed, is central to designing dependable container deployments.
Cricket analogy: Like the final step after a player is selected and cleared — actually getting him onto the field, whether a captain personally inserts him (imperative) or team management updates the official team sheet for match officials to process (GitOps-style), with different implications for accountability and how fast a mistake reverses.
Push-Based Deployment
In a push-based model, the CI pipeline itself has credentials to the target environment and directly issues the update — running kubectl set image, calling a cloud provider's deploy API, or executing a Helm upgrade. This is simple to set up and gives immediate feedback within the pipeline about whether the deployment succeeded. The tradeoff is that the pipeline now holds powerful, often broad, credentials to production infrastructure, and any pipeline misconfiguration, malicious code injection, or compromised CI runner has a direct path to modifying production. Push-based deployment also means the actual state of the cluster can drift from what's declared in git if anyone runs manual kubectl commands outside the pipeline.
Cricket analogy: Like giving the assistant coach the master key to change the batting order directly on the official scoresheet mid-match — fast and instant, but if that key is misused or someone else scribbles changes on the scoresheet later, the official record no longer matches what's happening on the field.
GitOps: Pull-Based Deployment
In the GitOps model, the CI pipeline's only responsibility after pushing an image is to update a manifest in a separate 'deploy' git repository (or a designated path in the same repo) to reference the new image tag, then open or auto-merge a commit. A cluster-resident controller — Argo CD or Flux are the dominant tools — continuously watches that repository and reconciles the live cluster state to match it, pulling the change rather than the pipeline pushing it. This inverts the trust model: the CI pipeline never holds cluster credentials at all, only git write access, while the in-cluster controller holds the deployment credentials and is the only thing that can modify the cluster. This significantly shrinks the blast radius of a compromised CI pipeline and gives a full audit trail of every deployment as git commits.
Cricket analogy: Like a captain only submitting the team sheet to the match referee's office (git write access) rather than substituting players himself, while match officials (controller) actually enforce the lineup on the pitch, so a rogue captain can only alter paperwork, never play directly, and every change is logged.
# .github/workflows/deploy.yml — GitOps-style: pipeline only updates the manifest
name: promote-to-staging
on:
workflow_run:
workflows: ["docker-build"]
types: [completed]
jobs:
update-manifest:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Checkout deploy repo
uses: actions/checkout@v4
with:
repository: acme/k8s-manifests
token: ${{ secrets.DEPLOY_REPO_TOKEN }}
- name: Update image tag with yq
run: |
yq -i '.spec.template.spec.containers[0].image = "ghcr.io/acme/api:${{ github.sha }}"' \
environments/staging/deployment.yaml
- name: Commit and push
run: |
git config user.name "ci-bot"
git config user.email "ci-bot@acme.dev"
git commit -am "deploy: api image ${{ github.sha }} to staging"
git push
# Argo CD, watching this repo, detects the commit and syncs the cluster.GitOps flips the deployment analogy from a delivery truck driving into your warehouse (push) to a warehouse worker continuously checking a shared order sheet and restocking shelves to match it (pull). The worker inside never needs to trust or admit anyone from outside.
A subtle failure mode in push-based deployments is 'kubectl apply drift' — if the pipeline applies a partial manifest or a manual hotfix is applied directly to the cluster, the git-declared state and the live state silently diverge, and the next pipeline run may unexpectedly revert an emergency fix nobody remembered was manual.
Deployment Verification and Health Gates
Regardless of push or pull model, a responsible deployment pipeline does not consider the job done the moment a new image reference is applied — it must verify the new version is actually healthy before declaring success. This typically means polling readiness/liveness probes, checking that the expected number of replicas reached Ready state within a timeout, and optionally running smoke tests against the newly deployed endpoint. Pipelines that skip this step can report a false 'deployment successful' status while pods are actually crash-looping in production.
Cricket analogy: Like not declaring a returning injured bowler match-fit the moment he steps onto the field — the physio must watch him bowl a full spell at pace within a set window and run him through drills before certifying him, otherwise a team risks fielding someone who breaks down mid-over.
- Push-based deployment has the CI pipeline directly apply changes using credentials to the target environment.
- GitOps (pull-based) has the pipeline only update a manifest; an in-cluster controller reconciles the live state to match it.
- GitOps shrinks the CI pipeline's blast radius since it never holds direct cluster credentials.
- Manual out-of-band changes cause drift between declared and live state in push-based setups.
- Deployment pipelines should verify rollout health (readiness, replica counts, smoke tests) before declaring success.
- Argo CD and Flux are the dominant GitOps controllers used to reconcile Kubernetes clusters against git.
Practice what you learned
1. What is the defining characteristic of push-based container deployment?
2. How does GitOps change the trust model compared to push-based deployment?
3. What is a key benefit of GitOps regarding blast radius?
4. What causes 'kubectl apply drift' in push-based deployments?
5. Why should a deployment pipeline verify rollout health rather than stop after applying the manifest?
Was this page helpful?
You May Also Like
Container Registries and Tagging Strategies
Explore how CI/CD pipelines push images to container registries and the tagging conventions that keep deployments traceable, safe, and repeatable.
Building Docker Images in Pipelines
Understand how CI/CD pipelines build, tag, and optimize Docker images, including layer caching, multi-stage builds, and build-time security checks.
Blue-Green Deployments
Understand the blue-green deployment pattern for near-instant, low-risk releases and rollbacks by running two full environments and switching traffic between them.
Rollback and Incident Response in CI/CD
Learn how to design pipelines that can quickly and safely revert a bad deployment, and how rollback strategy fits into a broader incident response process.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics