DEV Community

Mustafa ERBAY
Mustafa ERBAY

Posted on • Originally published at mustafaerbay.com.tr

Self-hosted GitHub Actions Runner: Balancing Cost and Control

Automating our CI/CD processes with GitHub Actions has become standard practice. However, for large projects or situations with specific requirements, the idea of hosting runners on our own infrastructure instead of using GitHub's general-purpose ones becomes plausible. This approach promises cost reduction, increased control, and performance optimization. So, do self-hosted runners truly deliver on these promises? In this post, I will delve deep into the advantages, disadvantages, and points to consider when setting up these installations on my own infrastructure, with concrete examples.

In this guide, we will focus on the process of setting up and managing self-hosted GitHub Actions runners. Our goal is to clearly outline the fundamental benefits and costs associated with this. Specifically, we will step-by-step explore how you can make your CI/CD processes more efficient by using your own hardware or virtual machines. This is not just a setup guide, but an analysis that will help you define a long-term strategy.

Why Self-hosted GitHub Actions Runner? Cost and Control Dynamics

The primary motivation for running GitHub Actions runners on our own servers usually lies in the cost and control dilemma. GitHub's hosted runners are ready to use and easy to manage, but can increase costs with heavy usage. Self-hosted runners, while requiring an initial investment, have the potential to reduce operational costs in the long run. Especially if you have existing server infrastructure or already have a high CI/CD traffic volume, this model can be more economical.

For example, let's consider a project where we spend an average of 10,000 minutes of build time per week. According to GitHub's pricing, this could amount to approximately $400-500 USD per month. However, when we perform this task on our own servers, on an existing VPS (e.g., $50 per month) or on-premise servers, the hardware and electricity costs can be significantly lower than this figure. Of course, this calculation should also include the cost of administrator time and potential troubleshooting.

ℹ️ Cost Analysis

When setting up your own infrastructure, you must consider not only the hardware cost but also maintenance, energy consumption, network bandwidth, and most importantly, the human resources required to manage this infrastructure. Sometimes, what appears to be a low hourly cost of $0.00075 for hosted runners can result in a substantial bill over thousands of hours of usage.

Control is another important factor. While hosted runners have a specific operating system and software set, you have complete control over self-hosted runners. This provides great flexibility in tasks such as installing specific dependencies, integrating custom tools, or tightening security policies. For instance, if you have builds that require specific hardware or use proprietary software, self-hosted runners can meet this need.

Setup Process: Step-by-Step Self-hosted Runner Configuration

Setting up a self-hosted runner essentially consists of a few steps, as outlined in GitHub's official documentation. First, you need a machine to host the runner. This can be a virtual machine (VM), a physical server, or even a Docker container. Then, you need to download and install the runner application from GitHub and connect it to your GitHub organization or repository.

The first step is to prepare the necessary software on the server where you will install the runner. This typically includes Node.js and some basic system tools. Then, you download the runner package from the official GitHub Actions repository. This package may vary depending on your platform (Linux x64, ARM, Windows, etc.).

💡 Runner Application

After downloading the runner application, you configure the runner using the config.sh (Linux/macOS) or config.cmd (Windows) script. At this stage, you will be prompted to specify your organization or repository name, a label for the runner, and the user under which the runner will operate. For example:

./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO --token YOUR_RUNNER_TOKEN --labels self-hosted,linux,x64

This command connects the runner to the specified repository and assigns it the labels self-hosted, linux, x64. These labels are used to determine which runner to use in your workflows.

Once the configuration is complete, running the runner as a service is the most practical approach. On Linux systems, you can easily do this using systemd. This ensures that the runner starts automatically when the server restarts. The svc.sh script will help you with this. For example, a systemd unit file might look like this:

[Unit]
Description=GitHub Actions Runner
After=network.target

[Service]
User=runneruser
Group=runnergroup
WorkingDirectory=/home/runneruser/actions-runner
ExecStart=/home/runneruser/actions-runner/run.sh
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

This configuration ensures that the runner runs in the background continuously and restarts automatically in case of an error. This is critical for an uninterrupted CI/CD process, especially in production environments.

Performance Optimization and Resource Management

One of the biggest advantages of self-hosted runners is performance optimization. Hosted runners are general-purpose and may not always have the hardware best suited for your specific needs. When you set up your own runners, you can choose the CPU, RAM, and disk configuration that best suits your workload.

