Self-hosted CI in 2026: revisit Jenkins, Woodpecker CI and Tekton before you add another build agent
Most teams that keep builds on their own servers did not choose their CI engine recently. It was chosen years ago, by someone who may have left, on a machine that has been upgraded “when there was time”. Builds are slow, so the usual answer is one more build agent. Sometimes that is the right answer. Often it hides a different problem: a controller doing too much, a plug-in list nobody has reviewed, secrets that every job can read, or pipelines that only one person understands.
This post compares three open-source CI engines that teams run on their own infrastructure in 2026: Jenkins, Woodpecker CI and Tekton. They come from three different eras and follow three different designs. Jenkins is a controller with agents and a huge plug-in ecosystem. Woodpecker is a small server with agents that runs YAML pipelines in containers. Tekton is a set of Kubernetes custom resources that you assemble into a CI system. We look at where each one stands on 26 October 2026, how they differ in licence, governance, security and operations, and how to plan a move, or decide not to move, without breaking delivery.
Two earlier posts sit close to this one. We compared self-hosted git forges (Gitea, Forgejo and GitLab CE), and the built-in CI of those forges comes up only briefly here. We also looked at secret scanning with Gitleaks, TruffleHog and detect-secrets. The CI engine is where many of those secrets finally get used, so the two topics meet.
A scenario to keep us honest
Take an invented Indore company that builds distribution and billing software for pharmaceutical stockists in Madhya Pradesh and Chhattisgarh. About 30 engineers. Java services, a React front end, a few Python reporting jobs and one old .NET Framework module that still runs on Windows for a large customer. Code lives on a self-hosted Forgejo instance.
Their CI looks like this:
- One Jenkins controller on a single virtual machine, set up in 2018. It has been upgraded from time to time, but not on a fixed schedule.
- The controller itself has four executors, and most jobs run there. Two extra Linux agents were added over the years when builds became slow. One Windows agent builds the .NET module.
- About 140 plug-ins are installed. Nobody is sure which ones are still used. A few are marked as no longer maintained in the plug-in manager.
- Jobs are a mix of old freestyle jobs configured in the web UI and newer Jenkinsfiles. A Groovy shared library written by a former employee does the deployment steps.
- Credentials for the container registry, the staging database and two customer SFTP servers live in the Jenkins credentials store, available to every job.
In the last quarter, three things happened. A month-end release was delayed because a plug-in update broke the shared library. The controller ran out of disk because build logs and artefacts had never been cleaned up. And during a review, an engineer noticed that a pull request job from a contractor’s branch could read the production SFTP credentials, because they were global.
The team’s first instinct was to add a fourth agent and a bigger disk. The question this post tries to answer is whether that is enough, whether Jenkins should be cleaned up and kept, or whether it is time to move to Woodpecker or Tekton.
The short version
- Jenkins (MIT, a graduated project of the CD Foundation) is the most flexible and most widely known of the three. It has a weekly release line and an LTS line with a new baseline every 12 weeks. Its strength and its main risk are the same thing: more than 2,300 community plug-ins, each with its own maintainers and security history. Run well, with zero executors on the controller, configuration as code and a small reviewed plug-in list, it is still a sound choice.
- Woodpecker CI (Apache 2.0, community-led, forked from Drone 0.8 in 2019) is a small Go server and agent that runs each pipeline step in a container defined in YAML. It is simple to run, light on memory, connects to Forgejo, Gitea, GitHub, GitLab and Bitbucket, and is the main CI at Codeberg. It suits small and medium teams whose builds already run in containers.
- Tekton (Apache 2.0, a graduated project of the CD Foundation) is not a ready product but a set of Kubernetes building blocks: Tasks, Pipelines and their runs as custom resources, plus Triggers, Chains, a Dashboard and a CLI. It suits platform teams that already run Kubernetes well and want CI that is native to it, with signed provenance through Tekton Chains.
- For the Indore team, the plan in this post is: first make Jenkins safe and boring (controller isolation, LTS, scoped credentials, plug-in pruning), then move container-based services to Woodpecker in stages, and keep a small Jenkins only for the Windows module until that module is retired. Tekton is not chosen because the team does not run Kubernetes for builds today.
- Whatever you choose, the CI engine holds your deployment credentials and runs code from every branch. All three projects published security fixes in 2026. Treat CI as production.
Where things stand on 26 October 2026
All times below are converted to IST.
Jenkins
- Latest weekly release: 2.587. The Jenkins changelog dates it 18 October 2026, and the GitHub release was published on 20 October 2026 at about 5:52 PM IST. Weekly releases come out roughly every week.
- Latest LTS release: 2.580.1, published on 30 September 2026 at about 4:54 PM IST. A release candidate for 2.580.2 was published on GitHub on 15 October 2026. Earlier LTS lines this year were 2.541.x, 2.555.x and 2.568.x.
- LTS policy, from the Jenkins LTS page: every 12 weeks the community picks a recent weekly release as the baseline for a new LTS line, and three updates for each baseline follow in four-week cycles.
- Java: since 2.555.1 (15 April 2026), Jenkins needs Java 21 or Java 25. Java 17 is no longer supported.
- Notable in the 2.580 line: Jenkins stopped bundling a set of older plug-ins (for example JUnit, Mailer, Matrix Authorization Strategy and Matrix Project), so very old installations that relied on them being installed automatically must now install them on purpose. The line also prohibits deserialising
Objectfields by default, a hardening step. - Security in 2026: the Jenkins security advisory index lists advisories on 18 February, 18 March, 29 April, 27 May, 10 June, 24 June, 5 August, 2 September and 16 September 2026. Five of these affected Jenkins core. The 2 September advisory alone covered core issues such as unsafe deserialisation and session fixation, and issues in more than fifteen plug-ins, including Script Security, LDAP, SAML and the GitLab plug-in.
- Licence: MIT. Governance: an independent open-source project hosted by the CD Foundation (part of the Linux Foundation), with an elected five-person governance board and officers. Jenkins was the first project to graduate at the CD Foundation. The core repository on GitHub dates from November 2010.
Woodpecker CI
- Latest release: 3.19.0, published on 7 October 2026 at about 11:34 PM IST. It includes a security fix that prevents matrix environment variables from being injected into the default clone step, server-side enforced agent labels, default user namespace support and secret masking in the CLI
execcommand. - Previous releases: 3.18.1 (9 September 2026), 3.18.0 (24 August 2026, with a log storage migration that the release notes say can take a while, so back up and plan downtime) and 3.17.0 (31 July 2026).
- Release policy, from the project’s versions page: a new version is planned every four weeks; only the latest stable release receives bug fixes by default; older minor versions might receive security patches; breaking changes are held back for major versions, with a release candidate about a week before. Woodpecker 3.0.0 came out on 11 January 2025.
- Security in 2026: two advisories rated high. One, published on 4 June 2026 IST, allowed an authenticated agent to impersonate another agent through gRPC metadata (fixed in 3.14.1). The other, published on 2 July 2026 IST, let any user with push access choose an arbitrary Kubernetes ServiceAccount for pipeline pods through
backend_options(patched version 3.16.0). - Licence: Apache 2.0. Governance: a community project without a foundation, funded through Open Collective and GitHub Sponsors, with an OpenSSF Best Practices badge. The repository dates from April 2019.
Tekton
- Tekton Pipelines latest release: v1.17.1, published on 23 October 2026 at about 5:33 PM IST, a Go toolchain update for a CVE. On the same day the project shipped patch releases for its LTS lines: v1.15.5, v1.12.3, v1.9.9 and v1.6.8.
- v1.17.0 (1 October 2026) added tracing attributes for reconciles and an alpha flag to surface Pod failure reasons such as
FailedMounton the TaskRun status, and fixed a Windows script-injection defence gap. - Release policy, from the project’s releases document: a monthly release, with four releases a year chosen as LTS and supported for about a year; other releases are supported for about a month. According to that document, v1.17 reaches end of life on 31 October 2026, v1.6 LTS also on 31 October 2026, and the newest LTS line, v1.15, is supported until 31 July 2027.
- Other components as of today: Triggers v0.37.2 (22 October 2026), Chains v0.29.9 (22 October 2026), Dashboard v0.73.0 (5 October 2026) and the
tknCLI v0.46.2 (23 October 2026). - Security in 2026: several advisories in the git and HTTP resolvers, including a critical path traversal in the git resolver published on 18 March 2026 IST and a high-rated git argument injection leading to remote code execution published on 21 April 2026 IST, both fixed across the supported lines.
- Release artefacts are signed through Tekton Chains with Sigstore, and each release note carries a Rekor entry you can use to check that the images match the release file.
- Licence: Apache 2.0. Governance: CD Foundation graduated project since October 2022, with its own governing board. The repository dates from August 2018.
Nearby alternatives, briefly
- Forgejo Actions and Gitea Actions run GitHub Actions-style workflows on runners you host, built into the forge. GitLab CI with self-managed GitLab Runners does the same inside GitLab. If you already run one of these forges, its built-in CI is a fair option and deserves a pilot of its own. This post does not repeat the forge comparison.
- Drone, the project Woodpecker forked from, is now run by Harness. Woodpecker’s own documentation describes Drone as published under the Polyform Small Business licence. Check the current terms yourself if you are considering it.
First question: what is actually wrong?
Before comparing engines, write down which of these problems you really have. They lead to different answers.
- Builds wait in a queue. Not enough executors at peak time. This is a capacity problem, and more agents, or agents that scale on demand, do fix it.
- Builds are slow even when nothing is waiting. Usually missing caches for Maven, npm or container layers, or tests that run serially. A new engine will not fix this by itself.
- The CI server is fragile. Upgrades break things, disk fills up, nobody wants to restart it. This is an operations and configuration problem. It can be fixed in Jenkins, and it can also be a reason to move to something with fewer moving parts.
- Security and secrets. Every job can read every credential, pull requests from forks run with secrets, builds run as root on the controller. This must be fixed in any engine, and the engine you choose changes how easy it is.
- Pipelines nobody understands. A shared library or a set of UI jobs that only one person can change. A move can help, because it forces you to rewrite, but the rewrite is the real work.
For the Indore team, the honest list was: fragile server, secrets that are too broad, and pipelines nobody understands. Capacity was a smaller issue than it first looked, because builds ran on the controller and competed with the UI.
What each engine is
Jenkins
Jenkins is a Java web application. The controller stores configuration, job definitions, build history and logs as files under JENKINS_HOME, serves the UI and API, and schedules builds. Agents connect to the controller over SSH or as inbound agents over WebSocket or TCP and run the actual work in executors. The controller also has a built-in node with executors of its own, and the Jenkins documentation advises against running builds there, because a build on the controller can read and change Jenkins’s own files.
Pipelines are written in a Jenkinsfile in Declarative or Scripted syntax, both based on Groovy and run through the Pipeline plug-ins. Untrusted pipeline code runs in the Script Security sandbox; shared libraries configured by an administrator can run outside it. Almost everything else is a plug-in: git, credentials, Kubernetes agents, authentication, notifications. The Configuration as Code plug-in lets you describe the controller’s settings in YAML, and the Kubernetes plug-in starts a pod per build as an agent.
Woodpecker CI
Woodpecker has a server and one or more agents, plus an optional autoscaler that starts and stops cloud virtual machines for pending work. The server receives webhooks from your forge, reads the pipeline YAML from the repository (.woodpecker/*.yaml or .woodpecker.yaml by default), stores state in SQLite by default or PostgreSQL or MariaDB for larger setups, and serves the UI and API. Agents connect to the server over gRPC and run each step through a backend: Docker, Kubernetes, or local.
Each step names a container image and a list of commands. Plug-ins are not code loaded into the server; they are container images that read their settings from environment variables. That design is the biggest difference from Jenkins: a broken plug-in breaks a step, not the server. The project’s README says the server needs around 100 MB of RAM and an agent around 30 MB when idle. That is the project’s own figure for an idle instance, not a sizing guide for real builds.
Permissions follow the forge: a user with admin rights on the repository in the forge gets admin rights on it in Woodpecker. Repository settings include approvals for pipelines (approval for forked repositories is the default), a “trusted” flag that only server admins can set and that allows privileged features such as volume mounts, and an allowlist for clone plug-ins that receive git credentials.
Tekton
Tekton is a set of Kubernetes controllers and custom resource definitions. A Step is a container. A Task is an ordered list of steps that run in one Pod and share a workspace. A Pipeline connects Tasks into a graph. TaskRun and PipelineRun objects are the actual executions. Workspaces are backed by volumes, and resolvers fetch Task and Pipeline definitions from git, OCI bundles, the Tekton catalogue or HTTP.
The other pieces are separate projects. Triggers turns webhooks into PipelineRuns. Chains watches completed runs and signs their results and provenance. Dashboard is a web UI. Results stores long-term run history outside etcd. tkn is the CLI. You assemble these into a CI system, or you use a product built on Tekton, such as Red Hat OpenShift Pipelines.
Side by side
Architecture
- Jenkins: a stateful controller with files on disk, agents that connect to it, and plug-ins loaded into the controller’s JVM. High availability of the open-source controller is not built in; most teams run one controller per group and back up
JENKINS_HOME. - Woodpecker: a stateless server with a database, agents that pull work over gRPC, and steps as containers. The server is small and easy to restart.
- Tekton: controllers inside Kubernetes. State lives in Kubernetes objects and, if installed, Tekton Results. Every run is Pods, so the cluster is your build farm.
Pipeline definition
- Jenkins: Groovy-based
Jenkinsfile, very powerful, with loops, functions and shared libraries. Easy to write clever pipelines that are hard to read later. - Woodpecker: YAML with steps, services,
whenconditions, matrix builds and dependencies between workflows. Less powerful, which is often a benefit. - Tekton: Kubernetes YAML for Tasks and Pipelines, with parameters, results and workspaces. Verbose, but explicit and reusable. Most teams write a small library of Tasks and keep Pipelines short.
Plug-ins and supply-chain risk
- Jenkins: more than 2,300 plug-ins, each with its own release cycle and maintainer. Plug-ins run inside the controller with full access, so a vulnerable plug-in is a vulnerable controller. The Jenkins security team publishes advisories regularly, and the plug-in manager shows warnings, but deciding what to install is your job.
- Woodpecker: plug-ins are container images. The risk moves from “code inside the server” to “an image that runs with the secrets you give it”. Pin plug-in images by tag or digest, restrict secrets to named plug-in images, and prefer the project’s own plug-ins for sensitive steps.
- Tekton: Tasks from a catalogue or a git repository are YAML that refers to images. Resolvers fetch them at run time, which is convenient and, as the 2026 advisories showed, an attack surface of its own. Pin Tasks to a commit or a bundle digest, and use trusted resources (signed Tasks and Pipelines) if your version supports what you need.
Secrets
- Jenkins: the Credentials plug-in stores secrets encrypted on the controller, at system and global level or in folder-level stores. Credentials Binding injects them into a build and masks them in the log. A global credential is visible to every job, which is where the Indore problem came from. External stores such as HashiCorp Vault are available through plug-ins.
- Woodpecker: repository, organisation and global secrets, injected with
from_secret. By default secrets are not exposed to pull request events. A secret can be limited to events and to a list of plug-in images. The documentation warns that only secrets from Woodpecker’s own store are masked; secrets fetched from an external provider inside a step are not. - Tekton: Kubernetes Secrets, mounted into steps through workspaces or environment variables, and credentials attached to the ServiceAccount used by the run. Anyone who can create a TaskRun in a namespace can use that namespace’s ServiceAccounts and Secrets, so namespace and RBAC design is the real secret boundary.
Scaling
- Jenkins: add static agents, or use cloud plug-ins (Kubernetes, EC2 and others) to start agents on demand. The controller itself scales up, not out.
- Woodpecker: add agents and tune the number of parallel workflows per agent; use the autoscaler for cloud virtual machines, or the Kubernetes backend to run steps as Pods.
- Tekton: scales with the cluster. You need a cluster autoscaler, storage for workspaces, and a plan for etcd and API server load when thousands of runs pile up. Tekton Results and a pruning policy keep old runs from filling etcd.
Governance, licence and support windows
- Jenkins: MIT, foundation-hosted, weekly releases plus an LTS line every 12 weeks with three updates. Commercial support is available from several companies.
- Woodpecker: Apache 2.0, community-led, a release about every four weeks, bug fixes only for the latest stable release. No foundation and no written LTS, so plan to upgrade every month or two.
- Tekton: Apache 2.0, foundation-hosted, monthly releases with four LTS lines a year supported for about a year. Several vendors build products on it.
None of the licences is a problem for internal use. The support window matters more: a small team that upgrades twice a year fits Jenkins LTS or Tekton LTS better than Woodpecker’s rolling releases, unless it commits to small monthly upgrades.
How a build actually runs
It helps to trace one build through each engine, because this is where the security model becomes clear.
- Trigger. The forge sends a webhook. Jenkins receives it through a plug-in (for example a branch source or webhook plug-in), Woodpecker receives it in the server, Tekton receives it through a Triggers EventListener.
- Read the pipeline. Jenkins reads the
Jenkinsfileand loads shared libraries. Woodpecker fetches the YAML through the forge API and checks approvals and trust. Tekton creates a PipelineRun from a TriggerTemplate, and resolvers may fetch Tasks. - Pick where to run. Jenkins matches labels to an agent with a free executor. Woodpecker queues the workflow for an agent whose labels and platform match. Tekton creates Pods and lets the Kubernetes scheduler place them.
- Inject secrets. Jenkins binds credentials the job is allowed to see. Woodpecker injects secrets allowed for that repository, event and image. Tekton mounts what the run’s ServiceAccount and workspaces allow.
- Run the steps. Jenkins runs shell commands in the agent’s workspace, or inside a container if the pipeline asks for one. Woodpecker runs each step as a container with a shared workspace. Tekton runs each step as a container in the Task’s Pod.
- Report and keep. All three report status back to the forge and keep logs. Jenkins keeps them on the controller’s disk. Woodpecker keeps them in its database or log store. Tekton keeps them in Pod logs and, if installed, in Results.
Step 4 is the one to study. In the Indore setup, the answer to “which secrets can this build see?” was “all of them”. In any engine, the answer should be “only the ones this job needs, and none for untrusted events”.
A worked plan for the Indore team
Step 1: make the current Jenkins safe before anything else
A migration takes months. The risk exists today. So start with changes that need no new tool:
- Set the built-in node’s executors to zero, and move all builds to agents. Add one more Linux agent if needed; this is the only extra agent the plan asks for.
- Move global credentials into folders, so only the jobs that deploy to a customer can see that customer’s SFTP credentials. Stop exposing deployment credentials to pull request builds.
- Move to the LTS line (2.580.x as of today, on Java 21) and decide on an upgrade rhythm, for example within two weeks of each LTS update.
- Set build discarders on every job, and move large artefacts to an artefact repository instead of the controller’s disk.
- Back up
JENKINS_HOME, and test a restore on a spare virtual machine once.
Step 2: inventory jobs and plug-ins honestly
List every job with its last successful run date, owner and what it deploys. Archive jobs that have not run in six months. For plug-ins, start from what the remaining jobs use, compare with what is installed, and remove the rest after a test on a copy of the controller. Plug-ins that the plug-in manager marks as deprecated, unmaintained or with open security warnings go to the top of the list. Expect the 140 plug-ins to shrink a lot; the exact number is less important than knowing why each one is there.
Step 3: decide where each job goes
Sort jobs into three groups:
- Container-friendly builds (the Java services, the React front end, the Python reports). These are candidates for Woodpecker.
- The Windows .NET Framework module. Woodpecker has a local backend that can run on Windows, but the Indore team decides not to touch this build until the module is retired. It stays on a small, cleaned-up Jenkins.
- Deployment jobs in the shared library. These are rewritten as small, explicit steps, not ported line by line.
Step 4: pilot Woodpecker on one service
Install the Woodpecker server with PostgreSQL, connect it to Forgejo, and add two agents with the Docker backend on separate virtual machines. Pin the server and agent images to a version tag, because the project publishes no latest tag on purpose. Move one Java service first. A pipeline for it can stay short:
when:
- event: [push, pull_request]
branch: main
steps:
- name: test
image: maven:3-eclipse-temurin-21
commands:
- mvn -B -q verify
- name: publish-image
image: woodpeckerci/plugin-docker-buildx
settings:
repo: registry.example.internal/billing/stock-service
tags: ${CI_COMMIT_SHA:0:8}
username: ci-publisher
password:
from_secret: registry_password
when:
- event: push
Then set the registry secret so it is available only to the woodpeckerci/plugin-docker-buildx image and only for push events. A pull request from a contractor’s branch can run the tests, but it never sees the registry password. Keep the “approval for forked repositories” default, and do not mark the repository as trusted unless a step truly needs privileged features.
Step 5: move the rest in batches, with a parallel run
For each batch of services, run Jenkins and Woodpecker side by side for two weeks, compare results and build times, then switch the forge’s required status check to Woodpecker and disable the Jenkins job. Fix caching early: share the Maven repository and container layer cache through volumes on trusted agents or through a cache plug-in, otherwise the new builds will look slower than Jenkins and the team will lose trust in the move.
Step 6: plan upgrades and watch advisories
- Woodpecker: upgrade at least every second release, read the release notes for migrations (3.18.0 had a log storage migration) and subscribe to the repository’s security advisories.
- Jenkins (the small one that remains): stay on LTS and follow the security advisory feed.
- Write both in the team’s monthly operations checklist, with a named owner.
When Jenkins or Tekton is the better choice
- Keep Jenkins if you have many complex pipelines that depend on Groovy logic or specific plug-ins, builds for Windows, macOS or special hardware that do not fit containers, or a team that knows Jenkins well and is willing to run it with discipline. Jenkins is not obsolete. An unmanaged Jenkins is the problem, and a managed one is a reasonable platform.
- Choose Tekton if your organisation already runs Kubernetes as its main platform, has a platform team that can own controllers, CRDs, storage and RBAC, and wants CI that uses the same tools as the rest of the cluster. Its signed releases and Chains provenance are strong for supply-chain requirements. It is also a good base if you want to build an internal CI product for many teams.
- Choose Woodpecker for small and medium teams with container-based builds who want a simple server, YAML pipelines and plug-ins that cannot break the server.
When not to change at all
- The problem is caching, not the engine. If builds are slow because each one downloads the internet, fix caches first.
- Nobody will own the new system. A new engine without an owner becomes the next ageing Jenkins box.
- You are about to change forge. If you are moving to Forgejo, Gitea or GitLab, try their built-in CI with your own runners before adding a separate engine.
- Your Jenkins is already in good shape. Zero executors on the controller, configuration as code, a short plug-in list, LTS upgrades on time. Then a move mostly costs effort.
A short history, so the choices make sense
Hudson was started by Kohsuke Kawaguchi at Sun Microsystems in the mid-2000s and became the popular open-source CI server for Java teams. After Oracle acquired Sun, a dispute over the project’s name and control led the community to vote in early 2011 to continue as Jenkins, while Oracle kept the Hudson name. Jenkins grew through plug-ins, and Pipeline as code with the Jenkinsfile became the centre of Jenkins 2 in 2016. In 2019 Jenkins became one of the founding projects of the Continuous Delivery Foundation.
Drone, a container-native CI system written in Go, became popular in the mid-2010s for its simple YAML pipelines. When Drone’s licence changed after version 0.8, a community fork was started in April 2019 from the last version under Apache 2.0, and it was renamed Woodpecker in August 2019. Woodpecker reached 1.0 in July 2023, 2.0 in December 2023 and 3.0 in January 2025.
Tekton began inside the Knative project as Knative Build and later Knative Pipeline. It was spun off in August 2018 as tektoncd/pipeline, donated to the new CD Foundation in March 2019 as one of its founding projects, set up an LTS policy and graduated in October 2022.
The three histories explain the designs. Jenkins comes from the era of long-lived build servers with plug-ins. Woodpecker comes from the era of “every step is a container”. Tekton comes from the era of “everything is a Kubernetes resource”.
Operational costs to plan for
- Jenkins: JVM memory tuning, disk for
JENKINS_HOME, plug-in testing before every upgrade, backups, and the time to read security advisories. The controller is a pet; plan for it. - Woodpecker: a database to back up, frequent small upgrades, agent hosts with container runtimes, and cache design. The server is light; the agents carry the real load.
- Tekton: a Kubernetes cluster sized for bursts, storage classes for workspaces, pruning of old runs, Results for history, a dashboard or other UI, and people who can debug controllers and admission webhooks. It is the most powerful and the most expensive to own if you do not already run Kubernetes.
- All three: build caches, an artefact repository, log retention, and someone who owns CI as a product.
A decision guide
- Small or medium team, container-based builds, wants a simple self-hosted server: Woodpecker CI.
- Many complex pipelines, non-container builds, deep investment in plug-ins and Groovy: Jenkins, run on LTS with controller isolation and configuration as code.
- Kubernetes-first organisation with a platform team and supply-chain requirements: Tekton.
- Already on Forgejo, Gitea or GitLab and happy with it: pilot the forge’s built-in CI with self-hosted runners first.
- Builds slow but the engine is otherwise fine: fix caches and parallelism before changing anything.
Checklist before you add another build agent
- Zero builds on the controller or server itself.
- Every secret scoped to the jobs, events and images that need it; none for pull requests from forks.
- A written list of plug-ins or plug-in images in use, with an owner and a reason for each.
- Pinned versions for the CI engine, its plug-ins and its step images.
- A tested backup and restore of CI configuration and history.
- Log and artefact retention set, with large artefacts in an artefact repository.
- Someone subscribed to the engine’s security advisories, with an agreed time to patch.
- Caches for dependencies and container layers measured before and after.
Common mistakes
- Running builds on the Jenkins controller. A build there can read the controller’s own files, including encrypted credentials and their keys.
- Global credentials “for convenience”. Every job, including pull requests from forks, can then reach production.
- Installing a plug-in for every small need. Each plug-in is code in the controller with its own security history.
- Trusting log masking as protection. Masking hides the exact value in logs. In our drill, a base64 copy of a masked secret printed in clear. Masking is a safety net, not access control.
- Marking repositories as trusted to fix one error. In Woodpecker, trusted means privileged features. Fix the step instead.
- Letting Tekton runs pick any ServiceAccount. RBAC in the build namespace is the real boundary. Keep powerful ServiceAccounts out of it.
- Porting a shared library line by line. Rewrite deployments as small explicit steps; the old code often carries years of workarounds.
- Floating versions. Unpinned plug-ins, images or Tasks turn every build into an unplanned upgrade.
A small hands-on drill
We ran a small local drill on 26 October 2026, around 10:25 PM IST, with the official Woodpecker CLI 3.19.0 binary for Linux x86_64, downloaded from the project’s GitHub release and checked against its published checksum, on a Linux 6.12 test machine. Only the Woodpecker CLI was used. No Woodpecker server, Docker or Kubernetes backend was involved, and Jenkins and Tekton were not run. The secret used was a fake value.
First, we linted a deliberately poor pipeline: one step with privileged: true and a plain-text database password in its environment, and a second step with no image. woodpecker-cli lint failed with exit code 1 and reported the missing image (“Invalid or missing image”). It did not flag the privileged flag or the plain-text password. A clean pipeline passed with “Config is valid”.
Second, we ran a one-step pipeline with woodpecker-cli exec --local --backend-engine local, passing a fake secret with --secrets:
when:
- event: push
steps:
- name: test
image: bash
commands:
- echo "token is $API_TOKEN"
- echo "$API_TOKEN" | base64
environment:
API_TOKEN:
from_secret: api_token
What we saw:
- With the local backend, the
imagefield is used as the shell.alpine:3.22failed with “executable file not found”, andbashworked. The local backend has no isolation, which the documentation warns about clearly. - Without a
whenblock, the CLI warned: “Consider adding awhenblock with aneventfilter”. echo "token is $API_TOKEN"printedtoken is ********. The secret was masked.echo "$API_TOKEN" | base64printed the encoded value in clear. Masking only matches the exact secret value.- The whole run took about 0.3 seconds.
This is not a benchmark. It shows two practical points: a linter checks structure, not judgement, so privileged steps and plain-text secrets need human review or a policy check; and log masking is not a barrier against a step that wants to leak a secret. Only scoping secrets to trusted events and images is.
Unlearn, re-learn
Unlearn the idea that slow or fragile CI is fixed by one more build agent. Sometimes it is, but in the Indore case the real problems were builds on the controller, a plug-in list nobody owned and credentials that every job could read. Unlearn, too, the idea that a newer engine is automatically safer. Woodpecker and Tekton both published high and critical advisories in 2026, just as Jenkins did. Re-learn CI as a production system that holds your keys: builds away from the server, secrets scoped to jobs and events, plug-ins and images pinned and reviewed, upgrades on a rhythm, and an owner. With that in place, Jenkins, Woodpecker and Tekton are all workable; the choice comes down to how your builds run, whether you live on Kubernetes, and how much machinery your team is willing to own.
Sources
- Jenkins LTS changelog, weekly changelog, LTS release line, security advisories and the 2 September 2026 advisory
- Jenkins core repository and releases
- Jenkins documentation: controller isolation, Pipeline and credentials
- Woodpecker repository, releases, security advisories and versions and release policy
- Woodpecker documentation: about and history, secrets, project settings and administration overview
- Tekton Pipelines repository, releases, release and LTS policy and security advisories
- Tekton documentation, Triggers, Chains, Dashboard and CLI
- Tekton blog: Tekton graduation and history
- CD Foundation: Tekton graduation announcement
- Forgejo Actions documentation and GitLab Runner documentation
Releted Posts
Container image signing in 2026: revisit cosign, Notation and in-toto attestations before you trust another image tag
Picture a software company in Rajkot that builds production planning and job work software for foundries, forging units and engineering workshops across Saurashtra (an invented example, not a real client).
Read moreContainer registries in 2026: revisit Harbor, Zot and CNCF Distribution before you push another image
Picture a product engineering team in Bengaluru that runs about forty microservices on Kubernetes for a B2B payments product used by NBFCs across Karnataka and Maharashtra (an invented example, not a real client).
Read moreContainer sandboxing in 2026: revisit gVisor, Kata Containers and Firecracker before you run another untrusted workload
A normal container is a set of Linux processes with namespaces, cgroups, capabilities and a seccomp profile around them. That is a good packaging boundary and a reasonable resource boundary.
Read more