๐ 1. Introduction
GitHub Actions self-hosted runners let you run CI/CD jobs on your own machines instead of GitHub-hosted ones.
Using Ansible, you can automate the installation, registration, and management of runners across multiple serversโsecurely and repeatably.
This tutorial covers:
- What GitHub Runners are
- Why use Ansible to manage them
- Installation prerequisites
- Step-by-step setup (two methods)
- Real Ansible playbooks (systemd and Docker)
- Troubleshooting and best practices
๐ง 2. What is a GitHub Runner?
A GitHub Actions Runner is an agent process that runs your CI/CD jobs from GitHub.
When a workflow is triggered, GitHub dispatches jobs to available runners.
You can host runners:
- GitHub-Hosted: Managed by GitHub (default)
- Self-Hosted: You install and manage them (ideal for custom hardware, private environments, or cost control)
โ๏ธ 3. Why Use Ansible for Runners
Manually registering and configuring self-hosted runners is tedious and error-prone.
Ansible simplifies this by:
โ
Automating installation & upgrades
โ
Managing multiple runners or servers
โ
Handling registration tokens & environment variables securely
โ
Supporting repeatable, idempotent deployments
โ
Integrating easily with CI/CD pipelines or infrastructure as code
๐งฉ 4. Architecture Options
You have two main ways to deploy runners:
| Type | Managed By | Suitable For | Isolation | Example Role |
|---|---|---|---|---|
| Systemd (Host) | OS service | Long-lived runners | Shared | MonolithProjects |
| Containerized (Docker) | Docker containers | Ephemeral/Scalable runners | High | compscidr |
๐งฐ 5. Prerequisites
GitHub Personal Access Token
Create a token (PAT) with the repo and admin:org or workflow scopes depending on whether itโs repo- or org-level.
GitHub โ Settings โ Developer Settings โ Personal Access Tokens โ โFine-grained tokensโ or โClassicโ โ Enable scopes:
repoadmin:orgworkflow
Export it on your control node:
export GH_PAT="ghp_xxxxxxxxxxxxxxxxxxxxx"
Code language: JavaScript (javascript)
Ansible Installed
pip install ansible
Target Hosts
- RHEL / CentOS / Ubuntu with Python installed
- SSH access from control node
๐งฑ 6. Option A: Systemd Runners (Recommended for VM/Server)
Weโll use MonolithProjects/ansible-github_actions_runner.
Step 1: Install the Role
ansible-galaxy role install git+https://github.com/MonolithProjects/ansible-github_actions_runner.git,1.21.1
Code language: JavaScript (javascript)
or use requirements.yml:
---
roles:
- name: monolithprojects.github_actions_runner
src: https://github.com/MonolithProjects/ansible-github_actions_runner
version: "1.21.1"
Code language: JavaScript (javascript)
then run:
ansible-galaxy install -r requirements.yml
Code language: CSS (css)
Step 2: Create Your Inventory
inventory.ini
[github_runners]
runner1 ansible_host=192.168.56.10 ansible_user=ec2-user
Step 3: Write the Playbook
install_runner.yml
- name: Install GitHub Runner via Ansible (Systemd)
hosts: github_runners
become: true
vars:
github_account: "my-org-or-user"
github_repo: "my-repo" # optional if org-level
runner_user: "runner"
runner_labels:
- linux
- ansible
runner_state: present
github_token: "{{ lookup('env','GH_PAT') }}" # use env variable
roles:
- monolithprojects.github_actions_runner
Code language: PHP (php)
Step 4: Run the Playbook
ansible-playbook -i inventory.ini install_runner.yml
Code language: CSS (css)
โ
This installs, registers, and enables the runner as a systemd service.
Check the service:
sudo systemctl status actions.runner*
Code language: CSS (css)
Step 5: Verify on GitHub
Go to
Repo โ Settings โ Actions โ Runners
โ Youโll see the new runner with your label and status = online.
๐ณ 7. Option B: Dockerized Runners (for High Density & Isolation)
Weโll use compscidr/ansible-github-runner.
Step 1: Install Role
ansible-galaxy role install compscidr.github_runner
Code language: CSS (css)
Make sure Docker is available on your hosts:
ansible -m apt -a "name=docker.io state=present" all
Code language: JavaScript (javascript)
Step 2: Create Inventory
inventory.ini
[docker_runner_hosts]
runner2 ansible_host=192.168.56.11 ansible_user=ubuntu
Step 3: Playbook
docker_runner.yml
- name: Install Dockerized GitHub Runner
hosts: docker_runner_hosts
become: true
vars:
github_owner: "my-org-or-user"
github_repo: "my-repo"
github_token: "{{ lookup('env','GH_PAT') }}"
github_runner_count: 3 # creates 3 runners (containers)
github_runner_labels:
- docker
- self-hosted
github_runner_image: "ghcr.io/myoung34/github-runner:latest"
github_runner_workdir: "/tmp/runner"
roles:
- compscidr.github_runner
Code language: PHP (php)
Step 4: Run Playbook
ansible-playbook -i inventory.ini docker_runner.yml
Code language: CSS (css)
This will:
- Pull the container image
- Register each container runner with GitHub
- Start them automatically via Docker
Step 5: Validate
List containers:
docker ps
Check runners online at
GitHub โ Settings โ Actions โ Runners
๐งฐ 8. Option C: Minimal Role (rolehippie)
Use if you want lightweight manual control.
ansible-galaxy role install rolehippie.github_runner
Code language: CSS (css)
Playbook
- name: Minimal GitHub Runner Setup
hosts: all
become: true
vars:
runner_workdir: /opt/actions-runner
runner_labels: ["linux","self-hosted"]
runner_repo: "my-org-or-user/my-repo"
runner_token: "{{ lookup('env','RUNNER_REG_TOKEN') }}"
roles:
- rolehippie.github_runner
Code language: JavaScript (javascript)
Note: You must manually fetch a runner registration token using the GitHub REST API or UI before running this playbook.
๐งช 9. Validating the Runner
Once playbooks finish:
- Log in to your GitHub repo/org
โ Settings โ Actions โ Runners - Verify status shows โOnlineโ
- Run a test workflow using your label:
name: Test Runner
on: [push]
jobs:
test:
runs-on: [self-hosted, ansible]
steps:
- run: echo "Hello from self-hosted runner!"
Code language: PHP (php)
โก 10. Updating or Removing a Runner
To remove:
runner_state: absent
Code language: HTTP (http)
Then re-run your playbook โ it unregisters and stops the service.
๐งญ 11. Troubleshooting
| Problem | Possible Fix |
|---|---|
| Runner not appearing in GitHub | Check token validity and scopes |
| Runner shows offline | Check network and systemd logs |
| Docker containers exit quickly | Verify PAT and environment variables |
| โPermission deniedโ | Ensure become: true or correct user privileges |
| Multiple runners overwrite each other | Use unique names or containerized approach |
๐ 12. Best Practices
โ
Use Personal Access Tokens (PAT), not registration tokens in playbooks
โ
Store tokens in Ansible Vault (ansible-vault encrypt_string)
โ
Use labels to route jobs cleanly
โ
Pin runner versions for reproducibility
โ
Use Dockerized runners for ephemeral workloads
โ
Rotate tokens periodically
๐ 13. Summary
| Deployment Type | Best For | Example Role |
|---|---|---|
| Systemd Runner | Long-lived VM runners | MonolithProjects |
| Dockerized Runner | Scalable, short-lived runners | compscidr |
| Minimal DIY | Custom/light setups | rolehippie |
๐ 14. References
- ๐ MonolithProjects GitHub Runner Role
- ๐ compscidr GitHub Runner Role
- ๐ rolehippie GitHub Runner Role
- ๐ GitHub Actions Self-Hosted Runner Docs
โ
In summary:
If youโre new to self-hosted runnersโstart with MonolithProjects (simple and reliable).
If you need scale and isolationโuse compscidr (Dockerized).
For ultimate control and minimal overheadโrolehippie is fine for tinkering.
I’m Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms.
I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.
Find Trusted Cardiac Hospitals
Compare heart hospitals by city and services โ all in one place.
Explore Hospitals
This article delivers a clear and practical explanation of how to use Ansible to set up GitHub self-hosted runners โ automating what would otherwise be a tedious, manual process. I like how it outlines each step and the configuration required, making it much easier for DevOps teams to replicate across multiple hosts. The guide makes a strong case for why using infrastructure-as-code tools like Ansible boosts consistency, repeatability, and reduces the risk of human error in CI/CD setups. For anyone managing build automation or custom deployment pipelines, this post is a valuable resource that can save time and improve reliability.