Skip to content

itdevsamurai/self-hosted-runner

Repository files navigation

GitHub Self-Hosted Runner

Dockerized self-hosted GitHub Actions runner, built on ghcr.io/actions/actions-runner.

Quick start (single command)

Copy the ./config.sh --url ... --token ... line from GitHub's "New self-hosted runner" page and paste it after the script:

./new-runner.sh ./config.sh --url https://github.com/owner/repo --token XXX --name my-runner

This builds the image and starts a detached container named after the runner (--name is optional; defaults to runner-<repo>).

Setup with compose

  1. Get a registration token: repo → Settings → Actions → Runners → New self-hosted runner (docs). Tokens expire after 1 hour.
  2. Copy .env.example to .env and fill in:
    • REPO_URLhttps://github.com/<owner>/<repo>
    • RUNNER_TOKEN — the registration token
    • DOCKER_GID — gid of the host's docker socket (stat -c %g /var/run/docker.sock), so the runner can use it
  3. Start:
docker compose up -d --build

Docker access (enabled by default)

Jobs can run docker build, docker push, and most other Docker CLI commands, subject to the limits below — sharing the host daemon supports some commands and not others. The image already ships the Docker CLI and buildx; both the compose file and new-runner.sh mount the host's /var/run/docker.sock and add its gid as a supplementary group so the runner user can reach it.

This shares the host daemon rather than running a second one inside the container, so:

  • Jobs get effective root on the host. A job can mount / into a container. Use this runner only for repositories with trusted contributors, never for workflows that run fork pull requests.
  • Containers started by a job are siblings, not children. docker build is unaffected, but docker run -v "$PWD:/src" resolves $PWD against the host filesystem, where the workspace does not exist, and mounts an empty directory instead of failing. Bind-mount _work at a matching host path if workflows need this.
  • Jobs cannot use container: or services:. Not supported. The runner writes each step's script into its own _work/_temp, then starts the job container with -v /home/runner/_work:/__w. The host daemon resolves that source against the host filesystem, where the path does not exist, so it mounts an empty directory and the step fails with cannot open /__w/_temp/<uuid>.sh: No such file. The injected Node used by uses: steps (/__e) is missing for the same reason. Upstream issue: actions/runner#406. Provision toolchains with setup-* actions instead; they work identically on GitHub-hosted and self-hosted runners.
  • Images and build cache land on the host and are not covered by the _work/_temp cleanup. Prune them on the host (docker system prune).
  • docker compose is not available inside jobs; the compose plugin is absent from the base image.

Disabling it

Remove the socket mount and the group in whichever entry point you use:

  • compose — delete the volumes and group_add blocks from docker-compose.yaml, and the DOCKER_GID line from .env. Leaving group_add in place with DOCKER_GID unset makes compose fail.
  • new-runner.sh — delete the -v /var/run/docker.sock... and --group-add ... lines.

Then recreate the container. Jobs that shell out to Docker will fail with Cannot connect to the Docker daemon.

Notes

  • Container restarts reuse the existing registration. Recreating the container (--force-recreate, rebuild) re-registers, which needs a fresh token in .env.
  • _work/_temp is cleaned daily inside the container. Override the interval with CLEANUP_INTERVAL_SECS in .env.
  • docker compose down stops the runner but does not deregister it; GitHub lists it as "Offline" and purges it after 14 days. Remove it manually in repo settings if it will not return.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages