Utilizing the Kubernetes pod spec in Woodpecker CI workflows

Published: 2026-09-30

Woodpecker CI is a lightweight and powerful CI runner. And, when deployed on Kubernetes, has some great integrations available to help control workflow behavior. Here are a couple of features I found very useful!

Node selection

My Kubernetes cluster is made out of a hodgepodge collection of hardware, and some of my nodes aren’t the best suited for running certain CI tasks. With Woodpecker, I can take advantage of Kubernetes node scheduling to ensure that my workflows are running on the right hardware.

There are a few ways to do this on Woodpecker. If all workflows need to follow the same scheduling rules, setting the WOODPECKER_BACKEND_K8S_POD_AFFITNITY environment variable on each agent would be enough.

WOODPECKER_BACKEND_K8S_POD_AFFITNITY: |
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: ci
                operator: In
                values:
                  - fast

For the above example, certain nodes in my cluster are labeled as fast CI runners, and I’ve set my agents to select only these nodes during scheduling. Woodpecker can parse almost any key in the pod configuration spec, so this isn’t limited to just node affinities! Resource limits, tolerations, security contexts, etc. are all available for workflow configuration.

Woodpecker also exposes these options via a workflow step’s backend_options.kubernetes key. For security reasons, some keys in the pod configuration spec are disabled by default, and therefore need to be enabled by setting the appropriate environment variables in each agent.

WOODPECKER_BACKEND_K8S_POD_AFFINITY_ALLOW_FROM_STEP: true

This, in turn, allows defining workflow steps like so:

# .woodpecker/build.yaml
steps:
  - name: build
    image: quay.io/podman/stable
    commands:
      - podman build .
    backend_options:
      kubernetes:
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
                - matchExpressions:
                    - key: ci
                      operator: In
                      values:
                        - fast

The above configuration is very useful if only certain steps and workflows have scheduling requirements or if the agent-wide configuration needs to be overwritten!

Services on Read Write Once (RWO) storage

One common issue I’ve encountered is running background services during CI (i.e. a database server for testing). By default, services on woodpecker attach to the same storage as the workflow step and assumes Read Write Many (RWX) capabilities are available to the storage backend.

Depending oh how a Kubernetes cluster is set up RWX may not be available. And, on RWO storage, the default behavior can lead to unwanted deadlocks where the service pod will indefinitely hold a lease on the workflow’s workspace volume, preventing the other workflow pods from starting until the lease is released.

To get around this issue, the backend_options.kubernetes.workspaceVolume key can prevent a service from attaching to the workspace volume:

services:
  - name: database
    image: postgres
    environment:
      - POSTGRES_USER:
          from_secret: postgres_user
      - POSTGRES_PASSWORD:
          from_secret: postgres_password
    # this key!
    backend_options:
      kubernetes:
        workspaceVolume: false

The above configuration only works if the service doesn’t need any access to files within the workspace volume. For a database service, this is enough, but other services should be handled on a case by case basis.

There’s a lot more to this topic, and I only wanted to share what I have found most helpful to know about. If you’d like to understand more about Woodpecker’s available Kubernetes integrations, give their documentation a read here!

Do you have a cool idea? I love helping bring complex visions to life.

Say hi or follow me here: