Skip to content
CI/CD

Self-Hosted CI/CD Platforms Compared: Jenkins, Gitea Actions, Woodpecker, and Dagger

If you run your own infrastructure, the idea of funneling every build, test, and deployment through a third-party SaaS platform can feel uncomfortable. You’re handing over your source code, your secrets, and your build logs to someone else’s servers. For many teams, whether for compliance reasons, cost control, or simply a preference for owning their […]

Sophie Martin March 7, 2026 6 min read

If you run your own infrastructure, the idea of funneling every build, test, and deployment through a third-party SaaS platform can feel uncomfortable. You’re handing over your source code, your secrets, and your build logs to someone else’s servers. For many teams, whether for compliance reasons, cost control, or simply a preference for owning their own stack, a self-hosted CI/CD pipeline is the only option that makes sense.

Self-hosted CI/CD (Continuous Integration / Continuous Delivery) means running your automation pipeline on servers you control. You decide where the data lives, how agents scale, and what the pipeline can touch. The tradeoff is operational responsibility: you maintain the platform, handle upgrades, and troubleshoot failures.

The good news is that the self-hosted landscape in 2026 is genuinely strong. This guide breaks down four of the most relevant options: Jenkins, Gitea Actions, Woodpecker CI, and Dagger. Each solves the same core problem from a very different angle.


Jenkins: The Veteran That Still Ships

Jenkins is the granddaddy of open-source CI. First released in 2005 (as Hudson), it has been the default answer to “how do we automate builds” for nearly two decades. As of early 2026, Jenkins releases weekly builds (currently in the 2.54x series) alongside an LTS (Long-Term Support) channel, with version 2.541.1 being the most recent LTS release.

Jenkins requires Java 17 or newer to run. A planned shift to Java 21 for weekly releases is underway, so factor that into your infrastructure planning.

What Makes Jenkins Powerful

The plugin ecosystem is the main reason teams still choose Jenkins. With over 1,800 plugins available, you can integrate Jenkins with nearly any tool: Kubernetes, Docker, Artifactory, Slack, Jira, SonarQube, and hundreds more. If a tool exists in the DevOps world, there is probably a Jenkins plugin for it.

Pipelines are defined as code using a Groovy-based DSL in a file called Jenkinsfile, which lives in your repository. This treats your pipeline definition like application code: versioned, reviewable, and reproducible.

// Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'mvn -B -DskipTests clean package'
            }
        }
        stage('Test') {
            steps {
                sh 'mvn test'
            }
        }
        stage('Deploy') {
            when { branch 'main' }
            steps {
                sh './deploy.sh'
            }
        }
    }
}

For managing Jenkins itself as code, the JCasC plugin (Jenkins Configuration as Code) lets you define your entire Jenkins setup in a YAML file, which can be version-controlled and applied at startup via the CASC_JENKINS_CONFIG environment variable.

The Honest Downsides

Jenkins carries significant operational weight. It is a Java application, so its memory footprint is substantial compared to newer alternatives. Plugin management is a real concern: plugins have their own release cycles, compatibility matrices, and security advisories. Keeping a Jenkins instance healthy often requires someone who specializes in it.

The UI, while functional, shows its age. And building something like a dynamic matrix build or a complex fan-out pipeline in Groovy can feel laborious compared to what newer tools offer out of the box.

Best for: Organizations with existing Jenkins investment, complex enterprise integration requirements, or teams that need that specific plugin that nothing else supports.


Woodpecker CI: Modern, Minimal, Container-Native

Woodpecker CI is a community-maintained, Apache 2.0 licensed CI system built entirely around containers. Its latest release, v3.13.0 (January 2026), reflects active, ongoing development from a passionate contributor community.

The pitch is simple: every pipeline step runs in an isolated Docker container. Your pipeline configuration lives in a YAML file inside your repository. There is no Groovy, no plugin ecosystem to manage, and no JVM to tune. In idle mode, the Woodpecker server uses roughly 100 MB of RAM and each agent uses around 30 MB.

