How to reduce the cost of GitHub Actions
Tags: github, devops, cloud, finops
Published: 2023-01-27 02:08:55

> I'll cover how to reduce the code of GitHub Actions, and give some advice.

According to G2's [statistical report](https://www.g2.com/categories/continuous-integration?tab=easiest_to_use), GitHub Actions is the easiest-to-use CI/CD tool, and more and more people like it.

![2023-01-24 08-12-54屏幕截图.png](https://s2.loli.net/2023/01/24/zG5gTWm1lHxBMK2.png)

Since GitHub Actions is GitHub's native CI/CD tool, tens of thousands of Actions can be used directly in the marketplace, and it is free for public repositories. More and more projects are switching their CI tools to GitHub Actions.

I also really like GitHub Actions and use it for almost all my GitHub-hosted repositories.

But recently I was working on a project that hit the GitHub Actions quota limit. It took me some time to focus on its cost.

{% embed https://twitter.com/zhangjintao9020/status/1616077513125691399?s=20&t=we9o9FxhgSEOXzY39czbJg %}

## Why is the quota exhausted?

Recently I found an interesting project: [upptime/upptime: ⬆️ Free uptime monitor and status page powered by GitHub](https://github.com/upptime/upptime)

I want to try to use it to monitor some of the services I have developed and make a status page, this will involve some API configurations, and I don't want to make it public, so I forked the project into a private repository. After a simple configuration, it works fine.

Since I wanted more data, I tweaked the CI scheduler configuration. Make these tasks run more frequently.

```yaml
workflowSchedule:
  graphs: "0 * * * *"
  responseTime: "0 * * * *"
  staticSite: "0 * * * *"
  summary: "0 * * * *"
  updateTemplate: "0 * * * *"
  updates: "0 * * * *"
  uptime: "*/5 * * * *"
```

According to the [billing documentation for GitHub Actions](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions), GitHub Actions for public repositories is Free, but there is a quota limit for private repositories.

> GitHub Actions usage is free for standard GitHub-hosted runners in public repositories, and for Self-hosted runners. For private repositories, each GitHub account receives a certain amount of free minutes and storage for use with GitHub-hosted runners, depending on the product used with the account. Any usage beyond the included amounts is controlled by spending limits.

Soon I received a quota reminder email from GitHub, reminding me that the quota was about to be used up.

This got me thinking about how to solve it.

## Cost of using GitHub Actions

Making the repository public is the most straightforward way, but I explained above why it cannot be made public. I can only find other solutions.

Paying for GitHub Actions is also a very straightforward solution.

Before deciding to pay for it, I want to estimate the cost. GitHub provides a [Pricing Calculator](https://github.com/pricing/calculator), which can easily estimate costs.

Since I modified the CI's scheduling configuration, the most frequently run tasks will run every 5 minutes.

I used [Meercode](https://meercode.io/) to collect the running data of GitHub Actions in this repository. It provides some dashboards by default:

![2023-01-25 11-29-12 screenshot.png](https://s2.loli.net/2023/01/25/m5S3ZAtdpNjQz9y.png)

It also allows users to customize it themselves. I created my dashboard. If you are interested in Meercode, please let me know in the comments.

![ci-dashboard.png](https://s2.loli.net/2023/01/25/5bUsJnwmurC1fxl.png)

As can be seen from the figure above, each task takes no more than 0.5 minutes, and there are no more than 12 tasks per hour. Using the price calculator, the approximate **cost is $35 per month**.

![2023-01-25 11-25-13 screenshot.png](https://s2.loli.net/2023/01/25/DGQ3ie2zW6MTjC1.png)

## Ways to save costs

Since my repository is mainly run uptime CI, it consumes few resources but has frequent tasks, so I wonder if I can save costs if I use a self-hosted runner.

I compared the prices of 3 lower-priced cloud service providers:

* [Civo](https://www.civo.com/)
    
* [DigitalOcean](https://www.digitalocean.com/pricing/droplets)
    
* [Vultr](https://www.vultr.com/pricing/)
    

Among them, both Civo and Vultr provide 1C1G instances at $5/month, and DigitalOcean instances with the same specifications are priced at $6/month.

I finally chose [Civo](https://www.civo.com/), which is a *cloud-native service provider*, and there is an introduction on its homepage:

> Transparent pricing from just $5 a month

Civo provides a variety of services, such as Kubernetes (based on k3s), or compute instances.

Among them, the instance specification of the *Extra Small* type is 1C1G, and it has 1TB traffic, and if you choose the Kubernetes service, you do not need to pay for the control plane(same as Azure AKS). Even the larger specs look cheap.

![2023-01-25 17-04-35 screenshot.png](https://s2.loli.net/2023/01/25/jzmTs6X7PR9QVMx.png)

I have tried using its Kubernetes service, and compute instance respectively, and they both work fine.

![2023-01-25 17-08-43 screenshot.png](https://s2.loli.net/2023/01/25/2ur4YJ6AlvKgm8O.png)

### Using compute instances

Deploying the GitHub Actions runner in a Linux compute instance is simple, just add it to the project `https://github.com/<Your name>/<Project name>/settings/actions/runners/new`.

There are complete deployment steps on this page, just follow the steps.

My installation process is as follows:

```bash
civo@polished-bush-99d8-1926a1:~$ mkdir actions-runner && cd actions-runner
civo@polished-bush-99d8-1926a1:~/actions-runner$ curl -o actions-runner-linux-x64-2.301.1.tar.gz -L https://github.com/actions/runner/releases/download/v2.301.1/actions-runner-linux-x64-2.301.1.tar.gz
civo@polished-bush-99d8-1926a1:~/actions-runner$ echo "3ee9c3b83de642f919912e0594ee2601835518827da785d034c1163f8efdf907  actions-runner-linux-x64-2.301.1.tar.gz" | shasum -a 256 -c
actions-runner-linux-x64-2.301.1.tar.gz: OK                                                                     
civo@polished-bush-99d8-1926a1:~/actions-runner$ tar xzf ./actions-runner-linux-x64-2.301.1.tar.gz              
civo@polished-bush-99d8-1926a1:~/actions-runner$ ./config.sh --url https://github.com/MoeLove/monitoring --token $TOKEN
```

After the execution is complete, some files will be added to the current directory. Execute `./env.sh` to start the GitHub Actions runner.

```bash
civo@polished-bush-99d8-1926a1:~/actions-runner$ ls
_diag  _work  actions-runner-linux-x64-2.301.1.tar.gz  bin  config.sh  env.sh  externals  run-helper.cmd.template  run-helper.sh  run-helper.sh.template  run.sh  safe_sleep.sh  svc.sh
```

If you want to run stably in the background, you can execute `./svc.sh install` to install the runner as a systemd service and manage its life cycle through systemd.

### Using Kubernetes

Civo does not charge for the Kubernetes control plane, but only for Worker Nodes. The advantage of using Kubernetes is that I can automatically scale up and down in the cluster, and I can easily run and create multiple runners for different projects.

Since GitHub official has not provided to deploy a Self-hosted runner on Kubernetes, I used the [Actions Runner Controller (ARC)](https://github.com/actions/actions-runner-controller) project, This project allows rapid deployment of Self-hosted runners through `Runner` custom resources.

The deployment process is clearly described in the [documentation](https://github.com/actions/actions-runner-controller/blob/master/docs/quickstart.md). The following is my deployment process.

```sh
# deploy cert-manager
(MoeLove) ➜ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml

# deploy ARC
(MoeLove) ➜ helm repo add actions-runner-controller https://actions-runner-controller.github.io/actions-runner-controller
(MoeLove) ➜ helm upgrade --install --namespace actions-runner-system --create-namespace\
  --set=authSecret.create=true\
  --set=authSecret.github_token="REPLACE_YOUR_TOKEN_HERE"\
  --wait actions-runner-controller actions-runner-controller/actions-runner-controller

# create runner
(MoeLove) ➜ cat <<EOF | kubectl apply -f -
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
  name: moelove-runner
spec:
  replicas: 1
  template:
    spec:
      repository: MoeLove/monitoring
EOF
```

After installation, the following results are achieved:

{% embed https://twitter.com/zhangjintao9020/status/1616251840429002755?s=20&t=SM0rFgSfq8b03CD11dhdNw %}

## Self-hosted vs GitHub-managed

In the content above, I introduced how I used Meercode to measure the key indicators of CI metrics and estimate the cost of GitHub Actions. According to my actual low resource consumption and high time-consuming scenario, I chose the Self-hosted runner.

So when is it more appropriate to choose a GitHub-managed runner? What are the benefits of GitHub-managed?

The GitHub-managed runner has the following advantages:

* **Support for multiple operating systems**: In addition to providing Linux systems, GitHub-managed runner also supports macOS and Windows, but most cloud providers do not provide macOS environments. (I used to put some Mac minis as servers in the data center for specific scenarios)
    
* **VM-level isolation**: According to the GitHub Actions documentation, when the GitHub Actions runner runs a job, it creates a VM to run all tasks, which brings certain security and isolation guarantees. If it is a Self-hosted runner when running through the binary, the task will share the host environment, and if it is running through ARC, it will bring isolation through the Pod. This will cause certain security issues.
    
* **Low Maintenance Costs**: In fact in any large system, maintenance costs are very expensive. If it is only for personal use, or only a few projects use the Self-hosted runner, the maintenance cost is relatively controllable. Once it gets big, it introduces a lot of complexity. The GitHub-managed runner is maintained by GitHub.
    

There are also two products that offer self-hosted runner services:

* [Actuated](https://actuated.dev?umt_source=blog.moelove.info)
    
* [cirun](https://cirun.io?umt_source=blog.moelove.info)
    

They reduce the cost of runner maintenance and management and provide more secure isolation and support for Arm-based environments. cirun also provides GPU runner support.

If you have the above requirements, you may also wish to consider these services.

## Summarize

In general, the following steps are required to reduce the cost of GitHub actions.

* Visualization/Observability: Estimate costs using actual data.
    
* Compare multiple vendors/solutions: Different vendors offer different pricing for different scenarios or products, and you can choose according to your actual situation.
    
* Security and maintenance costs also need to be considered.
    

If you are interested in my articles, please subscribe to my Newsletter!

{% embed https://blog.moelove.info/newsletter %}