CI CD Platform Models Compared
The market of Continuous Integration / Continuous Delivery (CI/CD) engines looks bewilderingly crowded — GitHub Actions, GitLab CI/CD, Jenkins, Tekton, Argo Workflows, Argo CD, Spinnaker, Buildkite, CircleCI, and a dozen more — but almost all of the meaningful differences collapse onto four architectural axes. Once you can place a tool on those axes, “which CI/CD platform should we use?” stops being a feature-checklist debate and becomes a small set of principled decisions driven by where your code lives, where your compute lives, and what you are delivering to. This is the synthesis note for §8 of the Continuous Integration and Delivery MOC: it defines the four axes, positions each engine on them, and gives a decision procedure. The individual tool deep-dives — GitHub Actions, GitLab CI CD, Jenkins, Tekton, Argo Workflows, Spinnaker, Buildkite, CircleCI — are its children; this note is the map, they are the territory.
Mental Model — Four Axes, Not a Feature List
The trap in comparing CI/CD tools is to compare features (matrix builds, caching, secret stores) — because on features they have all converged, and any of them can be made to do almost anything. The durable differences are architectural: they are baked into the tool’s shape and are expensive to change. There are four.
flowchart TD ROOT["Placing a CI/CD engine"] --> A1["Axis 1 — DELIVERY MODEL<br/>Push vs Pull"] ROOT --> A2["Axis 2 — EXECUTION LOCUS<br/>Hosted vs Self-Hosted"] ROOT --> A3["Axis 3 — SOURCE COUPLING<br/>Repo-Coupled vs Standalone"] ROOT --> A4["Axis 4 — CONFIG PARADIGM<br/>Imperative vs Declarative / K8s-Native"] A1 --> A1a["CI pushes to target<br/>(GH Actions, GitLab, Jenkins)"] A1 --> A1b["Agent in cluster pulls state<br/>(Argo CD, Flux — GitOps)"] A2 --> A2a["Vendor runs the compute<br/>(GH-hosted, CircleCI cloud)"] A2 --> A2b["You run the compute<br/>(Jenkins, self-hosted runners)"] A3 --> A3a["Lives in .github/ / .gitlab-ci.yml<br/>bound to one repo+SCM"] A3 --> A3b["Separate system, any SCM<br/>(Jenkins, Spinnaker, Buildkite)"] A4 --> A4a["Scripted steps, plugins<br/>(Jenkins Groovy)"] A4 --> A4b["Declarative CRDs as pods<br/>(Tekton, Argo)"]
What it shows and the insight to take: every engine is a point in this 4-dimensional space, and the axes are largely independent. Axis 1 (push/pull) is about how delivery reaches the target and is the deepest divide — it splits classic CI (push) from GitOps (pull). Axis 2 (hosted/self-hosted) is about who operates the compute and drives cost, control, and network reach. Axis 3 (repo-coupled/standalone) is about how tightly the pipeline is welded to one source-control system. Axis 4 (imperative/declarative) is about how the pipeline is expressed and executed — a Groovy script on a plugin-driven server versus a set of Kubernetes Custom Resources reconciled as pods. The insight is that a “CI/CD comparison” is really four smaller, orthogonal comparisons; conflating them is why these debates go in circles.
Axis 1 — Push vs. Pull Delivery
This is the most consequential axis because it changes the trust boundary. In the classic push model, the CI system holds credentials to the target environment and pushes the deployment out — it reaches into the cluster or cloud account and applies the change. In the pull model (GitOps), an agent inside the target continuously pulls the desired state from Git and reconciles the live system toward it; nothing outside the cluster holds cluster-write credentials.
sequenceDiagram participant Git as Git (desired state) participant CI as CI Engine (outside) participant Tgt as Target Cluster rect rgb(240,220,220) Note over CI,Tgt: PUSH — CI holds cluster creds CI->>Tgt: kubectl apply / deploy (reach in) Note over CI: credential blast radius = the CI system end rect rgb(220,235,220) Note over Git,Tgt: PULL — agent inside pulls Tgt->>Git: agent watches for changes Git-->>Tgt: new desired state Tgt->>Tgt: reconcile live → desired (converge) Note over Tgt: no external system has cluster-write creds end
What it shows and the insight to take: in push, a compromise of the CI system is a compromise of production, because the CI system is the thing with the keys. In pull, the reconciler runs inside the trust boundary and only reads from Git, so drift is auto-corrected and the credential blast radius shrinks dramatically. The four OpenGitOps principles formalize the pull model: desired state is declarative, versioned and immutable, pulled automatically by agents, and continuously reconciled (OpenGitOps). This axis is why Argo CD and Flux are a different category of tool from GitHub Actions: they are CD reconcilers, not CI runners. Most real organizations run both — a push-based CI engine to build and test, handing off to a pull-based reconciler for delivery. The full mechanism is in Push-Based versus Pull-Based Delivery.
Axis 2 — Hosted vs. Self-Hosted Execution
Who owns and operates the machines that run the jobs? Hosted (SaaS) execution — GitHub-hosted runners, CircleCI’s cloud, Buildkite-hosted agents — means the vendor provisions, patches, scales, and secures the compute; you trade money and some control for zero operational burden. Self-hosted execution — Jenkins agents, GitHub/GitLab self-hosted runners, your own Buildkite agents — means you run the compute, gaining control over hardware (GPUs, big memory, specific architectures), network placement (inside your VPC, reaching private services), data residency, and cost at scale, at the price of operating it (see Self-Hosted versus Cloud Runners).
Crucially, this axis is often not a binary choice per tool — it is a spectrum, and the most interesting engines are hybrid. Buildkite is the canonical hybrid: a hosted control plane (the orchestration, UI, and API are Buildkite’s SaaS) with self-hosted agents (your compute, in your network) that poll the Buildkite API outbound over HTTPS — “no need to forward ports or provide incoming firewall access” (Buildkite agent). You get SaaS convenience for orchestration and full control over where the actual builds run. GitHub Actions and GitLab CI are similarly hybrid: hosted control plane, and you choose hosted or self-hosted runners per job. CircleCI is primarily hosted but also offers self-hosted runners (machine runners and a Kubernetes container runner) for private/regulated compute (CircleCI runner overview). Jenkins sits at the far self-hosted end: you run everything, controller and agents alike.
Axis 3 — Repo-Coupled vs. Standalone
How tightly is the pipeline welded to a single source-control system? Repo-coupled engines live inside the repository and its forge: GitHub Actions stores YAML workflows in .github/workflows and triggers on repository events, binding automation to the exact commit SHA (GitHub Actions workflows); GitLab CI reads .gitlab-ci.yml from the repo root and is woven into GitLab’s issues, registry, and environments (GitLab pipelines). The pipeline is part of the repo — trivial to set up, but it presumes you live in that forge.
Standalone engines are separate systems that connect to source control rather than living inside it. Jenkins is the archetype: an independent automation server that can build from GitHub, GitLab, Bitbucket, or plain Git, driven by a Jenkinsfile it fetches (Jenkins pipeline). Spinnaker is standalone by design — it sits downstream of CI, triggering on “Jenkins completions, Docker image appearances, or CRON schedules” (Spinnaker concepts). Tekton and Argo Workflows are standalone in a different sense: they are Kubernetes-API-native and SCM-agnostic — a pipeline is a cluster resource, not a repo file. The trade-off is convenience vs. portability: repo-coupled tools are frictionless if you are all-in on one forge; standalone tools survive a forge migration and can orchestrate across many repos and systems, at the cost of more setup.
Axis 4 — Imperative vs. Declarative / Kubernetes-Native
How is the pipeline expressed and executed? At the imperative end, a pipeline is a script of steps executed by a server, extended by plugins. Jenkins is the exemplar: a Jenkinsfile in scripted form is Groovy code (node { ... } blocks), and even its declarative form is a thin structured wrapper over the same plugin-driven engine; its power is the vast plugin ecosystem (Jenkins pipeline). YAML-based engines (GitHub Actions, GitLab CI, CircleCI) are more declarative — you declare jobs, steps, and dependencies — but the steps themselves still run imperative shell.
At the Kubernetes-native declarative end, the pipeline is a set of Kubernetes Custom Resource Definitions (CRDs). Tekton “defines a set of Kubernetes Custom Resources that act as building blocks” — a Task is a sequence of Steps (each a container in a pod), a Pipeline orchestrates Tasks, and TaskRun/PipelineRun are their executions, run as pods and managed via kubectl (Tekton pipelines). Argo Workflows is a “container-native workflow engine for orchestrating parallel jobs on Kubernetes,” where each step is a container and workflows are DAG- or step-defined CRDs (Argo Workflows). Here the paradigm is: declare the desired workflow as data, let a controller reconcile it into running pods. This is CI/CD expressed in the same language as the platform it deploys to — the appeal is uniformity (one API, one RBAC model, one scheduler) for teams already all-in on Kubernetes.
CNCF/CDF status — a freshness catch (as of 2026-07)
Project governance here is routinely mis-stated, so pin it: Argo Workflows is a CNCF graduated project (Argo Workflows docs). Tekton was a CD Foundation graduated project that transitioned to the CNCF as an incubating project in March 2026 (Tekton blog, 2026-03-25; CNCF blog, 2026-03-24) — so a claim that “Tekton is a CDF project” is now stale, and “Tekton is CNCF graduated” is wrong (it re-entered CNCF at incubating). Spinnaker remains under the Linux Foundation’s CD Foundation (Spinnaker concepts; CD Foundation projects). Re-verify at authoring time — these move.
The Big Comparison
quadrantChart title Execution locus vs. config paradigm x-axis "Imperative / scripted" --> "Declarative / K8s-native" y-axis "Self-hosted" --> "Hosted (SaaS)" quadrant-1 "Hosted + declarative" quadrant-2 "Hosted + imperative" quadrant-3 "Self-hosted + imperative" quadrant-4 "Self-hosted + declarative" "GitHub Actions": [0.55, 0.75] "GitLab CI": [0.55, 0.62] "CircleCI": [0.5, 0.85] "Buildkite": [0.45, 0.6] "Jenkins": [0.2, 0.15] "Tekton": [0.85, 0.25] "Argo Workflows": [0.85, 0.3] "Spinnaker": [0.6, 0.4]
What it shows and the insight to take: the two most visible axes (execution locus × config paradigm) already spread the field into recognizable clusters — hosted YAML SaaS in the upper band (GitHub Actions, GitLab CI, CircleCI, Buildkite), the self-hosted imperative veteran (Jenkins) in the lower-left, and the Kubernetes-native declarative engines (Tekton, Argo) on the right. Spinnaker floats mid-field because it is a delivery orchestrator, not a CI runner, and does not fit the CI framing cleanly. Positions are approximate and hybrid tools resist a single point — treat this as a cluster map, not coordinates.
| Engine | Delivery (push/pull) | Execution | Source coupling | Config paradigm | Primary role | Governance (as of 2026-07) |
|---|---|---|---|---|---|---|
| GitHub Actions | Push | Hybrid (hosted + self-hosted) | Repo-coupled (GitHub) | Declarative YAML + reusable actions | CI + light CD | Proprietary (GitHub/Microsoft) |
| GitLab CI CD | Push | Hybrid | Repo-coupled (GitLab) | Declarative YAML, needs: DAG | CI + CD | Open-core (GitLab) |
| Jenkins | Push | Self-hosted | Standalone (any SCM) | Imperative/scripted Jenkinsfile + plugins | CI (+ CD via plugins) | CDF (Linux Foundation) |
| Tekton | Push (builds); pairs w/ pull CD | Self-hosted (K8s) | Standalone (K8s-native) | Declarative CRDs → pods | K8s-native CI/CD building blocks | CNCF incubating (since Mar 2026) |
| Argo Workflows | Push (workflow engine) | Self-hosted (K8s) | Standalone (K8s-native) | Declarative CRDs, DAG/steps → pods | K8s-native workflow/CI | CNCF graduated |
| Spinnaker | Push (multi-cloud deploy) | Self-hosted | Standalone (downstream of CI) | Declarative pipelines + Managed Delivery | Multi-cloud CD | CDF (Linux Foundation) |
| Buildkite | Push | Hybrid: hosted control plane + self-hosted agents | Standalone (connects to SCM) | Declarative YAML, dynamic pipelines | CI (scalable, your compute) | Proprietary (Buildkite) |
| CircleCI | Push | Hosted (+ self-hosted runners) | Repo-coupled (GitHub/GitLab/BB) | Declarative YAML + orbs | CI + CD | Proprietary (CircleCI) |
| Argo CD | Pull (GitOps) | Self-hosted (in-cluster) | Standalone (watches Git) | Declarative (reconcile to Git) | K8s GitOps CD | CNCF graduated |
Two distinctive per-tool wrinkles worth naming because they recur in interviews: CircleCI’s orbs are reusable, versioned packages of config (commands, jobs, executors) published to a registry and namespaced per org — its answer to composability (CircleCI orb concepts) — directly analogous to GitHub Actions’ reusable actions/workflows. And Spinnaker treats deployment strategies as first-class constructs (server groups, health-check-gated red/black), reflecting its Netflix immutable-infrastructure “bakery” heritage (Spinnaker concepts) — the strategy machinery that this MOC deliberately hands off to Site Reliability Engineering MOC.
How to Choose
flowchart TD START["Choosing a CI/CD engine"] --> Q1{"Already all-in on<br/>one Git forge?"} Q1 -->|"GitHub"| GH["Start with GitHub Actions<br/>(repo-coupled, zero setup)"] Q1 -->|"GitLab"| GL["Start with GitLab CI/CD"] Q1 -->|"No / multi-forge"| Q2 Q2{"Deploying primarily<br/>to Kubernetes?"} -->|"Yes, K8s-native"| Q3 Q2 -->|"No / VMs / multi-cloud"| Q4 Q3{"Need CI builds or<br/>GitOps delivery?"} -->|"CI/workflows"| K8SCI["Tekton / Argo Workflows<br/>(CRDs as pods)"] Q3 -->|"Pull-based CD"| K8SCD["Argo CD / Flux<br/>(GitOps reconcile)"] Q4{"Deep customization /<br/>legacy / air-gapped?"} -->|"Yes"| JEN["Jenkins<br/>(self-hosted, plugins)"] Q4 -->|"Want SaaS but<br/>your own compute"| BK["Buildkite<br/>(hybrid)"] Q4 -->|"Fully managed CI"| CC["CircleCI<br/>(hosted + orbs)"] Q4 -->|"Multi-cloud deploy<br/>orchestration"| SP["Spinnaker"] GH -.->|"then hand delivery to"| K8SCD GL -.->|"then hand delivery to"| K8SCD
What it shows and the insight to take: the first question dominates. If you already live in one forge, the repo-coupled engine (GitHub Actions or GitLab CI) is the default because it is frictionless — start there and only outgrow it deliberately. The Kubernetes question forks CI (Tekton/Argo Workflows) from GitOps CD (Argo CD/Flux), and the two commonly compose. Jenkins remains the right answer for deep customization, legacy integration, and air-gapped/regulated environments where self-hosting everything is a requirement, not a burden. Buildkite fits the specific want of “SaaS orchestration but our compute.” And the dashed edges encode the most common real-world architecture: a push-based CI engine that hands off to a pull-based GitOps reconciler for delivery — the two axes composing rather than competing.
The practical sequencing:
- Source coupling first. All-in on GitHub/GitLab → use its native CI unless you have a concrete reason not to. Multi-forge or forge-migration risk → standalone (Jenkins, Buildkite, Tekton/Argo).
- Execution locus by constraint. Need private-network access, special hardware, data residency, or huge scale economics → self-hosted or hybrid runners. Otherwise hosted, for the operational-burden savings.
- Config paradigm by platform. Deploying to Kubernetes and want one API/RBAC/scheduler for everything → Kubernetes-native declarative (Tekton, Argo). Need maximal extensibility and legacy reach → imperative/plugin (Jenkins). Otherwise, declarative YAML is the pragmatic middle.
- Delivery model by trust boundary. Want drift correction and a small credential blast radius for Kubernetes delivery → pull/GitOps (Argo CD). Simpler or non-Kubernetes targets → push, engineered to be idempotent (see Pipeline Reliability and Flakiness).
Boundary discipline
This note compares the engines. The deployment strategies they enact (canary, blue-green, rolling) and the DORA metrics that judge them belong to Site Reliability Engineering MOC; the security controls they invoke (signing, SBOMs, provenance) belong to DevSecOps and Supply Chain Security MOC; the environments they target are provisioned by Infrastructure as Code MOC. Compare the machinery here; link the strategy, security, and provisioning there.
See Also
- GitHub Actions · GitLab CI CD · Jenkins · Tekton · Argo Workflows · Spinnaker · Buildkite · CircleCI — the per-engine deep-dives this note maps
- Pipeline as Code — the shared premise (pipeline expressed as a versioned repo file) that Axis 3/4 build on
- Push-Based versus Pull-Based Delivery — the full mechanism behind Axis 1
- Self-Hosted versus Cloud Runners — the full trade-off behind Axis 2
- Runners Agents and Executors — the compute layer all these engines schedule onto
- GitOps Delivery · Argo CD — the pull-model delivery half
- Pipeline Reliability and Flakiness — why push delivery must be engineered idempotent
- Continuous Integration and Delivery MOC — parent MOC (§8 Tools and Platforms)
- Site Reliability Engineering MOC — owns deployment strategy + DORA; DevSecOps and Supply Chain Security MOC — owns security controls; Infrastructure as Code MOC — provisions the targets