For example, for projects involving large compilation tasks, a machine with a high core count and ample RAM would be preferred. For database tests or I/O-intensive operations, fast SSDs and sufficient disk space are critical. This flexibility can significantly reduce build times. In one project, a large C++ project's compilation, which took 15 minutes on hosted runners, was reduced to as little as 7 minutes on a specially configured self-hosted runner. This directly impacts not only the build time but also the speed at which developers receive feedback.

⚠️ CPU and RAM Limits

The CPU and RAM limits you set for your runners are vital for the success of your builds. If these limits are exceeded during a build, the process may terminate (OOM Killer might intervene) or the system may become unstable. Correctly setting these limits using tools like cgroup ensures system stability as well as performance. For example, correctly configuring soft limits like memory.high can help the system remain stable even during sudden memory spikes.

Another point to consider regarding resource management is scaling your runners. You might need multiple runners during peak times. For this, you can set up an auto-scaling infrastructure using container orchestration tools like Docker Swarm or Kubernetes. This allows you to dynamically increase or decrease the number of runners based on demand, thereby optimizing costs and ensuring your workflows run uninterrupted. For instance, new runner containers can be automatically started at a specific time or when a certain number of jobs arrive.

Security: Risks and Precautions for Self-hosted Runners

While self-hosted runners offer more control, they also bring additional security responsibilities. Like any service running on your own infrastructure, your runners must be protected against potential security vulnerabilities. While GitHub's hosted runners are managed by GitHub, the security of self-hosted runners is entirely your responsibility.

First and foremost, it is essential to keep the server running the runner up-to-date and regularly apply security patches. All components, such as the operating system, Node.js, Docker, and the runner application itself, must be current. Furthermore, granting the user running the runner the least privilege (principle of least privilege) is important for security. The runner should only have the permissions necessary to execute CI/CD jobs.

🔥 Unauthorized Access Risk

If unauthorized access is gained to the server running your runner, it can lead to serious consequences such as theft, modification of your code, or access to sensitive information. Therefore, maintaining strict network security, access controls, and monitoring mechanisms is vital. Basic security measures like preventing brute-force attacks with tools like Fail2ban and restricting SSH access should be implemented.

Additionally, you must ensure the security of the jobs running on your runners. For example, ensure that all dependencies used in your build processes come from trusted sources. Regularly scan for known security vulnerabilities in your dependencies using tools like npm audit or yarn audit. By tracking CVEs, identify critical vulnerabilities in the tools you use early and take precautions.

Disadvantages and Points to Consider

While self-hosted runners offer great potential, they also come with some significant disadvantages and challenges that should not be overlooked. The foremost among these is the burden of managing and maintaining the infrastructure. Monitoring, updating, troubleshooting, and scaling the servers running your runners requires additional effort. This can be a significant burden, especially for small teams or companies with limited IT resources.

Another disadvantage is the initial cost. Setting up your own servers or allocating existing infrastructure for this purpose can require a substantial initial investment. Hardware purchases, licenses (if proprietary software is used), and initial setup processes can strain the budget. With GitHub's hosted runners, you can start using them immediately without such an upfront investment.

ℹ️ Management Overhead

Managing your self-hosted runners essentially becomes a system administration task. Tasks such as server maintenance, security updates, backups, and performance monitoring become part of the CI/CD process. It is important not to underestimate this workload and to allocate the necessary resources.

Finally, there can also be limitations in terms of scalability and flexibility. Scaling with your own hardware may not be as fast and dynamic as with hosted runners. Adding new servers or increasing existing capacity can take time. The scalable infrastructure offered by GitHub is generally more successful in meeting sudden demand increases. Therefore, you need to carefully assess how variable your workload is and how quickly you might need to scale.

Conclusion: Should You Use Your Own Runners?

Self-hosted GitHub Actions runners offer a powerful solution for reducing costs, improving performance, and gaining full control over your infrastructure in the right scenarios. However, the management overhead, initial costs, and security responsibilities that come with this approach should not be overlooked.

If your project is large-scale, has high CI/CD traffic, has specific hardware or software dependencies, and you have the technical resources to manage this infrastructure, self-hosted runners might be the right choice for you. If you want to efficiently utilize your existing server infrastructure or save on hosted runner costs in the long run, you should seriously consider this method.

However, if your team is small, your CI/CD processes are more standard, and you don't want to take on the management burden, GitHub's hosted runners will offer a more practical and cost-effective solution. The final decision will depend on your project's specific requirements, your budget, and your team's technical capabilities. Running runners on your own infrastructure means more than just being an automation tool; it means building your own CI/CD cloud.

Top comments (0)