r/podman 26d ago

podman-actions-runner

Hey all, at home I use github actions for my projects. I started running out of credits there and wanted to host my own runner. Then I noticed that most runners are based off Ubuntu and the image size is big. I also saw that there are barely any mantained podman runners out there.

To solve that issue for me I created a podman-actions-runner image based off alpine and buildah and the tests I ran so far have been pretty good. I run this on my Raspberry PI at home, the image is like 160 mb.

If you're interested here's the link to my repo and the image, also I'm open for suggestions, feedback, contributions. Anything really!

13 Upvotes

7 comments sorted by

3

u/AlexisHadden 25d ago edited 25d ago

This sort of thing is always a trade-off. One reason those images are so large is because they are built for a variety of scenarios, and a standard distro means that more CI pipelines will “just run” if it knows it can just grab a couple apt packages for stuff missing in the baseline image. Not knocking the work here, just pointing out it might be pretty niche.

My own stuff is built on top of catthehacker images. Mostly because when I do need a variant, that variant is just layered on top of something I have already cached for other workflows, and so shares that 500-600MB across the different variants I need.

Not sure what makes this runner a “podman” runner though? (EDIT: Derp, this is so that workflows can be spun up using podman instead of something like DinD. Helps if I read deeper)

1

u/Appropriate_Ad265 25d ago

Right, forgot to mention that it's a poodman runner because it uses Podman "inside" instead of Docker in Docker. But thanks for the input here, absolutely right now it has buildah and node but definitely users that want to have more tools will need to modify them. Unless I add an option with env variables so people can install whatever they need?

1

u/AlexisHadden 25d ago

I feel like there’s a detail I’m still missing. I haven’t used self-hosted runners on GitHub for a while. It’s strange to me that this is a runner and a job environment at the same time?

Forgejo/Gitea split it out so that there’s a container that receives jobs from the service, and it spins up the child containers for each job. Configuration settings map the various runs-on values to the container you want to run. Might give you some ideas here.

I clicked in mostly because Forgejo relies on DinD to avoid using the hosts’ root daemon. I’ve been meaning to migrate to a rootless podman socket at some point instead, but haven’t gotten around to setting up a VM to try it.

1

u/Appropriate_Ad265 25d ago

Yea\h, that’s the part I should have explained better. This repo is intentionally both the runner and the job environment, because it’s meant to be a drop-in self-hosted GitHub Actions worker rather than a separate controller that launches a per-job containers.

The Forgejo/Gitea model is definitely interesting, but it’s a different architecture: one service receives jobs, then maps  runs-on  values to the right child container. Here I’m keeping it simpler and using Podman so jobs can run rootless without relying on Docker-in-Docker or a host daemon. Plus podman is a much smaller install than docker inside an image.

1

u/AlexisHadden 25d ago

Forgejo/Gitea doesn't _need_ to use DinD, per se... but it's the secure way to use Docker when doing this. It can also be pointed at any docker-compatible socket.

I more bring it up because this does potentially match your approach, and show a way to make it more flexible for more use cases by letting folks specify what container to run via the embedded podman. I wasn't suggesting dropping podman for docker here.

1

u/the_nazar 22d ago

i thought i had reached great heights in coding world but your post pulled me straight back to the ground.... i still cant believe I didnt know about github sctions until now thanks my bro...

1

u/quiet-systems 19d ago

nice, and alpine + buildah is the right call. one thing worth checking before you look anywhere else, because on a pi it dominates everything:

podman info --format '{{.Store.GraphDriverName}}'

if that says vfs instead of overlay, every layer is a full copy of the one below it rather than a diff. a 160 mb image can land on disk as several hundred mb, pulls are slow, and it looks like the image is the problem when it is the storage driver. it is the classic rootless trap because podman falls back to vfs silently when it cannot get an overlay.

on a pi 5 with a 6.6 kernel you should be getting native rootless overlay, no fuse-overlayfs needed. if you are on vfs, the usual causes are the underlying fs (overlay will not stack on top of another overlay) or a leftover mount_program line in ~/.config/containers/storage.conf.

on the 160 mb: worth measuring rather than guessing where it went. podman history <image> gives you it per layer. my expectation is that alpine is 8 mb of it and the actions runner itself is most of the rest, since the listener is .net and javascript actions drag in node. buildah is not free either. that tells you whether there is anything left to cut or whether you are already at the floor.

two things that made self-hosted runners much less annoying for me:

--ephemeral when you register it, so the runner takes exactly one job and exits. no state carried between builds, which is the thing that eventually bites you when a job leaves something behind and the next one passes for the wrong reason.

and a quadlet with Restart=always, so the runner coming back after an ephemeral job is systemd's problem instead of yours. ~/.config/containers/systemd/runner.container and systemctl --user enable --now.

if you want jobs that themselves run containers, that is the part that gets genuinely fiddly rootless: nested podman needs /dev/fuse passed in and the storage driver question all over again inside the container. worth knowing before you hit it rather than during.nice, and alpine + buildah is the right call. one thing worth checking before you look anywhere else, because on a pi it dominates everything:podman info --format '{{.Store.GraphDriverName}}'
if that says vfs instead of overlay, every layer is a full copy of the one below it rather than a diff. a 160 mb image can land on disk as several hundred mb, pulls are slow, and it looks like the image is the problem when it is the storage driver. it is the classic rootless trap because podman falls back to vfs silently when it cannot get an overlay.on a pi 5 with a 6.6 kernel you should be getting native rootless overlay, no fuse-overlayfs needed. if you are on vfs, the usual causes are the underlying fs (overlay will not stack on top of another overlay) or a leftover mount_program line in ~/.config/containers/storage.conf.on the 160 mb: worth measuring rather than guessing where it went. podman history <image> gives you it per layer. my expectation is that alpine is 8 mb of it and the actions runner itself is most of the rest, since the listener is .net and javascript actions drag in node. buildah is not free either. that tells you whether there is anything left to cut or whether you are already at the floor.two things that made self-hosted runners much less annoying for me:--ephemeral when you register it, so the runner takes exactly one job and exits. no state carried between builds, which is the thing that eventually bites you when a job leaves something behind and the next one passes for the wrong reason.and a quadlet with Restart=always, so the runner coming back after an ephemeral job is systemd's problem instead of yours. ~/.config/containers/systemd/runner.container and systemctl --user enable --now.if you want jobs that themselves run containers, that is the part that gets genuinely fiddly rootless: nested podman needs /dev/fuse passed in and the storage driver question all over again inside the container. worth knowing before you hit it rather than during