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.