{"id":26050304,"url":"https://github.com/kekyo/ga_runner","last_synced_at":"2026-05-14T01:40:12.459Z","repository":{"id":281144445,"uuid":"943725303","full_name":"kekyo/ga_runner","owner":"kekyo","description":"GitHub Actions Self-hosted immutable runner","archived":false,"fork":false,"pushed_at":"2025-03-07T08:11:08.000Z","size":144,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-07T08:28:53.999Z","etag":null,"topics":["github","immutable","podman","self-hosted-runner"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kekyo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-06T06:58:02.000Z","updated_at":"2025-03-07T08:11:11.000Z","dependencies_parsed_at":"2025-03-07T08:39:44.194Z","dependency_job_id":null,"html_url":"https://github.com/kekyo/ga_runner","commit_stats":null,"previous_names":["kekyo/ga_runner"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2Fga_runner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2Fga_runner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2Fga_runner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kekyo%2Fga_runner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kekyo","download_url":"https://codeload.github.com/kekyo/ga_runner/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242492110,"owners_count":20137575,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["github","immutable","podman","self-hosted-runner"],"created_at":"2025-03-08T02:27:52.920Z","updated_at":"2026-05-14T01:40:12.441Z","avatar_url":"https://github.com/kekyo.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Actions Self-hosted immutable runner\n\n[![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)\n\nTested Actions runner version: [2.322.0](https://github.com/actions/runner/releases) [2025/3/6]\n\n----\n\n[![Japanese language](images/Japanese.256.png)](https://github.com/kekyo/ga_runner/blob/main/README_ja.md)\n\n## What is this?\n\nGitHub Actions self-hosted runners are convenient, but have you ever thought about running them in an immutable way?\n\nThe runners hosted by GitHub are immutable, and are destroyed each time a build is run,\nso you can assume a clean build environment.\nHowever, because self-hosted runners cannot directly mimic this behavior,\nit is quite troublesome to set up a clean build environment.\n\nThis script configures GitHub Actions self-hosted runners to run immutably.\nIt is very easy to use, and the prepared runner instance is reset each time a job is executed, which improves the reproducibility of CI/CD.\n\nThis is a good fit for people with the following issues:\n\n* You quickly use up the free quota of runners hosted by GitHub.\n* You are not satisfied with the performance of runners hosted by GitHub.\n* You want to host them on your own server machine, but it is a hassle.\n* You are troubled by the fact that you can't customize the image, even though there are too many requirements for the runner.\n\n## How it works\n\nThis script has been tested on Ubuntu 24.04 host\n(it is probably also compatible with recent Ubuntu and Debian, but this has not been tested).\nAnd the runner runs based on [Ubuntu 24.04 docker image](https://hub.docker.com/_/ubuntu/).\n\nThe script installs [`podman` (an OSS implementation compatible with Docker)](https://podman.io/)\nand builds a self-hosted runner instance on a container.\n\nWhen the runner finishes executing the job, this container also terminates,\nthe container is deleted on the spot, and the container is executed again.\nAt each startup, `ga_runner` uses your GitHub personal access token (PAT) to request\na fresh registration token and register an ephemeral runner for a single job.\n\n![Architecture](images/architecture.png)\n\n`podman` runs as the superuser, but runs as a normal user inside the container (you can also use `sudo`).\n\nThis series of actions is registered as a `systemd` service,\nso once the host OS starts up, everything is handled automatically.\n\nIn other words, as the administrator of the host machine, you don't have to do anything! ...maybe ;)\n\n## How to use it\n\nThe script has `sudo` inserted appropriately, so you can start working as a normal user.\nWe will explain the repository that wants to install the self-hosted runner as `https://github.com/kekyo/foobar`:\n\n1. Clone `ga_runner` repository on your host machine.\n   The local repository contains the scripts that `systemd` refers to.\n   Since you will need to maintain the local repository after installation, please decide on a location based on that assumption:\n   ```bash\n   $ cd \u003cstable_location\u003e\n   $ git clone https://github.com/kekyo/ga_runner\n   ```\n2. Build `podman` image (You have to run only once per the host).\n   It will be installed `curl`, `uidmap` and `podman` automatically:\n   ```bash\n   $ cd ga_runner\n   $ ./build.sh\n   ```\n3. Create a fine-grained GitHub personal access token (PAT) for the target repository.\n   Grant `Administration: Read and write` under \"Repository permissions\".\n   The token is used only to request short-lived runner registration tokens automatically.\n   Please refer to the following screenshot:\n   ![Step 1](images/step1.png)\n   ![Step 2](images/step2.png)\n4. Install runner service by:\n   `install.sh \u003cGitHub user name\u003e \u003cGitHub repository name\u003e \u003cInstance postfix\u003e \u003cGitHub PAT\u003e`. For example:\n   ```bash\n   $ ./install.sh kekyo foobar \"\" github_pat_************************\n   ```\n\nThat's all!  (\"Instance postfix\" argument is specified as an empty string. Will explain this later.)\n\nThe `systemd` service is named as `github-actions-runner_kekyo_foobar`.\nTherefore, to check the service in operation:\n\n```bash\n$ sudo systemctl status github-actions-runner_kekyo_foobar\n```\n\n## Storing configuration information\n\nThe GitHub URL, runner name, and GitHub PAT are stored in the `scripts/config/` directory.\nEach time the container starts, `ga_runner` uses the PAT to request a fresh short-lived registration token\nand registers a new ephemeral runner for a single job.\n\nIf the PAT expires, is rotated, or something goes wrong, run `install.sh` again with a new PAT.\nIf you want to start over completely, delete the service using `remove.sh` and install it again.\n\n## Installed packages on the job container\n\nThe number of packages installed in the container is minimal.\nThe list is shown below:\n\n```\nsudo, uidmap, fuse-overlays, slirp4netns, ca-certificates,\ntzdata, locales, curl, libxml2-utils, git, unzip, libicu-dev\n```\n\nSee [Dockerfile](scripts/Dockerfile) for detail.\n\nIf necessary, you can install additional packages using `apt-get` or other tools within the Actions job YAML script.\nIn other words, you can control it using only the YAML script without having to rebuild the container image.\n\nAlternatively, you can edit the `Dockerfile` directly to build and run an image with many packages pre-installed.\n\n## Install multiple runner instance\n\nYes, you can run multiple runner instance on one host OS.\nExecute `install.sh` multiple time with different user/repository name.\n\nIf you want to run multiple runner instances on the same host for the same repository, you need to specify the \"Instance postfix\" argument and run `install.sh`.\n\nFor example, to run multiple instances for the `https://github.com/kekyo/foobar` repository:\n\n```bash\n$ ./install.sh kekyo foobar \"instance1\" github_pat_************************\n$ ./install.sh kekyo foobar \"instance2\" github_pat_************************\n$ ./install.sh kekyo foobar \"instance3\" github_pat_************************\n```\n\nPlease identify them using \"Instance postfix\".\nAs a result, the service names for `systemd` will be as follows:\n\n* `github-actions-runner_kekyo_foobar_instance1`\n* `github-actions-runner_kekyo_foobar_instance2`\n* `github-actions-runner_kekyo_foobar_instance3`\n\nThese are recognized as different services.\n\n## Package caching feature\n\nEvery time the Actions Runner is started, it downloads the latest package version\n`actions-runner-linux-x64-*.tar.gz` from the official\n[GitHub Actions runner release repository](https://github.com/actions/runner/releases)\nand automatically caches it in the directory `scripts/runner-cache/`.\nIf these files are up to date, the Runner will reuse them.\n\nThe distribution files and packages for APT(Ubuntu), NPM(Node.js), .NET runtime, NuGet(.NET) and Pip(Python) are also cached.\nIf you think that the cached content is causing a problem, delete the files under `scripts/runner-cache/`.\n\nEach instance also has a cache configuration file at `scripts/config/\u003cinstance_name\u003e/config.ini`.\nYou can enable or disable each cache independently:\n\n```ini\n[cache]\nrunner_package = enabled\napt = enabled\nnpm = enabled\nnuget = enabled\ndotnet = enabled\nmaven = enabled\nhome_cache = enabled\n```\n\n`home_cache` controls `/home/runner/.cache`, which includes Pip and other user-level caches.\nIf `config.ini` is missing, or a key is omitted, the cache remains enabled to preserve the previous behavior.\nIf you run `install.sh` again, the existing `config.ini` is kept as-is.\n\n## Redirect HTTP/HTTPS to the proxy server\n\nYou may want to cache the HTTP/HTTPS access that the job performs.\nThese can be redirected to your nearest local proxy server, which will then cache them.\nThis will speed up the download of packages and content.\nOf course, you can also use it to get tunneling firewalls.\n\nThe URL to the proxy server is specified as the optional fifth argument to `install.sh`:\n\n```bash\n$ ./install.sh kekyo foobar \"\" github_pat_************************ http://proxy.example.com:3128\n```\n\nThe URL you specify must be a valid hostname that can be accessed from within the runner container.\nIn other words, please note that `localhost` cannot be used.\n\n### Using squid proxy server\n\nHere is an example configuration for the [`squid` proxy server](https://www.squid-cache.org/) that can be used for this purpose.\nThis is an example of co-locating `squid` with maximum 1000MB (files each 100MB) disk cache on the machine that hosts `podman`:\n\n```bash\n$ sudo apt install squid\n$ echo \"http_access allow localnet\" | sudo tee /etc/squid/conf.d/localnet.conf\n$ echo \"cache_dir ufs /var/spool/squid 1000 16 256\" | sudo tee /etc/squid/conf.d/cache_dir.conf\n$ echo \"maximum_object_size 100 MB\" | sudo tee -a /etc/squid/conf.d/cache_dir.conf\n$ sudo systemctl restart squid\n```\n\n`podman` can specify the host's virtual network address by using the special `host.containers.internal` FQDN, so you can specify the URL as follows:\n\n```bash\n$ ./install.sh kekyo foobar \"\" github_pat_************************ http://host.containers.internal:3128\n```\n\n## Remove the runner service\n\n`remove.sh \u003cGitHub user name\u003e \u003cGitHub repository name\u003e \u003cInstance postfix\u003e`. For example:\n\n```bash\n$ ./remove.sh kekyo foobar \"\"\n```\n\n----\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkekyo%2Fga_runner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkekyo%2Fga_runner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkekyo%2Fga_runner/lists"}