No results found.

Infrastructure

Self-Hosted CI Runners

Docker images that run self-hosted GitHub Actions and Bitbucket Pipelines runners and scale with Docker Compose or Swarm, with no Kubernetes required.

Source on GitHub ↗Bitbucket runner on GitHub ↗Product page

GitHub runner versions published
135
Bitbucket runner versions published
41
Registries published to
2

The official ways to scale self-hosted CI runners assume Kubernetes: GitHub’s Actions Runner Controller, and Atlassian’s runners-autoscaler for Bitbucket Pipelines. I wanted runners I could run and scale on a plain Docker host, so I built one image per platform: github-actions-runner and bitbucket-pipeline-runner.

They do not autoscale. Scaling is one docker service scale away, but nothing watches the job queue and does it for you. They are not ephemeral either: a container keeps taking jobs until it is stopped.

The approach follows testdriven.io’s guide to running GitHub Actions in Docker, credited in both READMEs.

How does a runner register and clean up after itself?

Through the platform’s API, from the container’s entrypoint. On start, the container asks for a registration and registers a new runner. On SIGINT or SIGTERM it removes that runner again. The GitHub image’s entrypoint, trimmed:

REG_TOKEN=$(curl -sX POST -H "Authorization: token ${ACCESS_TOKEN}" \
  "https://api.github.com/orgs/${ORGANIZATION}/actions/runners/registration-token" | jq .token --raw-output)

./config.sh --url "https://github.com/${ORGANIZATION}" --token "${REG_TOKEN}"

trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM

The Bitbucket image wraps Atlassian’s official runner image and registers through the workspace runners API. It accepts either a workspace token or a user and API token, with the read:runner:bitbucket, write:runner:bitbucket and read:user:bitbucket scopes.

How do I run them on Docker Swarm?

Set the organisation or workspace and a token, then scale the service. The same images run under Docker Compose, Swarm or a Kubernetes Deployment. On Swarm:

services:
  github-runner:
    image: samjuk/github-actions-runner:latest
    environment:
      ORGANIZATION: acme
      ACCESS_TOKEN: ghp_xxxx
  bitbucket-runner:
    image: samjuk/bitbucket-pipelines-runner:latest
    environment:
      WORKSPACE: acme
      AUTH_TOKEN: xxxx
docker service scale ci-runners_bitbucket-runner=3

How do new runner versions get published?

Without me. A watcher runs daily at 05:30 UTC, compares the upstream releases (GitHub’s actions/runner releases, Atlassian’s runner changelog) with the tags already published and builds any version that is missing. The Bitbucket watcher skips versions known to be broken upstream, such as those with a missing base image.

Each release runs the bats tests, pushes latest and the version tag to Docker Hub and GHCR, and cuts a GitHub release. Third-party actions are pinned by commit SHA. Images are built for linux/amd64 only.

GitHub ActionsBitbucket Pipelines
BaseUbuntu 22.04 with actions/runnerAtlassian’s official runner image
Registers withOrganisation registration token from a PATWorkspace runners API
Versions published135, latest 2.338.0 (7th October 2026)41, latest 6.0.8 (29th September 2026)