├── .editorconfig ├── Dockerfile ├── LICENSE.md ├── README.md ├── deployment.yml ├── entrypoint.sh └── runsvc.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | charset = utf-8 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:buster-slim 2 | 3 | ENV GITHUB_PAT "" 4 | ENV GITHUB_TOKEN "" 5 | ENV GITHUB_OWNER "" 6 | ENV GITHUB_REPOSITORY "" 7 | ENV RUNNER_WORKDIR "_work" 8 | ENV RUNNER_LABELS "" 9 | ENV ADDITIONAL_PACKAGES "" 10 | 11 | RUN apt-get update \ 12 | && apt-get install -y \ 13 | curl \ 14 | sudo \ 15 | git \ 16 | jq \ 17 | iputils-ping \ 18 | && apt-get clean \ 19 | && rm -rf /var/lib/apt/lists/* \ 20 | && useradd -m github \ 21 | && usermod -aG sudo github \ 22 | && echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers 23 | 24 | USER github 25 | WORKDIR /home/github 26 | 27 | RUN GITHUB_RUNNER_VERSION=$(curl --silent "https://api.github.com/repos/actions/runner/releases/latest" | jq -r '.tag_name[1:]') \ 28 | && curl -Ls https://github.com/actions/runner/releases/download/v${GITHUB_RUNNER_VERSION}/actions-runner-linux-x64-${GITHUB_RUNNER_VERSION}.tar.gz | tar xz \ 29 | && sudo ./bin/installdependencies.sh 30 | 31 | COPY --chown=github:github entrypoint.sh runsvc.sh ./ 32 | RUN sudo chmod u+x ./entrypoint.sh ./runsvc.sh 33 | 34 | ENTRYPOINT ["/home/github/entrypoint.sh"] 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sander Knape 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Github self-hosted runner Dockerfile and Kubernetes configuration 2 | 3 | [![awesome-runners](https://img.shields.io/badge/listed%20on-awesome--runners-blue.svg)](https://github.com/jonico/awesome-runners) 4 | 5 | This repository contains a Dockerfile that builds a Docker image suitable for running a [self-hosted GitHub runner](https://sanderknape.com/2020/03/self-hosted-github-actions-runner-kubernetes/). A Kubernetes Deployment file is also included that you can use as an example on how to deploy this container to a Kubernetes cluster. 6 | 7 | You can build this image yourself, or use the Docker image from the [Docker Hub](https://hub.docker.com/r/sanderknape/github-runner). 8 | 9 | ## Building the container 10 | 11 | `docker build -t github-runner .` 12 | 13 | ## Features 14 | 15 | * Repository runners 16 | * Organizational runners 17 | * Labels 18 | * Graceful shutdown 19 | * Support for installing additional debian packages 20 | * Auto-update after the release of a new version 21 | 22 | ## Examples 23 | 24 | Register a runner to a repository. 25 | 26 | ```sh 27 | docker run --name github-runner \ 28 | -e GITHUB_OWNER=username-or-organization \ 29 | -e GITHUB_REPOSITORY=my-repository \ 30 | -e GITHUB_PAT=[PAT] \ 31 | sanderknape/github-runner 32 | ``` 33 | 34 | Register a runner with github token. 35 | 36 | ```sh 37 | docker run --name github-runner \ 38 | -e GITHUB_OWNER=username-or-organization \ 39 | -e GITHUB_REPOSITORY=my-repository \ 40 | -e GITHUB_TOKEN=[github.token] \ 41 | sanderknape/github-runner 42 | ``` 43 | 44 | Create an organization-wide runner. 45 | 46 | ```sh 47 | docker run --name github-runner \ 48 | -e GITHUB_OWNER=username-or-organization \ 49 | -e GITHUB_PAT=[PAT] \ 50 | sanderknape/github-runner 51 | ``` 52 | 53 | Set labels on the runner. 54 | 55 | ```sh 56 | docker run --name github-runner \ 57 | -e GITHUB_OWNER=username-or-organization \ 58 | -e GITHUB_REPOSITORY=my-repository \ 59 | -e GITHUB_PAT=[PAT] \ 60 | -e RUNNER_LABELS=comma,separated,labels \ 61 | sanderknape/github-runner 62 | ``` 63 | 64 | Install additional tools on the runner. 65 | 66 | ```sh 67 | docker run --name github-runner \ 68 | -e GITHUB_OWNER=username-or-organization \ 69 | -e GITHUB_REPOSITORY=my-repository \ 70 | -e GITHUB_PAT=[PAT] \ 71 | -e ADDITIONAL_PACKAGES=firefox-esr,chromium \ 72 | sanderknape/github-runner 73 | ``` 74 | -------------------------------------------------------------------------------- /deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: github-runner 5 | labels: 6 | app: github-runner 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: github-runner 12 | template: 13 | metadata: 14 | labels: 15 | app: github-runner 16 | spec: 17 | containers: 18 | - name: github-runner 19 | image: sanderknape/github-runner:latest 20 | env: 21 | - name: GITHUB_OWNER 22 | value: your-organization 23 | - name: GITHUB_REPOSITORY 24 | value: your-repository 25 | - name: GITHUB_PAT # stored separately in a Kubernetes secret 26 | valueFrom: 27 | secretKeyRef: 28 | name: my-secret 29 | key: pat 30 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -n "${ADDITIONAL_PACKAGES}" ]; then 4 | TO_BE_INSTALLED=$(echo ${ADDITIONAL_PACKAGES} | tr "," " " ) 5 | echo "Installing additional packages: ${TO_BE_INSTALLED}" 6 | sudo apt-get update && sudo apt-get install -y ${TO_BE_INSTALLED} && sudo apt-get clean 7 | fi 8 | 9 | registration_url="https://github.com/${GITHUB_OWNER}" 10 | token_url="https://api.github.com/orgs/${GITHUB_OWNER}/actions/runners/registration-token" 11 | 12 | if [ -n "${GITHUB_TOKEN}" ]; then 13 | echo "Using given GITHUB_TOKEN" 14 | 15 | if [ -z "${GITHUB_REPOSITORY}" ]; then 16 | echo "When using GITHUB_TOKEN, the GITHUB_REPOSITORY must be set" 17 | return 18 | fi 19 | 20 | registration_url="https://github.com/${GITHUB_OWNER}/${GITHUB_REPOSITORY}" 21 | export RUNNER_TOKEN=$GITHUB_TOKEN 22 | 23 | else 24 | if [ -n "${GITHUB_REPOSITORY}" ]; then 25 | token_url="https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPOSITORY}/actions/runners/registration-token" 26 | registration_url="${registration_url}/${GITHUB_REPOSITORY}" 27 | fi 28 | 29 | echo "Requesting token at '${token_url}'" 30 | 31 | payload=$(curl -sX POST -H "Authorization: token ${GITHUB_PAT}" ${token_url}) 32 | export RUNNER_TOKEN=$(echo $payload | jq .token --raw-output) 33 | 34 | fi 35 | 36 | if [ -z "${RUNNER_NAME}" ]; then 37 | RUNNER_NAME=$(hostname) 38 | fi 39 | 40 | ./config.sh \ 41 | --name "${RUNNER_NAME}" \ 42 | --token "${RUNNER_TOKEN}" \ 43 | --url "${registration_url}" \ 44 | --work "${RUNNER_WORKDIR}" \ 45 | --labels "${RUNNER_LABELS}" \ 46 | --unattended \ 47 | --replace 48 | 49 | remove() { 50 | if [ -n "${GITHUB_TOKEN}" ]; then 51 | export REMOVE_TOKEN=$GITHUB_TOKEN 52 | else 53 | payload=$(curl -sX POST -H "Authorization: token ${GITHUB_PAT}" ${token_url%/registration-token}/remove-token) 54 | export REMOVE_TOKEN=$(echo $payload | jq .token --raw-output) 55 | fi 56 | 57 | ./config.sh remove --unattended --token "${REMOVE_TOKEN}" 58 | } 59 | 60 | trap 'remove; exit 130' INT 61 | trap 'remove; exit 143' TERM 62 | 63 | ./runsvc.sh "$*" & 64 | 65 | wait $! 66 | 67 | -------------------------------------------------------------------------------- /runsvc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # convert SIGTERM signal to SIGINT 4 | # for more info on how to propagate SIGTERM to a child process see: http://veithen.github.io/2014/11/16/sigterm-propagation.html 5 | trap 'kill -INT $PID' TERM INT 6 | 7 | if [ -f ".path" ]; then 8 | # configure 9 | export PATH=`cat .path` 10 | echo ".path=${PATH}" 11 | fi 12 | 13 | # insert anything to setup env when running as a service 14 | 15 | # run the host process which keep the listener alive 16 | ./externals/node12/bin/node ./bin/RunnerService.js & 17 | PID=$! 18 | wait $PID 19 | trap - TERM INT 20 | wait $PID 21 | --------------------------------------------------------------------------------