The case for self-hosting CI is always made in the same terms: compute on your own cloud account costs a fraction of what a CI vendor charges per minute, and the savings scale with build volume. That arithmetic is correct and it is also not the number that decides the outcome.
What decides it is the denominator nobody puts in the spreadsheet: engineering hours per month spent keeping the thing running. Upgrades, certificate rotation, disk pressure on the artifact volume, a plugin that broke on the last minor version, the runner fleet that scaled to zero and would not scale back up, and the recurring hour spent explaining to a team why their build queued for twenty minutes. None of that shows up in a per-minute comparison, and all of it is real.
The second thing that decides it is autoscaling. Running static build machines is easy and wasteful. Running ephemeral machines that appear on demand and disappear afterwards is what makes self-hosting economically interesting, and it is also the hardest part of the whole exercise, because the things that make a runner cheap to create are the same things that make it slow to become useful.
This is not an argument against self-hosting. There are good reasons to do it: code that cannot leave your network, hardware nobody rents, compliance requirements, or build volume large enough that vendor metering genuinely dominates. It is an argument for pricing it honestly before you commit, because the failure mode is a platform team that spends a third of its capacity on a CI system that was supposed to save money.
Key takeaways
- The saving from self-hosting is real per minute and is consumed by maintenance hours. Estimate both sides before deciding, and estimate maintenance in a bad month rather than a good one.
- Autoscaling is the central engineering problem. Cheap runners are ephemeral, ephemeral runners have cold caches, and cache locality is usually worth more than instance price.
- Ephemeral runners are also the security answer. A persistent runner executing untrusted pull request code accumulates whatever the last build left behind.
- Choose by architecture, not features: controller-plus-agents, Kubernetes-native, or bundled into a source platform. The architecture determines what your operational burden looks like.
What self-hosting costs per month
The line items below are the ones that actually consume time. None of them are dramatic individually, which is exactly why they get left out of the business case.
Upgrades. Every component has a release cadence: the controller, the agents, the runner images, and in plugin-based systems each plugin independently. The controller and agent versions usually need to move together, which makes an upgrade a coordinated operation rather than a rolling one. Security releases do not wait for a convenient window.
Runner image maintenance. The image containing your toolchain is a distribution you now maintain. It needs base image updates for CVEs, language runtime updates when a project bumps, and a rebuild pipeline of its own. This is the item most often forgotten in planning and most reliably present in reality.
Storage. Artifacts, caches, logs and container layers all accumulate, and none of them have a natural end. Retention policy is a decision someone must make and enforce, and the day it is not enforced is the day the controller runs out of disk mid-build.
Secrets and certificates. Internal TLS certificates expire. Credentials used by runners to pull images and push artifacts rotate. Each of these is a small task with an unforgiving deadline.
Capacity and queueing. Somebody has to notice that builds are queueing before the engineers do, which means the CI system needs its own monitoring and alerting like any other production service. It is a production service. Teams that treat it as internal tooling discover this during a release.
Incident response. When CI is down, every engineer is blocked. That makes it an on-call surface, and an on-call surface for a system most of the team does not understand deeply.
Needs first-hand data: Track engineering time spent on CI operations for a full quarter, tagged by category: upgrades, runner image rebuilds, storage and cleanup, capacity incidents and user support. Divide by build volume to get a cost per build, and compare that to the vendor quote you rejected. Publish both numbers. This comparison is the whole decision and essentially nobody measures the left-hand side.
The runner autoscaling problem
This is where self-hosted CI is genuinely hard, and the difficulty is structural rather than a matter of picking the right tool.
Static runners are simple and wasteful. A fixed pool of machines is easy to operate, has warm caches and instant job pickup, and idles through nights and weekends while you pay for it. It also accumulates state: a build that leaves files behind changes the behaviour of the next build on that machine, which produces the failure class where a job passes on one runner and fails on another.
Ephemeral runners are correct and slow to start. One machine per job, destroyed afterwards, is both the clean isolation model and the cheap one. The cost is that every job now pays for instance provisioning, container image pull, and a cold dependency cache. On short jobs that overhead can exceed the work itself, which is the point where autoscaling stops saving money.
Cache locality usually matters more than instance price. A cheaper instance in a region far from your cache and registry can be slower and more expensive overall once you count transfer time and egress. Put the cache, the registry and the runners close together before optimising the instance type. This single consideration reverses a lot of naive cost comparisons.
Scale-to-zero has a first-build penalty. Scaling the fleet to nothing overnight is the largest available saving and makes the first build of the morning slow. That is usually an acceptable trade, but it needs to be a decision rather than a surprise, because the first build of the morning is often the one someone is watching.
Scaling signals are awkward. The metric you want is queue depth, and the thing that makes scaling useful is reacting before the queue forms. Most autoscalers are reactive, so there is a structural lag between demand appearing and capacity existing. A small warm pool covering typical burst size, with elastic capacity above it, is the pragmatic answer and the one most mature setups converge on.
Untrusted code changes the requirements. If your runners execute pull request code from outside the organisation, ephemeral is not an optimisation, it is the control. A persistent runner that has executed a malicious build retains whatever that build left behind, and the next job inherits it. This is covered further in CI runner providers, which discusses the same problem from the hosted-fleet side.
The OCI image spec: the standard your runners depend on
Almost every tool below expresses a build step as “run this container image”, and the thing that makes that portable is a specification rather than a product.
The OCI image and distribution specifications define what a container image is as a set of content addressed layers with a manifest, and how registries serve them. It is the reason the same runner image works under a completely different CI system, and the reason a registry from one vendor works with a runtime from another.
What it gives you
Portability of the expensive part. Your runner image, the thing that contains the toolchain and takes real effort to maintain, is not tied to your CI system. Changing CI tools does not mean rebuilding it. That decouples the two decisions in a way that is worth planning around, because it means you can standardise build environments first and choose an orchestrator second.
It also gives you content addressing, which is what makes pinning by digest meaningful. Pinning a tag guarantees nothing, since tags move. Pinning a digest guarantees byte-identical content, which is the foundation of a reproducible build environment.
What it does not do
It does not make builds reproducible by itself. An image pinned by digest gives you the same starting filesystem; everything the build then downloads is still unpinned unless you pinned it separately.
It also does not standardise the build step contract. How a CI system passes the workspace into the container, which environment variables it sets, how caches are mounted and how the exit code is interpreted are all per-tool conventions. Your images are portable; your pipeline definitions are not.
Drone

