myoung34/docker-github-actions-runner: a containerised self-hosted GitHub Actions runner
This will run the new self-hosted github actions runners with docker-in-docker
At a glance
- What is it?
- The image packages GitHub's self-hosted runner inside Docker, with Docker-in-Docker available but not fully supported by GitHub's own tooling. Here is what it installs, what it cannot do, and how it differs from running the runner binary directly.
- Who is it for?
- Adopt this image when you want self-hosted runners on hosts you already manage with Docker, and you accept that GitHub's runner does not officially support Docker from a self-hosted runner. Do not adopt it if you need containerd, or if you expect Job Services to work.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem: GitHub-hosted runners are not yours to configure
GitHub's own runners are managed machines. You get a defined image, a defined set of installed tools, and no ability to add a system package, mount a device, or keep a warm cache between jobs. For a project that needs a specific compiler version, a private network route, or hardware the hosted fleet does not offer, that is a dead end. The alternative GitHub documents is the self-hosted runner: a binary you download, register against a repository or organisation, and run as a service. It works, but the setup is manual and the machine is stateful. This project takes that binary and wraps it in a container, so the runner is something you build, tag and replace rather than something you configure in place.
The audience is teams already running Docker on their own infrastructure. If your deploy pipeline is a compose file or a Kubernetes manifest, the runner becomes one more service in it. The image is published on Docker Hub, so the adoption path is a pull rather than a compile. It is not aimed at people who want GitHub to manage the machine; that is what hosted runners are for.
How the image is assembled: base layer, runner binary, entrypoint
The repository splits into two Dockerfiles. Dockerfile.base builds the foundation, and Dockerfile starts from myoung34/github-runner-base:latest, sets AGENT_TOOLSDIRECTORY to /opt/hostedtoolcache, and creates that directory. The runner version is pinned as a build argument, GH_RUNNER_VERSION, currently 2.337.0, and install_actions.sh downloads and unpacks the runner for the target platform into /actions-runner. Ownership of /_work, /actions-runner and /opt/hostedtoolcache is handed to the runner user, which matters because the runner writes job data into the work directory.
Three scripts are copied to the filesystem root: token.sh, entrypoint.sh and app_token.sh. The image declares ENTRYPOINT ["/entrypoint.sh"] and CMD ["./bin/Runner.Listener", "run", "--startuptype", "service"]. So the container's default command is the runner listener itself, started as a service, and the entrypoint is what turns environment variables into a registered runner before that command runs. The README points to the wiki for usage rather than documenting the registration flow inline, so the entrypoint scripts are the authoritative source for how a container registers itself.
The build matrix is generated by GitHub Actions workflows that copy the Dockerfile, change the FROM line, and build. That is how the README describes the multi-distro tags: ubuntu-focal, ubuntu-resolute, ubuntu-noble, ubuntu-jammy, debian-bookworm and debian-trixie, each for x86_64 and arm64. Tags without an OS name track latest, which the README says is rebuilt nightly and on master merges. The debian-sid variant is disabled because a Docker repository index it depends on does not yet carry forky.
Installing the runner and registering it with a token
The image is on Docker Hub as myoung34/github-runner. A minimal run needs two things: a way to authenticate against GitHub, and a name for the runner. The README's environment variable table lists ACCESS_TOKEN as a GitHub PAT, RUNNER_NAME as the runner's name (which supersedes RUNNER_NAME_PREFIX), and RUN_AS_ROOT as a boolean that defaults to true. With RUN_AS_ROOT left at its default the container runs as root; setting it to any other value makes it run as the runner user and allows an optional override, while the exact value True alongside a user override is documented as an error.
The README does not publish a docker run example, so the starting point it gives is the wiki's usage page, which is where the environment variables above are meant to be applied. The relevant values are the ones from the environment variable table: ACCESS_TOKEN for a GitHub PAT, RUNNER_NAME for an explicit runner name, and RUN_AS_ROOT to choose the user the container runs as. The Dockerfile's CMD is already the listener command, so no command override is needed; the container starts the runner as a service by default.
After the container starts, the runner should appear in the repository or organisation's self-hosted runner list, and the container logs should show the listener connecting. The README does not document a rollback path for a registration, so plan on removing the runner through GitHub's own settings if the container is discarded.
RANDOM_RUNNER_SUFFIX defaults to true and produces a 13-character random string appended to RUNNER_NAME_PREFIX, which is convenient when you scale the service to several replicas and do not want name collisions. If you set the prefix to an empty string and disable the random suffix, the image falls back to the contents of /etc/hostname, or a random string if that file is missing or empty. That fallback is worth knowing about because a hostname-derived name can collide across hosts. The README does not ship a compose file; the wiki is where it directs readers for usage patterns.
Docker-in-Docker is installed but not supported by GitHub's runner
The project's own README is unusually direct here. It states that while the runner installs and allows Docker, GitHub Actions itself does not support using Docker from a self-hosted runner yet, and links to two issues in the actions/runner repository. The practical consequence is narrower than it sounds: workflows that build and push images with docker commands can work, but features that assume a Docker daemon managed by the runner will not. The README names Job Services specifically, saying they will result in an error, and links to issue 61 in this repository as the record of that.
The containerd situation is similar. The README states that runners currently do not support containerd, with a link to actions/runner issue 1265. If your cluster is containerd-only and you were hoping the image would bridge that, it will not; the limitation is upstream, not something a Dockerfile can fix.
There is also a host-version constraint in the tag table. The README notes an issue with jammy from inside a 20.04 LTS host, and says that is why jammy is not the latest tag. If your host is old, matching the base image to it is not cosmetic. The focal tag is the default latest, and the README describes the tags without an OS name as included in latest.
The security note you should read before granting workflow access
The README's security section says plainly that environment variables are not safe from exfiltration. That is not a flaw in this image; it is a property of the runner model. A container that holds an ACCESS_TOKEN in its environment is a container whose token can be read by any process that gets to run inside it. Since the whole point of a self-hosted runner is to execute workflow code, the boundary between trusted and untrusted code is the thing you have to control.
The README's own recommendation is to gate workflow changes behind a verification process in the actions settings so that malicious pull requests cannot exfiltrate the variables. That is a configuration decision in GitHub, not a flag on the container. There is a SECURITY.md in the repository for reporting issues, and the image is built from scripts (token.sh, app_token.sh) that handle credentials, so anyone auditing the setup should read those rather than assume the environment variable table is the whole story. The README does not describe a secrets-manager integration, and it does not describe token rotation.
How it differs from running the runner binary directly
The direct alternative is GitHub's own self-hosted runner installation: download the runner tarball on a machine, run config.sh with a registration token, and install it as a systemd service. That approach gives you a persistent machine with a persistent tool cache. It also gives you configuration drift, because every package you install by hand is invisible to version control, and rebuilding the machine means repeating the setup.
The container approach inverts that trade-off. The image is reproducible because it is a build artifact, and upgrading means pulling a new tag. The cost is that anything you need beyond the included software has to go into a derived image or a workflow step, and the README points to build/config.json for the list of included packages, noting the project is not perfectly 1:1 with the software upstream. If your jobs depend on a tool that is in GitHub's hosted image but not in this one, you will find out at runtime.
A third option is a managed runner service, where a vendor runs the machines for you. That removes the operational work but also removes the ability to put the runner on your own network, which is often the reason people leave hosted runners in the first place. This project sits between those: more control than hosted, more reproducibility than a hand-built VM.
Maintenance, versioning and the GPL-3.0 licence
The repository was last pushed on 2026-09-25, three days before this writing, and it is not archived. Recent releases track upstream runner versions: 2.337.0 on 2026-08-26, 2.336.0 on 2026-07-20, and 2.335.1 on 2026-06-09. The cadence suggests the project follows GitHub's runner releases rather than setting its own schedule. The Dockerfile pins GH_RUNNER_VERSION to 2.337.0, so a given image tag corresponds to a specific runner version, and the tag regexes in the README (for example /\d\.\d{3}\.\d+-ubuntu-noble/) let you pin to a distro-specific build.
Upgrade cost is mostly a tag change plus a re-registration, because the runner identity lives in the container. If you scale replicas, each one needs a distinct name, which is what RANDOM_RUNNER_SUFFIX is for. The repository also carries renovate.json, which suggests dependency updates are automated, and a .pre-commit-config.yaml for local checks.
The licence is GPL-3.0. That is a copyleft licence, and it applies to the image and the scripts in the repository. Redistributing a modified image carries obligations that permissive licences do not. This is not legal advice; if you plan to ship a derived image to customers, have someone who knows your obligations read the licence text in the repository.
Editorial conclusion
Adopt this image when you want self-hosted runners on hosts you already manage with Docker, and you accept that GitHub's runner does not officially support Docker from a self-hosted runner. Do not adopt it if you need containerd, or if you expect Job Services to work. Before rolling it out, verify the entrypoint's token handling against the security note in the README, and confirm which base tag (ubuntu-focal, ubuntu-noble, debian-bookworm) matches your host kernel.
Frequently asked questions
What does the GitHub Actions runner do?
It is the process that picks up jobs from a GitHub Actions workflow and executes them on a machine you control, instead of on GitHub's hosted fleet. This project packages that runner inside a Docker image so it can be started, named and replaced like any other container.
Can I use the GitHub Actions runner in Docker?
Yes. The image is published as myoung34/github-runner on Docker Hub, and the README's environment variable table covers the settings needed to start it, including ACCESS_TOKEN, RUNNER_NAME and RUN_AS_ROOT. The README points to the project wiki for full usage examples.
What is the GitHub Actions runner system?
It is GitHub's mechanism for running workflow jobs on self-hosted infrastructure. The runner registers against a repository or organisation, listens for jobs, and executes them; this project supplies that runner as a container image with the listener started as a service.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/myoung34-docker-github-actions-runner)