{"id":21044320,"url":"https://github.com/trixi-framework/ci-with-self-hosted-runners","last_synced_at":"2026-03-19T18:35:18.250Z","repository":{"id":166634016,"uuid":"642127063","full_name":"trixi-framework/ci-with-self-hosted-runners","owner":"trixi-framework","description":null,"archived":false,"fork":false,"pushed_at":"2023-06-01T18:27:08.000Z","size":25,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-06-02T02:49:00.153Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc-by-4.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trixi-framework.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,"zenodo":null}},"created_at":"2023-05-17T22:08:43.000Z","updated_at":"2023-05-28T06:29:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"92044164-e731-4ed1-ab3c-77e2934913da","html_url":"https://github.com/trixi-framework/ci-with-self-hosted-runners","commit_stats":null,"previous_names":["sloede/ci-setup-with-buildkite","trixi-framework/ci-with-self-hosted-runners"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/trixi-framework/ci-with-self-hosted-runners","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trixi-framework%2Fci-with-self-hosted-runners","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trixi-framework%2Fci-with-self-hosted-runners/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trixi-framework%2Fci-with-self-hosted-runners/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trixi-framework%2Fci-with-self-hosted-runners/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trixi-framework","download_url":"https://codeload.github.com/trixi-framework/ci-with-self-hosted-runners/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trixi-framework%2Fci-with-self-hosted-runners/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28813539,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T12:25:15.069Z","status":"ssl_error","status_checked_at":"2026-01-27T12:25:05.297Z","response_time":168,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-19T14:16:19.115Z","updated_at":"2026-01-27T13:33:39.660Z","avatar_url":"https://github.com/trixi-framework.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# CI with self-hosted runners\n\n[![License: CC BY 4.0](https://img.shields.io/badge/License-CC_BY_4.0-lightgrey.svg)](https://creativecommons.org/licenses/by/4.0/)\n\nThis project aims to curate and present information on how to set up a\ncontinuous integration (CI) infrastructure with self-hosted runners for an open\nsource project on GitHub. That is, instead of using only the GitHub-hosted\nrunners for testing, documentation building etc., we want to run additional CI\njobs on hardware that we control, either on cloud servers or on on-premise\nmachines. The focus of this effort is the open source research software\n[Trixi.jl](https://github.com/trixi-framework/Trixi.jl).\n\nThe key goal of this project is to try to balance ease of setup/maintenance with\nsecurity. That is, we are no experts in setting up online services and we do not\nwant to become one. However, since our research software is developed\ncollaboratively as an open source project, with contributions from many\ndifferent people, we require a reasonable level of security against accidental\nor deliberate abuse of the self-hosted runners.\n\nThe instructions on how to set up GitHub's own self-hosted runners can be found\ndirectly [below](#set-up-ephemeral-github-runners-with-docker-on-ubuntu).\nAdditionally, _some_ instructions for setting up\nself-hosted runners with [Buildkite](https://buildkite.com) can be found in\n[`buildkite.md`](buildkite.md). Information on how to contribute to this project\ncan be found at the [bottom](#license-and-contributing) of this file.\n\n## Set up ephemeral GitHub runners with Docker on Ubuntu\n\nThe following instructions were tested on an Ubuntu 22.04 system without any prior\nmodifications. We will perform the following steps:\n* [Install Docker](#install-docker)\n* [Enable running Docker as a non-root user (optional)](#enable-running-docker-as-a-non-root-user-optional)\n* [Create GitHub App for runner management](#create-github-app-for-runner-management)\n* [Prepare automatic GitHub token generation](#prepare-automatic-github-token-generation)\n* [Create script for service startup](#create-script-for-service-startup)\n* [Create service environment file](#create-service-environment-file)\n* [Create service definition file for systemd](#create-service-definition-file-for-systemd)\n* [Enable and control GitHub Actions runner service](#enable-and-control-github-actions-runner-service)\n* [Verify registration and update runner usage permissions](#verify-registration-and-update-runner-usage-permissions)\n* [Use a self-hosted runner](#use-a-self-hosted-runner)\n\n### Install Docker\nFollowing https://docs.docker.com/engine/install/ubuntu/\n\n1. Clean up\n   ```shell\n   sudo apt-get remove docker docker-engine docker.io containerd runc\n   ```\n2. Update `apt`\n   ```shell\n   sudo apt-get update\n   sudo apt-get install -y ca-certificates curl gnupg\n   ```\n3. Add Docker GPG keys\n   ```shell\n   sudo install -m 0755 -d /etc/apt/keyrings\n   curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg\n   sudo chmod a+r /etc/apt/keyrings/docker.gpg\n   ```\n4. Set up Docker repo\n   ```shell\n   echo \\\n     \"deb [arch=\"$(dpkg --print-architecture)\" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \\\n     \"$(. /etc/os-release \u0026\u0026 echo \"$VERSION_CODENAME\")\" stable\" | \\\n     sudo tee /etc/apt/sources.list.d/docker.list \u003e /dev/null\n   ```\n4. Install Docker\n   ```shell\n   sudo apt-get update\n   sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n   ```\n5. [optional] Verify that it works\n   ```shell\n   sudo docker run hello-world\n   ```\n\nAll commmands at once:\n```shell\nsudo apt-get remove docker docker-engine docker.io containerd runc\nsudo apt-get update\nsudo apt-get install -y ca-certificates curl gnupg\nsudo install -m 0755 -d /etc/apt/keyrings\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg\nsudo chmod a+r /etc/apt/keyrings/docker.gpg\necho \\\n    \"deb [arch=\"$(dpkg --print-architecture)\" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \\\n    \"$(. /etc/os-release \u0026\u0026 echo \"$VERSION_CODENAME\")\" stable\" | \\\n    sudo tee /etc/apt/sources.list.d/docker.list \u003e /dev/null\nsudo apt-get update\nsudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\nsudo docker run hello-world\n```\n\n### Enable running Docker as a non-root user (optional)\n\n*Note: Running Docker as non-root user is generally recommended as a means to\nmitigate potential vulnerabilities in the daemon and the container runtime.*\n\nThe following steps are, however, optional: If you are set on running Docker as\n`root`, you can just skip this entire section.\n\nFollowing https://docs.docker.com/engine/security/rootless/\n\n1. Install additional dependencies:\n   ```shell\n   sudo apt-get install -y uidmap\n   sudo apt-get install -y dbus-user-session\n   ```\n   If the second command triggers an installation, i.e., if the\n   `dbus-user-session` package had not been installed before, you need to\n   relogin.\n2. Create a non-root user `docker-rootless`:\n   ```shell\n   sudo adduser \\\n       --gecos 'Non-privileged user for rootless Docker execution' \\\n       --shell /bin/bash \\\n       --disabled-password \\\n       docker-rootless\n   ```\n3. Verify that enough subordinate UIDs/GIDs are available by running\n   ```shell\n   grep ^docker-rootless: /etc/subuid /etc/subgid\n   ```\n   which should give an output similar to\n   ```\n   /etc/subuid:docker-rootless:100000:65536\n   /etc/subgid:docker-rootless:100000:65536\n   ```\n   where the final number after the last colon whould be \u003e= 65,536.\n4. Enable use of systemd services without being logged in:\n   ```shell\n   sudo loginctl enable-linger docker-rootless\n   ```\n5. Allow non-root users to limit all resources using cgroups (by default, only\n   `memory` and `pids` are allowed):\n   ```shell\n   sudo mkdir -p /etc/systemd/system/user@.service.d\n   sudo cat \u003e /etc/systemd/system/user@.service.d/delegate.conf \u003c\u003c EOF\n   [Service]\n   Delegate=cpu cpuset io memory pids\n   EOF\n   systemctl daemon-reload\n   ```\n5. Disable system-wide Docker daemon:\n   ```shell\n   sudo systemctl disable --now docker.service docker.socket\n   ```\n6. Switch to a login shell for the `docker-rootless` user,\n   ```shell\n   sudo su - docker-rootless\n   ```\n   then add your SSH key for key-based authentication:\n   ```shell\n   mkdir $HOME/.ssh\n   echo 'YOUR_KEY_GOES_HERE' \u003e\u003e $HOME/.ssh/authorized_keys\n   # e.g., echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE1hyJ7bPlchRiH5x/2T5S/66CjWaqPSvoO2VIiLp//c hpcschlo@hlrs-hpcschlo' \u003e\u003e $HOME/.ssh/authorized_keys\n   ```\n7. Restart the system for the cgroup settings to take effect:\n   ```shell\n   reboot\n   ```\n\nNow login via SSH as `docker-rootless`. Since we disabled password logins, there\nis no other way than using the SSH key you uploaded in the previous steps. The\nnext steps are performed as `docker-rootless`.\n\n1. Install rootless Docker for user `docker-rootless`:\n   ```shell\n   dockerd-rootless-setuptool.sh install\n   ```\n2. The command will give you some hints about environment variables that need to\n   be added to your `~/.bashrc`, you can do that by running\n   ```shell\n   echo 'export PATH=/usr/bin:$PATH' \u003e\u003e ~/.bashrc\n   echo 'export DOCKER_HOST=unix:///run/user/1000/docker.sock' \u003e\u003e ~/.bashrc\n   ```\n   (but please check if the user id matches).\n3. Enable the systemd service to launch the Docker daemon at startup:\n   ```shell\n   systemctl --user enable docker\n   ```\n\n### Create GitHub App for runner management\n\nNote: This step needs to be done only once.\n\n1. Go to https://github.com/organizations/trixi-framework/settings/apps/new\n2. Enter app details:\n   * Name: **Self-hosted runner administration**\n   * Description: **App to manage self-hosted runners.**\n   * Homepage URL: https://github.com/trixi-framework\n   * Identifying and authorizing users\n     * ❌ Expire user authorization tokens\n     * Leave all other checkboxes/fields unchecked/empty as well\n   * Post installation: leave everything empty\n   * Webhook\n     * ❌ Active\n     * Leave all other checkboxes/fields unchecked/empty as well\n   * Permissions\n     * Organization permissions\n       * Self-hosted runners: read and write\n   * Where can this GitHub App be installed?\n     * 🔘 Only on this account\n3. Click \"Create GitHub App\"\n4. You will be redirected to an overview of the newly created app. Write down\n   the App ID near the top of the page, you will need it later. It looks\n   something like `339175`.\n5. Scroll down and click \"Generate a private key\". This will create a key and\n   automatically download a file named something like\n   `self-hosted-runner-administration.2023-05-26.private-key.pem`. Keep this\n   file private, i.e., never commit it to a GitHub repository, do not share it\n   with anyone who does not need it etc.\n6. Click on \"Install App\" in the left navigation bar and then press \"Install\"\n   for the current organization.\n7. You will land on the configuration page for the app for the current GitHub\n   organization. Note the URL - it will read something like\n   https://github.com/organizations/trixi-framework/settings/installations/37934927.\n   The last part is the \"installation id\" of the app. Write it down, you will\n   need it later.\n\n### Prepare automatic GitHub token generation\nWhen a new runner is started, it needs to be registered with an organization\nbefore it can be used by jobs created in the GitHub Action workflows. For this,\nan authentication token is required. We will use the GitHub App created in the\nprevious step for this purpose.\n\nTo get an authentication token, two steps are required:\n1. Authenticate with GitHub as the app itself, using the app id noted above\n   (e.g., `339175`).\n2. Authenticate as concrete app installation (e.g., as the app in the Trixi\n   Framework organization), using the installation id noted above (e.g.,\n   `37934927`).\n\nProceed now to install additional dependencies required for the token\ngeneration:\n```shell\nsudo apt-get install -y python3 python3-pip\n```\nWhile the previous step could be done by any user with `sudo` privileges (or\n`root` itself), the next steps should be performed as the user under which the\nDocker containers are to be run. That is, `root` if you go with a `root`-based\nDocker setup, and `docker-rootless` if you use a rootless Docker setup.\n\n1. Create a directory for managing the GitHub authentication:\n   ```shell\n   mkdir $HOME/github-authentication\n   chmod 700 $HOME/github-authentication\n   ```\n   The directory can only be accessed by our user, protecting all files inside\n   from prying eyes.\n2. Upload the private app key generated above to the newly folder, e.g., by running\n   the following command on your _local_ machine where the key is located:\n   ```shell\n   scp ~/Downloads/self-hosted-runner-administration.*.private-key.pem USER@HOSTNAME:github-authentication/\n   ```\n   where `USER` is `root` or `docker-rootless` and `HOSTNAME` is your server's\n   hostname or IP address.\n3. Log in back to the server and make the key read-only,\n   ```shell\n   chmod 600 $HOME/github-authentication/*.pem\n   ```\n   and create a symbolic link to the current key:\n   ```shell\n   ln -rs $HOME/github-authentication/self-hosted-runner-administration.*.private-key.pem \\\n       $HOME/github-authentication/self-hosted-runner-administration.current.private-key.pem\n   ```\n4. Install the Python packages `jwt` and `requests`\n   ```shell\n   pip install jwt requests\n   ```\n5. Create a file `$HOME/github-authentication/access_token.py` with the\n   following content (loosely based on\n   [this](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app#example-using-python-to-generate-a-jwt)\n   snippet):\n   ```python\n   #!/usr/bin/env python3\n\n   import json\n   import jwt\n   import os\n   import requests\n   import time\n   import sys\n   \n   # Set PEM file path\n   pem = os.path.expanduser('~/github-authentication/self-hosted-runner-administration.current.private-key.pem')\n   \n   # Set the App ID\n   app_id = '339175'\n   \n   # Set the installation ID\n   install_id = '37934927'\n   \n   # Open PEM\n   with open(pem, 'rb') as pem_file:\n       signing_key = jwt.jwk_from_pem(pem_file.read())\n   \n   # Create JWT\n   payload = {\n       # Issued at time (-60 recommended to allow for clock drift)\n       'iat': int(time.time()) - 60,\n       # JWT expiration time (10 minutes maximum, but we might be 60 seconds in the past)\n       'exp': int(time.time()) + 500,\n       # GitHub App's identifier\n       'iss': app_id\n   }\n   \n   jwt_instance = jwt.JWT()\n   encoded_jwt = jwt_instance.encode(payload, signing_key, alg='RS256')\n   \n   # Create access token\n   url = 'https://api.github.com/app/installations/' + install_id + '/access_tokens'\n   headers = {\n       'Accept': 'application/vnd.github+json',\n       'Authorization': 'Bearer ' + encoded_jwt,\n       'X-GitHub-Api-Version': '2022-11-28'\n   }\n   payload = {'permissions': {'organization_self_hosted_runners': 'write'}}\n   r = requests.post(url, headers=headers, data=json.dumps(payload))\n   print(r.json()['token'])\n   ```\n   and set it to executable:\n   ```shell\n   chmod +x $HOME/github-authentication/access_token.py\n   ```\n6. Verify that the token generation works by executing\n   `$HOME/github-authentication/access_token.py`. The output should be\n   something like this:\n   ```shell\n   ghs_jq4Iy0M08WJ0qHFJcvukHjCKrIGhfG0c6u4f\n   ```\n\n#### Alternative: create personal access token\n*Note: this is not necessary if using the GitHub App based authentication as\ndescribed above.*\n\nGo to https://github.com/settings/tokens and create a token with the permission\nfor `manage_runners:org`.\n\nThis is not as secure as the app-based authentication since it allows the\nmanagement of runners for all organizations for which the GitHub user has\nsufficient permissions.\n\n### Create script for service startup\nWe will run the GitHub runners inside a Docker container that is automatically\nstarted as a systemd service. For convenience, we will create a script that\nholds all the startup commands, including the command to get a fresh access\ntoken.\n\nCreate a folder that will hold all relevant files for the Docker setup and\nGitHub runner configuration:\n```shell\nmkdir -p $HOME/github-runner-setup\n```\nNext, create a file `$HOME/github-runner-setup/start-docker-github-runner.py` with the following content\n```shell\n#!/usr/bin/env python3\n\nimport multiprocessing\nimport os\nimport subprocess\nimport sys\nimport tempfile\n\n# Resource limits (optional; set to `0` or less for unlimited resources)\n# Maximum amount of memory assigned to each runner (in gigabytes)\nmax_memory = 8\n# Number of CPUs assigned to each runner\ncpu_count = 2\n\n# Store arguments\nhostname = sys.argv[1]\nunit_name = sys.argv[2]\ninstance_name = sys.argv[3]\n\n# Create name arguments\nrunner_name = hostname + '-' + instance_name\ndocker_name = unit_name + '-' + instance_name\n\n# Get access token and write it to temporary file\n# Note: using a temporary file avoids us to having to expose the token in the\n# command line for all other users to see\nenv_temp = tempfile.NamedTemporaryFile(prefix=f'access-token-{docker_name}-',\n                                       dir=os.path.expanduser('~/github-authentication'))\naccess_token_exec = os.path.expanduser('~/github-authentication/access_token.py')\naccess_token = subprocess.run(access_token_exec,\n                              stdout=subprocess.PIPE,\n                              text=True).stdout.strip()\nenv_temp.write(f\"ACCESS_TOKEN={access_token}\\n\".encode())\nenv_temp.flush()\n\n# Configure memory limits\nif max_memory \u003e 0:\n    limit_memory = f'--memory={max_memory}g'\n    limit_swap = f'--memory-swap={max_memory}g'\nelse:\n    limit_memory = ''\n    limit_swap = ''\n\n# Configure CPU limits\nif cpu_count \u003e 0:\n    # We assume that `instance_name` will be '1', '2', '3' etc.\n    runner_id = int(instance_name)\n\n    # We always assign CPUs consecutively\n    start_cpu = (runner_id - 1) * cpu_count\n    end_cpu = start_cpu + cpu_count - 1\n    if end_cpu \u003e= multiprocessing.cpu_count():\n        sys.exit('no cpus left to assign to this runner')\n\n    cpuset_cpus = f'--cpuset-cpus={start_cpu}-{end_cpu}'\nelse:\n    cpuset_cpus = ''\n\n# Build Docker command\nenv_file = os.path.expanduser('~/github-runner-setup/ephemeral-github-actions-runner.env')\ndocker_cmd = [\n    '/usr/bin/docker', 'run', '--rm',\n    '--env-file', env_file,\n    '--env-file', env_temp.name,\n    '-e', f'RUNNER_NAME={runner_name}',\n    '--name', docker_name,\n    'myoung34/github-runner:latest'\n]\n\nif max_memory \u003e 0:\n    docker_cmd.insert(-1, limit_memory)\n    docker_cmd.insert(-1, limit_swap)\n\nif cpu_count \u003e 0:\n    docker_cmd.insert(-1, cpuset_cpus)\n\n# Start docker\nsubprocess.run(docker_cmd)\n```\nand make it executable with\n```shell\nchmod +x $HOME/github-runner-setup/start-docker-github-runner.py\n```\nThe above script will perform the following steps:\n* Call the `access_token.py` script to obtain a new registration token\n* Save the access token to a temporary file (such that it cannot be seen\n  on the command line by other users)\n* Set up the arguments (if configured) for limiting memory and/or CPU usage. By\n  default, the script will limit each runner...\n  * to use at most 8 gigabytes of memory\n  * to use 2 CPUs (CPUs get assigned consecutively and non-overlappingly)\n* Execute `docker run` with all arguments\n\n### Create service environment file\nFollowing https://github.com/myoung34/docker-github-actions-runner/wiki/Usage#systemd\n\nCreate the file `$HOME/github-runner-setup/ephemeral-github-actions-runner.env` with the following\ncontent\n```shell\nRUNNER_SCOPE=org\nORG_NAME=trixi-framework\nLABELS=ephemeral,2core-8gib\nDISABLE_AUTO_UPDATE=1\nEPHEMERAL=1\nDISABLE_AUTOMATIC_DEREGISTRATION=1\n```\nHere, `LABELS` is a comma-separated list of tags that can be used to configure\nwhich jobs should be run on this runner. This can later be controlled in the\nGitHub Actions workflow file through the\n[`runs-on`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on)\ndirective.\n\n### Create service definition file for systemd\nFollowing https://github.com/myoung34/docker-github-actions-runner/wiki/Usage#systemd\n\n~the next steps differ depending on whether you plan to run Docker as\n`root` or as a non-root user. For simplicity, we will refer to each variants by\nthe respective usernames, i.e.,\neither `root` for root-based Docker or `docker-rootless` for non-root Docker.\n\nCreate the file\n* `root`: `/etc/systemd/system/ephemeral-github-actions-runner@.service`\n* `docker-rootless`: `$HOME/.config/systemd/user/ephemeral-github-actions-runner@.service`\n\nwith the following content:\n```shell\n[Unit]\nDescription=Ephemeral GitHub Actions Runner Container\nAfter=docker.service\nRequires=docker.service\n\n[Service]\nTimeoutStartSec=0\nRestart=always\nExecStartPre=-/usr/bin/docker stop %N\nExecStartPre=-/usr/bin/docker rm %N\nExecStartPre=-/usr/bin/docker pull myoung34/github-runner:latest\nExecStart=$HOME/github-runner-setup/start-docker-github-runner.py %H %p %i\n\n[Install]\nWantedBy=multi-user.target\n```\nand set the permissions accordingly:\n* `root`: `chmod 644 /etc/systemd/system/ephemeral-github-actions-runner@.service`\n* `docker-rootless`: `chmod 644 $HOME/.config/systemd/user/ephemeral-github-actions-runner@.service`\n\n### Enable and control GitHub Actions runner service\n\n#### When running Docker as `root`\nEnable the service:\n```shell\nsudo systemctl daemon-reload\nsudo systemctl enable --now ephemeral-github-actions-runner@1\n# sudo systemctl enable --now ephemeral-github-actions-runner@2 # to start more than one runner \n```\n\nStart/stop daemon with\n```shell\n# Run with:\nsudo systemctl start ephemeral-github-actions-runner@1\n# Stop with:\nsudo systemctl stop ephemeral-github-actions-runner@1\n```\n\nSee logs for a single runner with\n```shell\njournalctl -f -u ephemeral-github-actions-runner@1 --no-hostname --no-tail\n```\nor for all runners with\n```shell\njournalctl -f -u \"ephemeral-github-actions-runner@*\" --no-hostname --no-tail\n```\n\n#### When running Docker as non-root user\nEnable the service:\n```shell\nsystemctl --user daemon-reload\nsystemctl --user enable --now ephemeral-github-actions-runner@1\n# systemctl --user enable --now ephemeral-github-actions-runner@2 # to start more than one runner \n```\n\nStart/stop daemon with\n```shell\n# Run with:\nsystemctl --user start ephemeral-github-actions-runner@1\n# Stop with:\nsystemctl --user stop ephemeral-github-actions-runner@1\n```\n\nSee logs for a single runner with\n```shell\njournalctl --user -f -u ephemeral-github-actions-runner@1 --no-hostname --no-tail\n```\nor for all runners with\n```shell\njournalctl --user -f -u \"ephemeral-github-actions-runner@*\" --no-hostname --no-tail\n```\n\n### Verify registration and update runner usage permissions\nGo to https://github.com/organizations/trixi-framework/settings/actions/runners.\nIf everything was successful, you should see your newly created runner in the\nlist of runners.\n\nBefore the runners can be used for running workflows of public repositories, the\npermissions of the *Default* runners group need to be updated by going to the\nGitHub organization's\n[runner group website](https://github.com/organizations/trixi-framework/settings/actions/runner-groups)\nand clicking on the \"Default\" group. There, you need to check \"Allow public repositories\".\n\n### Use a self-hosted runner\nTo run a job on one of the self-hosted runners, you need to modify the\n[`runs-on`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on)\nsetting in your GitHub workflow configuration file to include one of the\ntags that you specified under `LABELS` in the\n[service environment file](#create-service-environment-file).\n\n\n## Authors\nThis project was initiated by [Michael Schlottke-Lakemper](https://lakemper.eu)\n(RWTH Aachen University/High-Performance Computing Center Stuttgart (HLRS),\nGermany). It is maintained by the\n[Trixi.jl authors](https://github.com/trixi-framework/Trixi.jl/blob/main/AUTHORS.md).\n\n## License and contributing\nThe contents of this repository are available under the Create Commons\nAttribution 4.0 International license ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)),\nsee also [`LICENSE`](LICENSE).\nWe are very happy to accept contributions from the community.\nTo get in touch with the developers,\n[join us on Slack](https://join.slack.com/t/trixi-framework/shared_invite/zt-sgkc6ppw-6OXJqZAD5SPjBYqLd8MU~g)\nor [create an issue](https://github.com/trixi-framework/ci-with-self-hosted-runners/issues/new).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrixi-framework%2Fci-with-self-hosted-runners","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrixi-framework%2Fci-with-self-hosted-runners","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrixi-framework%2Fci-with-self-hosted-runners/lists"}