How to Use a Raspberry Pi as a Self-Hosted GitHub Actions Runner
GitHub Actions is a powerful CI/CD platform built into GitHub. By default, workflows run on GitHub-hosted runners in the cloud. However, you can register your own machines as self-hosted runners. A Raspberry Pi makes an excellent always-on runner for personal projects, ARM-specific builds, or workflows that need access to local hardware and services.
Prerequisites
Before you begin, ensure you have:
- A Raspberry Pi 4 or newer running Raspberry Pi OS 64-bit (arm64). The 32-bit OS is not supported by the GitHub Actions runner.
- A GitHub account and a repository where you want to use the runner.
- SSH access to your Raspberry Pi.
- A stable internet connection.
Step 1: Prepare the Raspberry Pi
Update your system packages:
sudo apt update && sudo apt upgrade -y
Install the dependencies required by the GitHub Actions runner:
sudo apt install -y curl jq libicu-dev
Optionally, create a dedicated user for the runner and add it to the docker group if you plan to use Docker-based actions:
sudo useradd -m -s /bin/bash ghrunner
sudo usermod -aG sudo ghrunner
sudo usermod -aG docker ghrunner
sudo su - ghrunner
Step 2: Get the Runner Token from GitHub
Navigate to your GitHub repository in a browser. Go to Settings > Actions > Runners > New self-hosted runner. GitHub will display a page with the download URL for the Linux ARM64 runner package and a registration token. Keep this page open.
Step 3: Download and Extract the Runner
On your Raspberry Pi, create a directory for the runner and download the ARM64 release:
mkdir -p ~/actions-runner && cd ~/actions-runner
Download the runner package (check the GitHub settings page for the latest version URL):
curl -o actions-runner-linux-arm64.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.321.0/actions-runner-linux-arm64-2.321.0.tar.gz
Extract the archive:
tar xzf actions-runner-linux-arm64.tar.gz
The version number above may change over time. Always use the version shown on your repository's runner setup page for the latest release.
Step 4: Configure the Runner
Run the configuration script using the token from your GitHub repository settings:
./config.sh \
--url https://github.com/your-username/your-repo \
--token YOUR_REGISTRATION_TOKEN
The script will ask you a few questions:
- Runner group: Press Enter to use the default.
- Runner name: Give it a descriptive name like
raspi-runner-01. - Labels: Add custom labels to target this specific runner. For example:
self-hosted,linux,arm64,raspi. The default labelsself-hosted,linux, andARM64are added automatically. - Work folder: Press Enter to accept the default
_workdirectory.
Step 5: Test the Runner
Start the runner interactively to verify it connects to GitHub:
./run.sh
You should see "Connected to GitHub" and "Listening for Jobs". Press Ctrl+C to stop once confirmed.
Step 6: Install as a systemd Service
To keep the runner running in the background and start automatically on boot, install it as a service:
cd ~/actions-runner
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status # Verify the service is running
Other useful commands: sudo ./svc.sh stop to stop and sudo ./svc.sh uninstall to remove the service.
Step 7: Write a Workflow That Uses Your Runner
In your GitHub repository, create a workflow file at .github/workflows/pi-build.yml:
name: Build on Raspberry Pi
on:
push:
branches:
- main
jobs:
build:
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: System info
run: |
echo "Architecture: $(uname -m)"
echo "Hostname: $(hostname)"
- name: Run tests
run: echo "Running tests on Raspberry Pi..."
Push this file to your repository and the workflow will run on your Raspberry Pi runner.
Targeting Specific Runners with Labels
If you have multiple self-hosted runners, you can use labels to control which runner handles a job. During setup, you assigned labels to your runner. Use them in your workflow like this:
jobs:
build:
runs-on: [self-hosted, raspi]
This ensures the job only runs on runners that have both the self-hosted and raspi labels. You can manage labels after setup through the GitHub UI at Settings > Actions > Runners, or by re-running ./config.sh with new --labels.
To make a runner available across multiple repositories, register it at the organization level instead: Organization Settings > Actions > Runners > New self-hosted runner.
Updating the Runner
The GitHub Actions runner updates itself automatically when a new version is available. If you need to manually update, download the new version and re-extract it in the same directory. The configuration is preserved.
Troubleshooting
- Runner shows as offline: Check service status with
sudo ./svc.sh status. Review logs at~/actions-runner/_diag/. - Permission denied during builds: Ensure the runner user has permissions for the directories and tools your workflow needs.
- Docker actions fail: Verify Docker is installed and the runner user is in the
dockergroup. Log out and back in after adding the group. - "Must not run with sudo": Run
config.shandrun.shas the dedicated runner user, not withsudo. - Token expired: Registration tokens expire after about one hour. Generate a new one from the GitHub settings page.
- ARM compatibility: Some Actions may not support ARM64. Use
run:steps with native commands as a fallback.
Conclusion
Your Raspberry Pi is now a self-hosted GitHub Actions runner, ready to execute CI/CD workflows whenever you push code. Combined with custom labels, you have precise control over which jobs run where across your infrastructure.