Github actions are great! However the free tier of 2000 minutes / month in private repositories can quickly run out if your workflow requires your CI/CD pipeline to be ran frequently.
Thankfully, Github offers the possibilty to use a self-hosted Github Action Runner. More information here. They may be a good idea to optimize costs (especially if you have cloud credit!).
In this article we will go through the three steps needed to create one and talk about some of the challenges VS using Github’s default runner
Step 1: Create an instance
Here we only cover creating a VM on GCP’s Compute Engine, but the next steps are exactly the same for other cloud providers like AWS EC2
Go to https://console.cloud.google.com → Your project → Compute Engine → Vm instances → Create, with the following settings:
- Name: github-action-runner
- Machine configuration: The E2 serie offers the best cost / performance value for general purpose instances. I recommend the e2-medium (2 vCPUs / 4GB of memory) for ~$25 / month, we can always scale up or down as needed later on.
- Boot disk: Ubuntu 18.04 with 20Gb. Best to start smaller on the disk because we can increase but not decrease its size later on.
- Firewall: Allow HTTP and HTTPS traffic. We need that to connect with Github’s backend
Hit Create and wait a few seconds.
Step 2: Configure the instance
The default Ubuntu setup we get from a fresh VM is missing just a few dependencies before we can install the runner:
- We need the latest version of Git (by default an older version is installed)
- We may need to install Docker (if it is part of CI/CD)
- We may need sudo access
- And of course we want the latest patches and security updates
Here is a script to automate those steps:
sh -c "$(curl -fsSL https://gist.githubusercontent.com/gjgd/5a08da85a98bf147294f331461e44d1f/raw/a63bf0f4169a8ab651adfa0a56e676e6bc465876/setup-github-action-runner.sh)"
Step 3: Install the action runner on the instance
In this step, we will download the action runner software on our instance, so that Github can communicate to our instance which workflow to run, and when.
Github’s documentation on the topic is fantastic, just follow theses steps to link your repository with your runner instance: https://docs.github.com/en/actions/hosting-your-own-runners/adding-self-hosted-runners
Lastly, use runs-on: self-hosted in your workflow in order to use your instance instead of the default runner:
name: CI
on: [push]
jobs:
ci:
runs-on: self-hosted
steps:
(...)
Now you’re all set 🚀
Note that, as mentioned in the documentation, you can setup the runner in a specific private repository, or organization wide!
A few things to consider when using your own runner
It may be cheaper to pay for extra Github minutes
Depending on your compute requirements or CI/CD build time / frequency, using a dedicated VM may be more expensive than to just pay for the extra Github minutes. More on Github minutes pricing here
You need to cleanup after yourself
When using Github’s default runner, you will get a new VM for each workflow execution, however if you use your own self-hosted runner, it is your responsibility to clean up your environment in between workflow runs (kill running processes, remove build artifacts, etc…)
I personally ran into two complications:
- Docker containers created as part of your Github Workflow are not automatically stopped, so you need to do it manually at the end of your workflow. For that, I found the
always()
workflow function extremely helpful.
This last step will always run, even if a previous step failed
steps:
(...)
- name: Cleanup
if: ${{ always() }}
run: docker ps -q | xargs -n 1 -P 8 -I {} docker stop {}
- Build artifacts and other generated files may need to be cleanup as well at the end of the workflow execution
DO NOT use a self-hosted runner for a public repo
Here are some reasons why it is a bad idea.
Moreover, there is really no reason to do it since Github minutes on public repositories are free, yay!
Final thoughts
Building your dedicated self-hosted Github Action runner is really easy, and it may be a good solution to reduce CI/CD costs depending on your requirements, however it requires to think about how to reset the environment in between workflow run.
Please comments with questions or feedback!