GitHub Actions self-hosted runners allow running GitHub Actions CI/CD workflows on your own infrastructure instead of GitHub’s shared runners. This is essential when workflows need to: access private network resources (internal databases, Docker registries, Kubernetes clusters), use specialised hardware (GPUs, large memory), comply with data residency requirements, or reduce GitHub Actions billing costs for high-volume pipelines. A self-hosted runner is a lightweight agent process that registers with GitHub, polls for workflow jobs, and executes them on the host machine. On RHEL 9, runners run as a systemd service and can be scoped to a specific repository, organisation, or enterprise. This guide covers registering and configuring a GitHub Actions self-hosted runner on RHEL 9 as a systemd service.
Prerequisites
- RHEL 9 with internet access to github.com
- GitHub repository or organisation with admin access
Step 1 — Create a Dedicated Runner User
# Run the runner under a non-root, non-privileged user
useradd --system --shell /bin/bash --create-home --home-dir /opt/actions-runner actions-runner
# If workflows need Docker, add the user to the docker group
usermod -aG docker actions-runner
Step 2 — Download the Runner Package
# In GitHub: Repository → Settings → Actions → Runners → New self-hosted runner
# Select: Linux x64 — copy the token from the setup page
su - actions-runner
mkdir -p /opt/actions-runner && cd /opt/actions-runner
# Download the latest runner (check https://github.com/actions/runner/releases for version)
RUNNER_VERSION=2.319.1
curl -fsSL "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz" | tar xz
Step 3 — Configure and Register the Runner
# Configure the runner (run as the actions-runner user)
./config.sh
--url https://github.com/myorg/myrepo
--token AABBCCDDEEFF1234
--name rhel9-runner
--labels rhel9,self-hosted,x64,docker
--unattended
--replace
Step 4 — Install as a systemd Service
# Exit back to root, install the systemd service
exit
/opt/actions-runner/svc.sh install actions-runner
/opt/actions-runner/svc.sh start
# Verify the service is running
systemctl status actions.runner.myorg-myrepo.rhel9-runner
# Enable auto-start on boot (the svc.sh install does this automatically)
systemctl enable actions.runner.myorg-myrepo.rhel9-runner
Step 5 — Use the Self-Hosted Runner in a Workflow
# .github/workflows/build.yml
name: Build on RHEL 9
on: [push]
jobs:
build:
runs-on: [self-hosted, rhel9, docker] # Match the runner labels
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Push to internal registry
run: |
docker tag myapp:${{ github.sha }} registry.internal.example.com/myapp:latest
docker push registry.internal.example.com/myapp:latest
Conclusion
GitHub Actions self-hosted runners on RHEL 9 enable CI/CD pipelines that access private infrastructure while keeping the workflow definitions in GitHub’s familiar Actions syntax. The runner label system (runs-on: [self-hosted, rhel9, docker]) allows targeting specific runner pools with specific capabilities — essential when different workflows have different requirements (some needing Docker, some needing GPU, some needing access to different network segments). For security, never run self-hosted runners with root privileges or on public repositories, as workflow files submitted by external contributors will execute on your infrastructure.
Next steps: How to Configure Jenkins Pipelines on RHEL 9, How to Configure GitLab CI/CD Pipelines on RHEL 9, and How to Install Ansible on RHEL 9.