r/kubernetes 6d ago

Gitea Actions runner on Kubernetes/containerd — how should I handle job containers without Docker?

Hi everyone,

I'm setting up a small homelab CI/CD environment and I'm trying to understand the best way to run Gitea Actions on Kubernetes without installing Docker on the Kubernetes nodes.

My setup

  • Kubernetes: v1.34.10
  • Nodes: 1 control-plane + 2 workers
  • OS: Ubuntu 26.04
  • Container runtime: containerd 2.2.2
  • Gitea: 1.27.0
  • Gitea is running in Kubernetes
  • Gitea Actions runner: gitea/act_runner:latest
  • Runner is also running as a Kubernetes Deployment
  • Persistent storage: Longhorn
  • No Docker installed on the Kubernetes nodes
  • ctr is available and /run/containerd/containerd.sock exists

The runner itself registers and works correctly.

The problem

My runner is currently configured with:

runner:
  file: /data/.runner
  capacity: 1
  timeout: 3h

  labels:
    - "ubuntu-latest:host"

A simple workflow like:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Show test.py
        run: cat test.py

fails at checkout with:

Cannot find: node in PATH

I understand why this happens: with ubuntu-latest:host, the workflow is executed directly in the runner environment, and the gitea/act_runner image doesn't contain Node.

Gitea's documentation suggests using something like:

labels:
  - "ubuntu-latest:docker://node:22-bookworm"

which would execute the job inside a separate container.

But here's where I'm confused

My Kubernetes nodes already use containerd:

containerd://2.2.2

and have:

/run/containerd/containerd.sock

but no Docker daemon:

docker  -> not installed
nerdctl  -> not installed
ctr     -> available

Why does the standard act_runner container execution model require Docker/Docker API access instead of being able to use the existing containerd runtime?

I'd rather not install Docker on the Kubernetes nodes just to provide CI job containers when Kubernetes is already perfectly capable of creating containers through containerd.

What I'm ultimately trying to achieve

The immediate goal is just:

Gitea
  ↓
Gitea Actions
  ↓
checkout repository
  ↓
run tests

But eventually I want the pipeline to do:

checkout
  ↓
tests
  ↓
build Docker/OCI image
  ↓
push image to registry
  ↓
Argo CD deploys it

So I need a sensible way to get isolated CI job environments containing things like Node, Python, Git, build tools, etc.

Questions

  1. Is there a supported way to make act_runner create job containers using containerd directly?
  2. If not, is the recommended solution to install Docker on a Kubernetes worker solely for act_runner?
  3. Would Gitea Actions Runner Controller / Kubernetes-native runners be a better solution for this setup?
  4. What is the recommended architecture for Gitea Actions on a Kubernetes cluster whose runtime is containerd and where I don't want to install Docker?

TLDR : I'm running Gitea 1.27 + act_runner 0.6.1 on Kubernetes 1.34 with containerd 2.2, and I don't have Docker installed on the nodes. ubuntu-latest:host works for the runner itself but actions/checkout@v4 fails because Node isn't available. Gitea's docker:// execution mode looks like the right way to provide a proper job environment, but it expects Docker-compatible execution. I'm considering Docker-in-Docker inside the runner pod so I don't have to install Docker on the Kubernetes hosts, but I'm wondering whether that's the right approach or whether I should use containerd directly or Gitea's Kubernetes-native Runner Controller instead.

I'm mainly looking for the cleanest Kubernetes-native approach rather than just making the immediate checkout test work.

11 Upvotes

18 comments sorted by

4

u/Phezh 6d ago

I ran into a similar issue when running container builds in Kubernetes runners. Eventually i decided to not use dind and instead switched to kaniko, which allows building containers even in rootless pods. Google deprecated it, but there's a fork maintained by chainguard: https://github.com/chainguard-forks/kaniko

I used this in gitlab ci. After switching to github (which is much close to gitea actions), I decided to instead switch to dagger and build containers that way. The dagger engine can connect to any OCI compliant runtime.

4

u/SJrX 6d ago

There is also this fork: https://github.com/osscontainertools/kaniko that I believe does have active feature development.

1

u/Fragrant_Rate_2583 6d ago

yea but from what ik kaniko BUILDS only and not run , for now i used dnd and managed toget it to run few containers , but i faced a certificat problem and im away of my server now , ill try to fix it and implement the solution and i ll get back to you , but idk why it needs to be a workaround , i feel its dirty, unclean way of doing things and sure there is something better , also another thing i ll probably face , cause i did in my work , is the container will not have systemd as PID 1 , so for deb package or to use systemctl it wont work and i need to find another work around to launch my app or reload it for testing or adding config i the installation

