{"id":1930,"date":"2025-02-20T12:49:43","date_gmt":"2025-02-20T10:49:43","guid":{"rendered":"https:\/\/upcloud.com\/global\/us\/resources\/tutorials\/supercharge-your-ci-cd\/"},"modified":"2025-02-20T12:49:43","modified_gmt":"2025-02-20T10:49:43","slug":"supercharge-your-ci-cd","status":"publish","type":"tutorial","link":"https:\/\/upcloud.com\/global\/resources\/tutorials\/supercharge-your-ci-cd\/","title":{"rendered":"Deploy Lightning-Fast GitHub Actions Runners on Managed Kubernetes\u00ae with UpCloud: Part 1"},"content":{"rendered":"\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions <a href=\"https:\/\/docs.github.com\/en\/actions\/about-github-actions\/understanding-github-actions#runners\" target=\"_blank\" rel=\"noopener\">Runners<\/a> are the backbone of GitHub\u2019s CI\/CD workflows, executing jobs defined in your pipelines. While GitHub provides hosted runners, many teams opt for self-hosted runners for faster build times, cost efficiency, and custom environments tailored to their workloads. Self-hosted runners are particularly useful for resource-intensive jobs, proprietary software builds, or workflows requiring specific tools or configurations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this four-part series, you\u2019ll learn how to deploy and optimize lightning-fast GitHub Actions Runners on <a href=\"https:\/\/upcloud.com\/global\/products\/managed-kubernetes\">Managed Kubernetes<\/a> service. We\u2019ll cover everything from provisioning the cluster to advanced configurations, troubleshooting, and security best practices.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Part 1, we\u2019ll focus on setting up the foundation: provisioning an UpCloud Kubernetes Cluster and installing the GitHub Actions Runner controller. You\u2019ll find step-by-step instructions to get your runners up and running, ready to execute workflows from your repositories. Let\u2019s dive in and get started!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Prerequisites<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before we begin provisioning the UpCloud Kubernetes cluster and installing the GitHub Actions Runner controller, make sure that you have the following ready:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>UpCloud Account<\/strong>: Make sure to sign up at <a href=\"https:\/\/upcloud.com\/global\/\">upcloud.com<\/a>.<\/li>\n\n\n\n<li><strong><code>kubectl<\/code> Installed and Configured<\/strong>: Download and install <code>kubectl<\/code> to manage your Kubernetes cluster. Ensure it\u2019s added to your system\u2019s <code>PATH<\/code>.<\/li>\n\n\n\n<li><strong>Helm 3.x Installed<\/strong>: Helm is a package manager for Kubernetes. <a href=\"https:\/\/helm.sh\/docs\/intro\/install\/\" target=\"_blank\" rel=\"noopener\">Install Helm<\/a> and verify the installation using <code>helm version<\/code>.<\/li>\n\n\n\n<li><strong>GitHub Personal Access Token (PAT)<\/strong>: Create a PAT with the following scopes from your GitHub account:\n<ul class=\"wp-block-list\">\n<li><code>repo<\/code> (Full control)<\/li>\n\n\n\n<li><code>workflow<\/code> (Full control)<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>[Optional]: <a href=\"https:\/\/upcloudltd.github.io\/upcloud-cli\/\" target=\"_blank\" rel=\"noopener\"><code>upctl<\/code><\/a> installed locally to simplify managing the <code>kubeconfig.yaml<\/code> file of your cluster.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">With these prerequisites in place, you are ready to deploy your self-hosted runners on Managed Kubernetes. Let\u2019s proceed to the first step\u2014creating the managed Kubernetes cluster!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Setting up the Managed Kubernetes Cluster on UpCloud<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To get started, head over to the <a href=\"https:\/\/hub.upcloud.com\/kubernetes\">Kubernetes Clusters page<\/a> on the UpCloud console and click on the <strong>Create new cluster<\/strong> button:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners12-1024x522.png\" alt=\"-\" class=\"wp-image-47634\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You will be taken to the <strong>New Kubernetes Cluster<\/strong> page:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners11-1024x522.png\" alt=\"-\" class=\"wp-image-47646\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">On this page, you will configure the Kubernetes cluster you need for self-hosting GitHub Actions runners. To be able to run GitHub Actions Runners, you need to set up a Kubernetes cluster with the following <strong>minimum<\/strong> specifications:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cluster size: 1 node<\/li>\n\n\n\n<li>Node specifications: 4 vCPU, 8GB RAM<\/li>\n\n\n\n<li>Storage: 20 GB per node<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">To start, choose a location for your managed Kubernetes cluster. Next, choose the cluster plan. For now, you can let it be <strong>Development<\/strong>, but for production use-cases, make sure to choose <strong>Production<\/strong>:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners10-1024x522.png\" alt=\"-\" class=\"wp-image-47645\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next, you need to create a private network for your cluster to use for connecting to its worker nodes. Click on the <strong>+ Create Private network<\/strong> button to create a new private network:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners9-1024x522.png\" alt=\"-\" class=\"wp-image-47644\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You can choose the default configuration for the new private network and click on the <strong>Create Private Network<\/strong> button:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners8-1024x522.png\" alt=\"-\" class=\"wp-image-47643\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next, you need to configure the node group. Start by choosing the plan with 4 CPU cores, 8 GB memory, and 160 GB storage. You\u2019ll find it under the <strong>General purpose<\/strong> tab. Also, set the number of nodes to <em>1<\/em> to create just one node for now. In production use-cases, this number will depend on your scalability and other relevant requirements:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners7-1024x522.png\" alt=\"-\" class=\"wp-image-47642\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next, you will need to create and add an SSH key to the cluster. Doing this will allow you to SSH into the node group nodes once they\u2019re ready.<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners6-1024x522.png\" alt=\"-\" class=\"wp-image-47641\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If you have existing SSH keys that you used with UpCloud earlier, you can use them by selecting them from the list. You can also create a new SSH key pair by running the command <code>ssh-keygen -t rsa<\/code>. You can then print the public key by running the command <code>cat ~\/.ssh\/id_rsa.pub<\/code>. You will need to paste this value into UpCloud when adding a new SSH key to it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next, you can choose the Kubernetes version (leave it as v1.29 for now) and enable the <strong>Allow access from all IP addresses<\/strong> switch to allow the <code>kubectl<\/code> tool to connect to your cluster from any machine.<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners5-1024x522.png\" alt=\"-\" class=\"wp-image-47640\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In production use-cases, you would want to add the IPv4 address or CIDR block of your development machine into this section to only allow your (trusted) machine to be able to connect to this cluster via <code>kubectl<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At the end, you can choose a name for your cluster. Once done, click the <strong>Create cluster<\/strong> button.<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners4-1024x522.png\" alt=\"-\" class=\"wp-image-47639\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now, you need to wait for a few minutes while UpCloud provisions resources and spins up your cluster. Once it\u2019s ready, you will notice the <strong>Running<\/strong> status under the name of the cluster on the cluster details page:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners3-1024x522.png\" alt=\"-\" class=\"wp-image-47638\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If you scroll down, you will find two commands that will help you download the <code>kubeconfig.yaml<\/code> file for this cluster and set it as your <code>KUBECONFIG<\/code> to be able to connect to the cluster via command line:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners2-1024x522.png\" alt=\"-\" class=\"wp-image-47637\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You can switch to the <strong>Manual<\/strong> tab to download the kubeconfig file manually if you wish to.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you have <code>upctl<\/code> installed and configured locally, copy the two commands and run them on a terminal window. If not, follow the instructions in the <strong>Manual<\/strong> tab. Once done, your terminal will be able to access the newly created cluster<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Installing the Runner Controller<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now, let\u2019s walk through setting up the Actions Runner Controller (ARC) on  Managed Kubernetes. You\u2019ll use Helm to handle the installation to keep things quick and straightforward.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You will also need a GitHub repo to link your GitHub Actions runner with. If you are using an organization account, you can use the organization URL instead. For personal accounts, you will need a GitHub repo though. Feel free to <a href=\"https:\/\/github.com\/krharsh17\/random-facts-generator\" target=\"_blank\" rel=\"noopener\">fork this one<\/a> to your GitHub account if you need it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, let\u2019s start!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Install the Operator and CRDs<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">First, choose a namespace for the ARC operator pods (for now, use <code>arc-systems<\/code>):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">NAMESPACE=\"arc-systems\"\nhelm install arc \\\n  --namespace \"${NAMESPACE}\" \\\n  --create-namespace \\\n  oci:\/\/ghcr.io\/actions\/actions-runner-controller-charts\/gha-runner-scale-set-controller<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This command installs the latest ARC version. You can specify a version if needed using <code>--version<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Set Up a Runner Scale Set<\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Next, configure the runners. Here\u2019s where you define how many runners to create and connect them to your GitHub repository or organization:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">INSTALLATION_NAME=\"arc-runner-set\"\nNAMESPACE=\"arc-runners\"\nGITHUB_CONFIG_URL=\"https:\/\/github.com\/&lt;your_account&gt;\/&lt;your_repo&gt;\"\nGITHUB_PAT=\"&lt;your_pat&gt;\"\n\n\nhelm install \"${INSTALLATION_NAME}\" \\\n  --namespace \"${NAMESPACE}\" \\\n  --create-namespace \\\n  --set githubConfigUrl=\"${GITHUB_CONFIG_URL}\" \\\n  --set githubConfigSecret.github_token=\"${GITHUB_PAT}\" \\\n  oci:\/\/ghcr.io\/actions\/actions-runner-controller-charts\/gha-runner-scale-set\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s what each variable means:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>INSTALLATION_NAME<\/code> will match your <code>runs-on <\/code>value in workflows.<\/li>\n\n\n\n<li><code>NAMESPACE <\/code>needs to be the namespace where you want your pods to be created.<\/li>\n\n\n\n<li><code>GITHUB_CONFIG_URL<\/code> links the runners to your GitHub repo.<\/li>\n\n\n\n<li><code>GITHUB_PAT<\/code> is your personal access token for authentication.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Once this commands run successfully, you have successfully set up the runner. If you ran into any issues with this command, feel free to check out <a href=\"https:\/\/docs.github.com\/en\/actions\/hosting-your-own-runners\/managing-self-hosted-runners-with-actions-runner-controller\/troubleshooting-actions-runner-controller-errors\" target=\"_blank\" rel=\"noopener\">this troubleshooting guide<\/a> from GitHub for Actions Runner Controller errors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, you can test it by creating an example workflow file in your repo!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Testing It All Together<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To try out the new runner, create a <code>.github\/workflows\/demo.yml<\/code> file in your repository:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">name: ARC Demo\non:\n  workflow_dispatch:\n\n\njobs:\n  Test-Runner:\n    runs-on: arc-runner-set\n    steps:\n      - run: echo \"\ud83c\udf89 Self-hosted runner is up and running!\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Make sure to set the value of <code>runs-on<\/code> as <code>arc-runner-set<\/code>, which is the name of your runner scale group.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can now trigger a workflow run to see the workflow wait for a runner instance to come up in the <code>arc-runner-set<\/code> scale group and run the workflow when it\u2019s ready.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s what the workflow run logs will look like:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/runners1-1024x522.png\" alt=\"-\" class=\"wp-image-47636\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">While the job runs, you can try running <code>kubectl get pods -n arc-runners -w<\/code> to watch the runners spin up in real time:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">gh-actions-runner-p1 git:(main) \u2717 kubectl get pods -n arc-runners -w\nNAME                                READY   STATUS              RESTARTS   AGE\narc-runner-set-cpnd4-runner-27cwp   0\/1     ContainerCreating   0          12s\narc-runner-set-cpnd4-runner-27cwp   1\/1     Running             0          15s\narc-runner-set-cpnd4-runner-27cwp   0\/1     Completed           0          29s\narc-runner-set-cpnd4-runner-27cwp   0\/1     Terminating         0          29s<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This shows that you have successfully set up the GitHub Actions runner on your UpCloud based managed Kubernetes instance!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Next Up<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In this part, you learned how to provision an UpCloud Kubernetes cluster, install the Actions Runner Controller, configure a runner scale set, and test your deployment with a sample workflow. You now have a functional self-hosted runner setup ready to execute your GitHub Actions workflows!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In the next part of this series, we\u2019ll dive into advanced configurations. You\u2019ll learn how to customize runner pods with specific resources and tools, implement network policies, and set up security contexts. Additionally, we\u2019ll cover monitoring runners using Prometheus and Grafana, discuss cost management strategies, and explore autoscaling policies to optimize performance and efficiency.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/upcloud.com\/global\/resources\/tutorials\/supercharge-your-ci-cd-deploy-lightning-fast-github-actions-runners-on-upclouds-managed-kubernetes-part-2\">Continue here for part 2!<\/a><\/p>\n","protected":false},"author":82,"featured_media":47652,"comment_status":"open","ping_status":"closed","template":"","community-category":[256,223,229],"class_list":["post-1930","tutorial","type-tutorial","status-publish","has-post-thumbnail","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/1930","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial"}],"about":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/types\/tutorial"}],"author":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/users\/82"}],"replies":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/comments?post=1930"}],"version-history":[{"count":0,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/1930\/revisions"}],"wp:attachment":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/media?parent=1930"}],"wp:term":[{"taxonomy":"community-category","embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/community-category?post=1930"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}