Drone popularised container-native CI: every step is an image, the pipeline is a short YAML file, and the server is a single binary with a database behind it. That simplicity is still its main argument. The important context is that its licensing and stewardship moved under a commercial owner, which is what prompted the community fork below, and that history should factor into a long-term bet.
Pros
- Very small operational surface: a server, a database and agents, with nothing else to run
- Container-native step model means no plugin system to maintain and no runtime installed on the host
- Pipeline syntax is small enough to learn in an afternoon, which matters when every team writes their own
- Integrates with the common self-hosted source platforms rather than assuming one vendor
Cons
- Licensing changes after the commercial acquisition created real uncertainty for self-hosted users, and that uncertainty has not fully dissipated
- Feature development is driven by a commercial roadmap that is oriented toward the paid product
- Limited pipeline composition compared with the heavier orchestrators, so complex fan-out gets repetitive
Best for: Small platform teams who want container-native CI with minimal moving parts and are comfortable with the licensing position.
Pricing: Open source core with a commercial enterprise offering, licensed by user count above a free threshold.
Woodpecker CI
Woodpecker is the community fork of Drone, maintained under a permissive open source licence with no commercial tier. It keeps the property that made the original attractive, a genuinely small system you can understand completely, and removes the licensing question. For teams whose main requirement is a CI system that does not surprise them, that combination is hard to beat.
Pros
- Fully open source with no commercial edition, so there is no capability withheld behind a licence
- Small enough that one engineer can understand the entire system, which is a real operational property
- Container-native steps mean runner maintenance is image maintenance rather than host configuration
- Integrates cleanly with self-hosted source platforms, which suits an entirely on-premises toolchain
Cons
- Smaller ecosystem and community than the major platforms, so unusual integrations are yours to build
- No commercial support option, which is a blocker in organisations that require a vendor contract
- Feature surface is deliberately limited, and large monorepo workflows will feel the absence of advanced orchestration
Best for: Teams running a fully self-hosted toolchain who value a small, comprehensible system and do not need a support contract.
Pricing: Open source with no commercial tier. Cost is infrastructure and operational time only.
Concourse

Concourse takes a stricter position than anything else here: every step runs in a container, no state carries between builds unless it is declared as a resource, and pipelines are explicit graphs of resources and jobs. That strictness produces genuinely reproducible pipelines and a learning curve that teams either respect or resent.
Pros
- Enforced statelessness eliminates the entire class of failures caused by leftover state on a runner
- The resource abstraction makes external inputs and outputs explicit rather than implicit in scripts
- Pipeline visualisation is the clearest in the category for understanding a complex dependency graph
- Strong fit for release engineering where reproducibility outranks convenience
Cons
- The conceptual model is unlike every other CI system, and the learning curve is steep enough to affect adoption across teams
- Enforced statelessness means caching must be modelled explicitly, which is more work than a cache directive
- Community momentum has slowed relative to the Kubernetes-native tools, which matters for a long-term platform choice
Best for: Release engineering teams who need pipelines to be reproducible by construction and will accept a distinctive model to get it.
Pricing: Open source with no licence cost. Infrastructure and operational time are the only expenses.
GitLab