Pipeline Configuration

Woodpecker reads its configuration from a .woodpecker/ directory in your repo. Multiple YAML files in that directory become separate, optionally parallel workflows.

# .woodpecker/ci.yaml
when:
  - event: push
    branch: main

steps:
  - name: install
    image: node:20-alpine
    commands:
      - npm ci

  - name: test
    image: node:20-alpine
    commands:
      - npm test

  - name: build
    image: node:20-alpine
    commands:
      - npm run build

Woodpecker supports conditional execution (the when block), workflow dependencies via depends_on, and a growing library of community plugins. It connects to Gitea, Forgejo, GitHub, GitLab, and Bitbucket as SCM backends.

What to Watch

The plugin ecosystem is smaller than Jenkins by a large margin. If you need a very specific integration, you may need to write it yourself using standard Docker images and shell commands, which is often fine but requires more DIY work. Woodpecker is also primarily designed for teams running a single trust domain; its multi-tenancy story is less mature than something like GitLab CI.

Best for: Small to mid-sized teams who want a simple, fast, container-native CI system without the overhead of Jenkins.


Gitea Actions: Familiar Syntax, Fully Self-Hosted

Gitea is a self-hosted Git platform (think: your own GitHub), and its integrated CI/CD system is called Gitea Actions. Gitea’s latest release is version 1.25.4 (January 22, 2026). Actions is built to be compatible with the GitHub Actions YAML syntax, which means if your team already knows GitHub Actions, the learning curve is minimal.

Gitea Actions runs via a separate process called act_runner, which you deploy alongside your Gitea instance. Runners can be scoped to the entire instance, a specific organization, or a single repository. They support Docker, Kubernetes, virtual machines, and bare shell execution environments.

Workflow Example

Because the syntax mirrors GitHub Actions, a workflow file lives at .gitea/workflows/ci.yml:

# .gitea/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run tests
        run: pytest

Gitea Actions can download and use Actions plugins from any Git hosting service, not just GitHub Marketplace, which is a useful flexibility for air-gapped environments.

The Caveats

Gitea Actions is described as stable but still maturing. Feature parity with GitHub Actions is not complete, and some more advanced workflow features (like complex reusable workflows or certain expression contexts) may not behave identically. If you are already running Gitea for Git hosting, adding Actions is a natural extension. If you are not running Gitea, standing it up just for CI adds a layer of infrastructure that other options avoid.

It is worth noting that Forgejo (a community fork of Gitea, governed by the non-profit Codeberg e.V.) also offers a compatible Actions implementation and has been diverging from Gitea with additional features like federation. If open governance matters to your organization, Forgejo is worth evaluating alongside Gitea.

Best for: Teams already using Gitea or Forgejo for source control who want an integrated, GitHub Actions-compatible CI layer without an additional system.


Dagger: Pipelines as Code (Literally)

Dagger is a different kind of tool. Rather than defining pipelines in YAML, you write them in a real programming language: Go, Python, TypeScript, PHP, Java, .NET, Elixir, or Rust. Dagger’s engine runs your pipeline steps in containers, and every operation is cached based on its inputs using content-addressed storage. Change one file and only the affected steps re-run, automatically, with no cache invalidation logic to maintain.

The key differentiator is portability. A Dagger pipeline you run on your local laptop runs identically on Jenkins, GitHub Actions, GitLab CI, or any other CI system. The CI platform becomes a thin wrapper around dagger run, rather than the thing you’re actually maintaining.

The latest releases (v0.20.x, early 2026) include native SDKs for eight languages, an interactive REPL for exploring your pipeline interactively, and built-in OpenTelemetry observability that exports traces to Jaeger, Honeycomb, or any compatible backend.

Python Pipeline Example

# ci.py
import dagger

