GitHub Actions self-hosted runners let you run jobs on your own infrastructure, which is crucial for security, cost, or specific hardware needs.
Let’s get one running. We’ll use a simple Ubuntu VM for this example.
First, you need to create a repository or an organization-level runner. For a single repository, go to Settings > Actions > Runners and click Add runner. For an organization, go to Settings > Actions > Runners. You’ll see options for Linux, Windows, and macOS. Copy the setup script and the token.
Here’s the setup script you’ll typically see (simplified):
# Download the latest runner version
latest_version=$(curl -s https://api.github.com/repos/actions/runner/releases/latest | grep "tag_name" | cut -d '"' -f 4)
curl -o actions-runner-linux-x64-${latest_version}.tar.gz -L https://github.com/actions/runner/releases/download/${latest_version}/actions-runner-linux-x64-${latest_version}.tar.gz
tar xzf actions-runner-linux-x64-${latest_version}.tar.gz
# Configure the runner
./config.sh --url https://github.com/YOUR_USERNAME/YOUR_REPO --token YOUR_UNIQUE_TOKEN --labels self-hosted,linux,x64
# Install the runner as a service
sudo ./svc.sh install
sudo ./svc.sh start
Let’s break down the config.sh command:
--url: This is the GitHub repository or organization URL where the runner will register. For a repo, it’shttps://github.com/OWNER/REPO_NAME. For an org, it’shttps://github.com/OWNER.--token: This is a one-time use token generated by GitHub for registering the runner. It’s crucial for authentication.--labels: These are tags you assign to the runner. You’ll use these labels in your workflow files to specify which runner should execute a job. Common labels includeself-hosted,linux,windows,macos,gpu, etc.
After running the config.sh script, it’s highly recommended to install and start the runner as a service. This ensures that the runner automatically starts when your machine boots up and runs in the background.
# Install the runner as a service (requires sudo privileges)
sudo ./svc.sh install
# Start the runner service
sudo ./svc.sh start
# To stop the runner
sudo ./svc.sh stop
# To restart the runner
sudo ./svc.sh restart
# To uninstall the runner service
sudo ./svc.sh uninstall
Now, in your GitHub Actions workflow file (e.g., .github/workflows/main.yml), you’ll target this runner using its labels:
name: Run on Self-Hosted Runner
on: [push]
jobs:
build:
runs-on: self-hosted # This tells GitHub to use your self-hosted runner
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run a script
run: echo "Hello from my self-hosted runner!"
When a workflow job is triggered and it specifies runs-on: self-hosted, GitHub will look for an available runner with the self-hosted label that you assigned during configuration.
You can also add more specific labels. If you configured your runner with --labels self-hosted,docker, you could use runs-on: docker in your workflow to target it. This is powerful for managing pools of runners with different capabilities.
The runner daemon listens for jobs from GitHub. When a job is assigned to it, it downloads the job’s instructions, executes them in the specified environment, and sends the output back to GitHub. The runner service itself runs as a background process, managed by systemd on most Linux systems.
The most surprising thing is how easily the runner can be managed by systemd using the provided svc.sh script. You don’t need to manually craft systemd unit files; the script handles the installation and management of the service for you. This makes it incredibly robust for long-running services.
The next step is to explore how to manage multiple self-hosted runners and use labels to route specific jobs to them.