{"id":106189,"date":"2025-04-18T21:14:28","date_gmt":"2025-04-18T21:14:28","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=106189"},"modified":"2025-04-09T21:19:36","modified_gmt":"2025-04-09T21:19:36","slug":"introduction-to-github-actions-part-2-github-hosted-runners","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/devops\/introduction-to-github-actions-part-2-github-hosted-runners\/","title":{"rendered":"Introduction to GitHub Actions Part 2: GitHub-hosted runners"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\" id=\"h-\"><\/h1>\n\n\n\n<p>In the <a href=\"https:\/\/www.red-gate.com\/simple-talk\/featured\/introduction-to-github-actions-part-1-getting-started\/\">first article of this series<\/a>, you learned how to set up a GitHub Actions Workflow in a GitHub repository. Like learning many languages and technologies, we started out with code that printed a \u201cHello, World!\u201d message. This process verifies that it\u2019s possible to run a basic script.<\/p>\n\n\n\n<p>The \u201cHello, World\u201d script ran on a Linux VM provided by GitHub. Workflow code must run on a server, called a <em>Runner<\/em>. In this article, you\u2019ll learn about the many options for using a GitHub-hosted runner, including Docker.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-github-runner-options\">GitHub runner options<\/h2>\n\n\n\n<p>There are two main options for runners: self-hosted and GitHub-hosted. Each option has its benefits and drawbacks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-github-hosted-runners\">GitHub-hosted runners<\/h3>\n\n\n\n<p>When running your workflow on a GitHub-hosted runner, you have the choice of Linux, Windows, and macOS. See this <a href=\"https:\/\/docs.github.com\/en\/actions\/using-github-hosted-runners\/using-github-hosted-runners\/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories\">documentation page<\/a> to see the list of supported operating systems.<\/p>\n\n\n\n<p>The GitHub-hosted runners are inexpensive to use, and typically free when using public repositories. This is why we left our test repository public). See <a href=\"https:\/\/docs.github.com\/en\/billing\/managing-billing-for-your-products\/managing-billing-for-github-actions\/about-billing-for-github-actions\">this GitHub docs page<\/a> for more information about billing when using private repos. Be sure to review the options, as different types of accounts have different time limits, measured in minutes per month. Linux runners are the least expensive to run.<\/p>\n\n\n\n<p>The other benefit is that GitHub manages the runners for you. You do not need to worry about upgrades or patching, including the software installed on the images.<\/p>\n\n\n\n<p>The downside is that you may need to install some tools during the run. Thankfully, there are many utilities installed by default. If you need something specific that\u2019s not installed, such as <a href=\"https:\/\/www.red-gate.com\/products\/flyway\/community\/\">Flyway<\/a>, you can always create a Docker image with everything you need and just run the workflow on your Docker container.<\/p>\n\n\n\n<p>GitHub-hosted runners live in the cloud, so any resources needed, such as a database server must be available to the runner. That means you may need to open certain ports or create network security groups, but it might not be possible to use GitHub-hosted runners for certain workloads, such as deploying database code.<\/p>\n\n\n\n<p>There are also <a href=\"https:\/\/docs.github.com\/en\/actions\/using-github-hosted-runners\/using-larger-runners\/about-larger-runners\">larger VMs available<\/a> with advanced features if you have specific workloads or use cases that require more resources.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-self-hosted-runners\">Self-Hosted runners<\/h3>\n\n\n\n<p>The other option is using virtual machines that you maintain either in a data center or in the cloud provider of your choice. The benefit is that you can tailor the operating system and tools to your exact specifications. Working through security requirements for accessing databases or other resources should be much easier than by using the GitHub-hosted runners.<\/p>\n\n\n\n<p>The main downside of using your own runners is that you will need to maintain them and the software that is installed. You will most likely pay for licensing and storage of the VMs, but GitHub doesn\u2019t charge you for the minutes that they run.<\/p>\n\n\n\n<p>The next article in the series will cover self-hosted runners in detail.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-specifying-a-runner\">Specifying a runner<\/h2>\n\n\n\n<p>The first article demonstrated how to run this \u201cHello, World\u201d workflow:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block\" highlight=\"false\" decode=\"true\">name: Hello, World\n\n# Controls when the workflow will run, in this case manually\non: workflow_dispatch\njobs:\n  PrintHelloWorld:\n    name: Print Hello, World!\n    runs-on: ubuntu-latest\n    steps:\n    - run: echo \"Hello, World!\"<\/pre><\/div>\n\n\n\n<p>In the <strong>jobs:<\/strong> section, you can specify one or more jobs and one or more steps in each job. In this case, there is one job with one step. Notice the <strong>runs-on:<\/strong> code in the job specifying <code>ubuntu-latest<\/code>. This line in the code tells GitHub Actions to spin up a VM for running this workflow.<\/p>\n\n\n\n<p>Instead of running on the latest version of ubuntu, you could run the job on any supported version of Ubuntu, Windows or, macOS. (See the <a href=\"https:\/\/docs.github.com\/en\/actions\/using-github-hosted-runners\/using-github-hosted-runners\/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories\">documentation<\/a> for the list of possible operating systems).<\/p>\n\n\n\n<p>Notice that the runner is specified at the job level. The same runner will be used for all steps in the job, but a different runner will be created for the next job if there is one.<\/p>\n\n\n\n<p>This example saves \u201cHello, World\u201d to a file instead of displaying it. The next step runs a directory listing. Finally, a new job also runs a listing. You can find the code for this article <a href=\"https:\/\/github.com\/KathiKellenberger\/GitHubActionsExample-Part2\">here<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block\" highlight=\"false\" decode=\"true\">name: Hello, World \n# Controls when the workflow will run, in this case manually\non: workflow_dispatch\n\njobs:\n  PrintHelloWorld:\n    name: Print Hello, World!\n    runs-on: ubuntu-latest\n    steps:\n    - run: echo \"Hello, World!\" &gt; file.txt\n    - run: ls\n  PrintDirectory:\n    name: Print directory\n    needs: PrintHelloWorld\n    runs-on: ubuntu-latest\n    steps:\n    - run: ls<\/pre><\/div>\n\n\n\n<p>Note that you can edit the script locally in VS Code and then commit\/push it to the repo. It\u2019s also possible to edit the file directly in GitHub.<\/p>\n\n\n\n<p>After running the workflow and clicking on the run, you\u2019ll see the two jobs:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"682\" height=\"217\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-screen-description-aut.png\" alt=\"A screenshot of a computer screen\n\nDescription automatically generated\" class=\"wp-image-106190\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-screen-description-aut.png 682w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-screen-description-aut-300x95.png 300w\" sizes=\"auto, (max-width: 682px) 100vw, 682px\" \/><\/figure>\n\n\n\n<p>\n  \n<\/p>\n\n\n\n<p><strong>Image 1: Job steps<\/strong><\/p>\n\n\n\n<p>Notice that there is also a dependency created by the <strong>needs:<\/strong> statement. <code>Print Directory<\/code> cannot run unless <code>Print Hello, World!<\/code> is successful.<\/p>\n\n\n\n<p>Drilling down into the first job, you\u2019ll see that the file was created.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"769\" height=\"499\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" class=\"wp-image-106191\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica.png 769w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-300x195.png 300w\" sizes=\"auto, (max-width: 769px) 100vw, 769px\" \/><\/figure>\n\n\n\n<p>\n  \n<\/p>\n\n\n\n<p><strong>Image 2: The file was created<\/strong><\/p>\n\n\n\n<p>Notice that the second job doesn\u2019t return the file because this job is running in a new VM.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"645\" height=\"442\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-1.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" class=\"wp-image-106192\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-1.png 645w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-1-300x206.png 300w\" sizes=\"auto, (max-width: 645px) 100vw, 645px\" \/><\/figure>\n\n\n\n<p>\n  \n<\/p>\n\n\n\n<p><strong>Image 3: The second job uses a new VM<\/strong><\/p>\n\n\n\n<p>You can even use different operating systems for specific jobs if you have the need.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-installing-tools-on-a-github-hosted-runner\">Installing tools on a GitHub-hosted runner<\/h2>\n\n\n\n<p>As mentioned, there are <a href=\"https:\/\/github.com\/actions\/runner-images#available-images\">utilities already available on GitHub-hosted runners<\/a>, but you may not have everything you need. For example, you may want to run something like Flyway Community to deploy database changes. You can install tools by running commands as job steps.<\/p>\n\n\n\n<p>This new workflow installs Flyway Community (<a href=\"https:\/\/documentation.red-gate.com\/fd\/command-line-277579359.html\">code from the Flyway docs<\/a>) and then tests to see that it\u2019s in place. Be sure to save the new file in the .github\\workflows folder and use the yml or yaml extension.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block\" highlight=\"false\" decode=\"true\">name: Install Flyway\non: workflow_dispatch\n\njobs:\n  Install:\n    name: Install Flyway\n    runs-on: ubuntu-latest\n    steps:\n      - name: Install\n        run: wget -qO- https:\/\/download.red-gate.com\/maven\/release\/com\/redgate\/flyway\/flyway-commandline\/11.1.1\/flyway-commandline-11.1.1-linux-x64.tar.gz | tar -xvz &amp;&amp; sudo ln -s `pwd`\/flyway-11.1.1\/flyway \/usr\/local\/bin\n      - name: Check Install\n        run: flyway --version<\/pre><\/div>\n\n\n\n<p>After creating and running the new workflow, you should see results similar to this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"901\" height=\"549\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-2.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" class=\"wp-image-106193\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-2.png 901w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-2-300x183.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-2-768x468.png 768w\" sizes=\"auto, (max-width: 901px) 100vw, 901px\" \/><\/figure>\n\n\n\n<p>\n  \n<\/p>\n\n\n\n<p><strong>Image 4: Installing flyway results<\/strong><\/p>\n\n\n\n<p>If you added the job steps to the same job, you could run additional Flyway commands to deploy your database code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-using-docker-on-a-github-hosted-runner\">Using Docker on a GitHub-hosted runner<\/h2>\n\n\n\n<p>Running a Docker container on the GitHub-hosted runner is a convenient option when you need software installed on the runner. You can use images managed by the software vendor or create an image yourself.<\/p>\n\n\n\n<p>NOTE: Creating Docker images is beyond the scope of this article series.<\/p>\n\n\n\n<p>For example, If you need Flyway, you can just use one of the images created by Redgate for <a href=\"https:\/\/hub.docker.com\/r\/flyway\/flyway\">Community<\/a> or <a href=\"https:\/\/hub.docker.com\/r\/redgate\/flyway\">Paid editions<\/a>.<\/p>\n\n\n\n<p>Create this new workflow that runs Flyway in a Docker container. Be sure that the file extension is yml or yaml and that you save it in .github\\workflows. Notice that the only addition to the code is the <strong>container:<\/strong> line specifying the name of the image.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block\" highlight=\"false\" decode=\"true\">name: Run Flyway on Docker\non:   workflow_dispatch\n\njobs:\n  runFlyway:\n    name: Run Flyway\n    runs-on: ubuntu-latest\n    container: flyway\/flyway\n    steps:\n    - name: runFlyway\n      run: flyway --version<\/pre><\/div>\n\n\n\n<p>After running the file, check the results. You\u2019ll see results similar to this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1020\" height=\"730\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-3.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" class=\"wp-image-106194\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-3.png 1020w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-3-300x215.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/a-screenshot-of-a-computer-description-automatica-3-768x550.png 768w\" sizes=\"auto, (max-width: 1020px) 100vw, 1020px\" \/><\/figure>\n\n\n\n<p>\n  \n<\/p>\n\n\n\n<p><strong>Image 5: The results of running Flyway in a Docker container<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion<\/h2>\n\n\n\n<p>This article covered how to use Git-Hub hosted runners in your workflows. This is the most convenient way to run workflows if the VMs will meet your security requirements. You can choose from several versions of Ubuntu or Windows, and even macOS. These VMs have utilities installed, but it\u2019s easy to install any other required tools. One of the best options is using Docker images from the software vendor.<\/p>\n\n\n\n<p>The next article will discuss self-hosted runners.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the first article of this series, you learned how to set up a GitHub Actions Workflow in a GitHub repository. Like learning many languages and technologies, we started out with code that printed a \u201cHello, World!\u201d message. This process verifies that it\u2019s possible to run a basic script. The \u201cHello, World\u201d script ran on&#8230;&hellip;<\/p>\n","protected":false},"author":110218,"featured_media":106195,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143512,53],"tags":[5970,5775,159280],"coauthors":[11292],"class_list":["post-106189","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-featured","tag-devops","tag-github","tag-kathikellenbergergithubseries"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/106189","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/110218"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=106189"}],"version-history":[{"count":1,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/106189\/revisions"}],"predecessor-version":[{"id":106196,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/106189\/revisions\/106196"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/106195"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=106189"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=106189"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=106189"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=106189"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}