async def main():
    async with dagger.Connection() as client:
        src = client.host().directory(".")

        result = await (
            client.container()
            .from_("python:3.12-slim")
            .with_directory("/app", src)
            .with_workdir("/app")
            .with_exec(["pip", "install", "-r", "requirements.txt"])
            .with_exec(["pytest", "--tb=short"])
            .stdout()
        )
        print(result)

You run this locally with dagger run python ci.py. Your CI system runs the exact same command. No YAML, no environment drift.

The Tradeoffs

Dagger is not a CI server in the traditional sense. It does not have a built-in web UI for browsing build history, managing secrets, or triggering pipelines from a dashboard. You still need something to trigger it: a CI server, a cron job, a webhook listener. Think of Dagger as the execution layer for your pipeline logic, not the orchestration layer. It pairs well with any of the other tools in this comparison.

Best for: Teams who want portable, testable pipeline logic and are willing to invest in writing pipeline code rather than YAML. Particularly well-suited for platform engineering teams building shared CI infrastructure.


Comparison Table

Tool Best For Pricing Open Source? Key Strength
Jenkins Enterprise, complex integrations Free (self-hosted infra costs apply) Yes (MIT) 1,800+ plugins, massive ecosystem
Woodpecker CI Small/mid teams, lightweight setup Free Yes (Apache 2.0) Container-native, low resource footprint
Gitea Actions Teams already on Gitea/Forgejo Free Yes (MIT / GPL for Forgejo) GitHub Actions syntax compatibility
Forgejo Actions Open governance, federation roadmap Free Yes (GPL-3.0) Community-governed, pairs with Forgejo
Dagger Platform teams, portable pipelines Free (Dagger Cloud tier available) Yes (Apache 2.0) Language-native pipelines, content-addressed caching

Which Tool Should You Choose?

Choose Jenkins if you are inheriting an existing Jenkins setup, if you need a specific plugin that no other tool provides, or if your organization has complex, multi-team CI requirements that benefit from Jenkins’ mature RBAC and distributed build infrastructure. Budget for someone to own it operationally.

Choose Woodpecker CI if you want a clean, modern CI system that gets out of your way. It is especially compelling for small teams running their own servers who do not want to manage a JVM-based application. The YAML pipeline format is approachable, and its low memory usage makes it easy to co-locate with other services on modest hardware.

Choose Gitea Actions (or Forgejo Actions) if you are already running Gitea or Forgejo for code hosting. Getting CI/CD “for free” as part of your existing Git platform is a compelling value proposition. The GitHub Actions syntax compatibility also makes it easy to onboard developers who already know that ecosystem. If open governance and a non-profit steward matter to your organization, lean toward Forgejo.

Choose Dagger if your team writes a lot of code and finds YAML-based CI brittle and hard to test. Dagger shines when a platform engineering team is building a shared pipeline library that product teams consume. It also solves the “it works in CI but not locally” problem at a fundamental level, since your local environment and CI environment run the exact same engine.

For most greenfield self-hosted deployments today, Woodpecker CI or Gitea Actions will cover the majority of use cases with the least operational burden. Jenkins remains the right answer for large, complex environments where its depth is genuinely needed. And Dagger is the most forward-looking option for teams who want to treat their CI pipeline with the same engineering rigor as their application code.


πŸ” Secure your CI/CD pipelines: The free GitHub Actions Security Checker scans workflows for supply chain risks, missing permissions blocks, and hardcoded secrets. No install β€” paste your YAML and get findings in seconds.

πŸ› οΈ Try These Free Tools

⚠️ K8s Manifest Deprecation Checker

Paste your Kubernetes YAML to detect deprecated APIs before upgrading.

πŸ“¦ Dependency EOL Scanner

Paste your dependency file to check for end-of-life packages.

πŸ—ΊοΈ Upgrade Path Planner

Plan your upgrade path with breaking change warnings and step-by-step guidance.

See all free tools β†’

Stay Updated

Get the best releases delivered monthly. No spam, unsubscribe anytime.

By subscribing you agree to our Privacy Policy.