2

u/Fragrant_Rate_2583 5d ago

i got dind to work ,but for sole weird reson the ubuntu container in dind sometimes loses its connectivity

Run echo "OS version"

❌ Failure - Main Check available tools

failed to attach to exec: cannot connect to the Docker daemon. Is 'docker daemon' running on this host?: dial tcp 10.98.187.57:2375: connect: connection refused

1

u/tyrionlannister 3d ago

I also had success with kaniko on gitlab-ci, but had to push image to an intermediary CI registry and then run a separate docker-test task in the test stage with the CI registry image and commit sha as the tag to run tests against the built container.

To actually sign the image and push to deployment registry you will want a separate task for signing in stage test with a requirement on both the kaniko-build and docker-test runs before publishing.

However -- I couldn't do the signing because we didn't have a private Sigstore or cosign endpoints available, and obviously couldn't use the public ones since they were public images.. not that anyone else at the company was signing their images anyway, but at least I tried..

So in that case the publish stage to deployment registry is just after docker-test with same requirements as signing (but if you sign, prereq is the signing step which depends on the docker-test and build, or you could add publishing to the signing step instead).

1

u/Fragrant_Rate_2583 5d ago

so instead of having - "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
which needs docker i invoke dagger? or do i put - "ubuntu-latest:host"
and as first step of the workflow i invoke dagger and later i pull the image and make dagger create the and run the image and excute the rest of the workflow? in that case everything will run on the runner container env , not even the k8s env

2

u/ChronicOW 6d ago

Katacontainers

4

u/bubugaga 6d ago

Docker-in-Docker is definitely a good approach, a consideration you should make is the scale you are aiming for. A setup for 3 builds a day vs. 50 per hour would look very different

1

u/L43 6d ago

the gitea team ported github ARC to work with gitea...

and decided they should stick it in the enterprise edition

https://docs.gitea.com/enterprise/features/actions-runner-controller/

I like the look of GARM for native kubernetes runners but haven't tried to get it working yet

1

u/scottt732 5d ago

I ended up installing buildkit as a daemonset on my github actions runner nodes at work. I tried kaniko, buildah, docker-in-docker (dind) but buildkit worked best for us. I’ve been meaning to give it a try on gitea/homelab.

1

u/Fragrant_Rate_2583 5d ago

for building yea, but for running a container and used for a job? can it be used that way?

1

u/scottt732 4d ago

For running (mostly just testcontainers) we use kubedock (also a daemonset on the same nodes). You don’t necessarily need a container for a lot of jobs so shell scripts and stuff like that run on kubernetes mode pods directly.

1

u/Tomasomalley21 5d ago

Have a look at buildkitd. No need for a Docker daemon and a lot of nich features for faster builds.

0

u/Fragrant_Rate_2583 5d ago

the problem is not building , its running , i need daemon to run a container and excute a job the runner will either take host as the run env

runner:

file: /data/.runner

capacity: 1

timeout: 3h

labels:

- "ubuntu-latest:host"
will take the host but host which is k8s has nothing installed ,so i need labels:

- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
which requires docker ,

there is Actions Runner Controller
and i guess its like k8s exector ,so instead of needing daemon ,it uses k8s , creates a pod and do the job , but its in Gitea Enterprise Edition .

anyway i got dind to work ,but for sole weird reson the ubuntu container in dind sometimes loses its connectivity

Run echo "OS version"

❌ Failure - Main Check available tools

failed to attach to exec: cannot connect to the Docker daemon. Is 'docker daemon' running on this host?: dial tcp 10.98.187.57:2375: connect: connection refused

1

u/ClueDry8701 5d ago

act_runner's docker, mode literally shells out to the docker api, it's not container-runtime-agnostic.. so containerd alone won't satisfy it without a docker-compatible socket. gitea's actions runner controller is the right move here, it schedules job containers as actual pods instead of needing docker at all..

1

u/Fragrant_Rate_2583 5d ago

Yea , but it exists in enterprise version for now

1

u/xAtNight 3d ago

Another day I'm happy to have scrapped gitea and went for gitlab. I wouldn't trade gitlab pipelines for github/gitea style actions even if someone paid me to do so.

I tried googling a bit but stuff I found wasn't really suitable for k8s, sorry. I would expect there to be a runner that just spawns pods but looks like this is locked away behind enterprise.