Self-managed GitLab is the heavyweight option: source hosting, CI, container registry, environments and security scanning as one installation. The relevant property for this article is that the self-managed edition is the same product as the hosted one, so you are not running a reduced legacy build. The relevant cost is that you are now operating a large application, not a CI tool.
Pros
- One installation covers source, registry, CI and environments, which removes several integration points
- Runner autoscaling is a well-trodden path with documented executors for cloud instances and Kubernetes
- Self-managed is a first-class deployment mode with real upgrade tooling and documentation
- The same pipeline definitions work if you later move to the hosted service, so the decision is reversible
Cons
- Substantially larger operational footprint than a dedicated CI tool, including database, object storage and background job infrastructure
- Upgrades are coordinated events across several components rather than a binary swap
- Feature tiering applies to the self-managed edition too, so some capabilities still require a paid licence
Best for: Organisations that want the entire toolchain inside their boundary and have a platform team able to operate a large stateful application.
Pricing: Free self-managed edition with per-seat licensed tiers for advanced security, compliance and management capabilities.
Jenkins

Jenkins is self-hosted by definition and remains the most deployed CI system in existence. Its advantages in this category are coverage and control: it runs anywhere, integrates with anything through plugins, and has no vendor relationship. Its costs are equally well documented, and they are concentrated in exactly the two areas this article is about.
Pros
- Plugin coverage for toolchains and hardware no modern platform will support, including genuinely obscure ones
- Runs in fully disconnected environments with no external dependency of any kind
- Enormous body of existing knowledge, examples and internal expertise in most established organisations
- Agent model supports almost any compute substrate, from static machines to Kubernetes pods
Cons
- Plugin version compatibility is the dominant maintenance cost and the most common cause of unplanned outages
- The controller is stateful and effectively a single point of failure, with genuinely involved high-availability options
- Groovy pipeline code accumulates into an unowned codebase that resists both refactoring and testing
Best for: Organisations with an existing estate, unusual toolchains, or disconnected environments, where the realistic plan is managed reduction rather than replacement.
Pricing: Open source with no licence fee. Cost is infrastructure plus the operational time that dominates this category.
Tekton
Tekton is CI expressed as Kubernetes custom resources. Tasks and pipelines are objects in the cluster, execution happens in pods, and the whole thing is designed as a building block for platform teams rather than a finished product for developers. That framing is important: adopting Tekton usually means building the developer-facing layer yourself.
Pros
- Native Kubernetes scheduling means autoscaling is your existing cluster autoscaler rather than a separate system
- Composable task and pipeline resources are designed to be assembled into a platform, not used directly
- Isolation per step is inherited from the pod model rather than implemented by the CI tool
- Vendor-neutral foundation governance, which lowers the long-term stewardship risk
Cons
- It is a toolkit rather than a product, and the developer experience is whatever you build on top
- Operating it well requires real Kubernetes expertise, which relocates the operational burden rather than removing it
- Debugging failed runs means reading pod logs and custom resource status, which is unfriendly for application teams
Best for: Platform teams already operating Kubernetes who intend to build an internal developer platform and want CI as a component of it.
Pricing: Open source with no licence cost. Expense is cluster capacity plus the platform engineering to make it usable.
Argo Workflows
Argo Workflows is a general Kubernetes workflow engine that is frequently used for CI, and its strengths reflect that generality. Each step is a pod, complex directed graphs are natural, and it handles long-running and fan-out-heavy work that a CI-shaped tool would struggle with. It pairs naturally with the GitOps side of the same family.
Pros
- Expressive DAG and step model handles very large fan-out and long-running jobs comfortably
- Pods as the execution unit means resource requests, node selection and scaling are standard Kubernetes concerns
- Pairs cleanly with GitOps delivery tooling from the same ecosystem for an end-to-end Kubernetes-native path
- Artifact handling between steps via object storage is built in rather than improvised
Cons
- Not a CI product: source integration, pull request triggers and status reporting need additional components
- Workflow definitions are verbose, and expressing a simple build takes noticeably more YAML than a purpose-built CI tool
- Assumes Kubernetes fluency from anyone debugging a failure, which is a poor fit for application teams
Best for: Teams already invested in Kubernetes-native delivery who need heavy fan-out or long-running pipelines and will assemble the source integration themselves.
Pricing: Open source with no licence cost. Expense is cluster capacity and operational effort.
Forgejo Actions
Forgejo is a community-governed fork of a lightweight self-hosted source platform, and Forgejo Actions is its built-in CI, deliberately compatible with the workflow syntax most engineers already know. The pitch is a small, fully self-hosted source-and-CI system that does not require you to learn a new pipeline language.
Pros
- Familiar workflow syntax means existing knowledge and many published workflow files transfer directly
- Source hosting and CI in one lightweight service with modest resource requirements
- Community governed under a permissive licence, with no commercial tier holding back capabilities
- Runner model is straightforward to operate, with far less to maintain than a plugin-based system
Cons
- Compatibility with the larger platform’s action ecosystem is partial, and third-party actions that assume the original host can fail in non-obvious ways
- Smaller project with a smaller contributor base, so unusual requirements land on you
- Lacks the advanced orchestration and governance features of the heavier platforms, which limits it at organisational scale
Best for: Small to mid-size teams wanting a complete self-hosted source and CI stack with a familiar workflow syntax and a genuinely small operational footprint.
Pricing: Open source with no commercial tier. Cost is infrastructure and operational time only.
How to choose
Start from the substrate, not the tool. If you already run Kubernetes and have the expertise, the Kubernetes-native options inherit your existing scaling and isolation. If you do not, adopting Kubernetes to run CI is a much larger project than the CI project.
Decide how much system you want to own. There is a real spectrum here, from a single binary and a database up to a multi-component application with object storage and background workers. Pick the smallest thing that meets the requirement, because every component is a recurring maintenance item.
Model the autoscaling design before choosing. Warm pool size, scale-to-zero policy, cache location and runner lifetime are the decisions that determine both cost and developer experience. They are also mostly independent of which tool you pick, which is a good reason to settle them first.
Be honest about support requirements. If your organisation requires a vendor contract for production systems, several options here are eliminated regardless of technical fit.
| Tool | Architecture | Autoscaling path | Operational weight | Fits when |
|---|---|---|---|---|
| Drone | Server plus agents | Cloud instances or Kubernetes | Low | You want container-native CI with few moving parts |
| Woodpecker CI | Server plus agents | Cloud instances or Kubernetes | Low | Fully open source with no licence question |
| Concourse | Web, database and workers | Worker pool scaling | Medium | Reproducibility by construction matters most |
| GitLab | Full application | Runner executors, well documented | High | The whole toolchain must live in your boundary |
| Jenkins | Controller plus agents | Agent plugins, many substrates | High | Legacy toolchains or disconnected environments |
| Tekton | Kubernetes custom resources | Cluster autoscaler | Medium, plus platform work | You are building an internal developer platform |
| Argo Workflows | Kubernetes workflow engine | Cluster autoscaler | Medium, plus integration work | Heavy fan-out or long-running pipelines |
| Forgejo Actions | Source platform with built-in CI | Runner instances | Low | Small complete self-hosted stack with familiar syntax |
Needs first-hand data: Measure the same job on a warm static runner, on a cold ephemeral runner with a remote cache, and on a cold ephemeral runner with no cache. Record provisioning time, image pull time, cache restore time and job time separately. That three-way split is what determines whether scale-to-zero is worth it for your workload, and it varies enormously by dependency tree size.
Frequently asked questions
Is self-hosting actually cheaper?
Per build minute, clearly yes. In total, it depends entirely on how many engineering hours per month the system consumes and what those hours are worth. The break-even moves with build volume: at low volume the maintenance cost dominates and self-hosting is a poor trade, at high volume the compute saving dominates. Measure your own maintenance hours rather than assuming.
Should self-hosted runners execute pull request code from forks?
Only if the runners are ephemeral, network-restricted and hold no credentials. A persistent runner executing untrusted code is a straightforward path from a public pull request to your internal network. The safe default is to require approval before any workflow from a fork runs at all.
Can I self-host runners but keep a hosted control plane?
Yes, and for many teams it is the better trade. You get the compute economics and network isolation of self-hosting without operating the orchestration layer, its database or its upgrade cycle. Most hosted platforms support this, and it is the single most common self-hosting arrangement in practice. Best CI/CD platforms covers how that splits the bill.
How do we keep caches warm with ephemeral runners?
Externalise the cache. Remote caching backed by object storage in the same region as the runners turns a cold machine into a warm one at the cost of a download rather than a rebuild. Build caching tools covers the options, and for large repositories monorepo build tools reduce how much needs restoring in the first place.
What monitoring does a self-hosted CI system need?
Treat it as a production service: queue depth, job duration percentiles, runner pool size against demand, controller resource usage and disk headroom on artifact storage. Queue depth is the leading indicator that predicts the complaints, and it is the one most often missing. APM tools covers the instrumentation side.
Related reading
- Best CI/CD platforms — the category map and how billing models interact with pipeline shape.
- Best CI runner and compute providers — the middle ground between hosted minutes and running the fleet yourself.
- Jenkins alternatives — migration paths for an existing estate rather than a greenfield choice.
- Best CI/CD platforms for enterprise — the compliance requirements that make self-hosting mandatory.
- Best build caching tools — how to keep ephemeral runners from paying full price every time.