John

Senior Cloud Engineer & Technical Lead

Turning My Dusty Raspberry Pi into a Self-Hosted GitHub Actions Runner

I discovered that my Raspberry Pi had been collecting dust for months while I was getting hit with unexpected GitHub Actions billing charges. After some billing headaches with my automation workflows, I decided it was time to put that Pi to work as a self-hosted runner.

The Problem

My daily GitHub stars automation and Claude workflows were running up costs on GitHub Actions. What started as a few dollars turned into something more noticeable when I began running multiple workflows daily. The irony wasn’t lost on me - I had a perfectly capable ARM64 device sitting idle while paying for compute time in the cloud.

The billing structure for GitHub Actions can catch you off guard if you’re not careful. While public repositories get free minutes, private repositories and extensive usage can add up quickly. My automation scripts were running daily, and with multiple workflows for different projects, I was burning through the free tier faster than expected.

The Solution: Self-Hosted Runner

Setting up a self-hosted GitHub Actions runner on my Raspberry Pi turned out to be surprisingly straightforward. Here’s how I got it running:

Step 1: Check Your Architecture

First, I needed to confirm my Pi’s architecture since GitHub provides different runner binaries:

uname -m
# Output: aarch64

Perfect - I was running ARM64, which is supported by GitHub’s self-hosted runners.

Step 2: Download and Configure the Runner

I navigated to my repository settings and went to Actions > Runners > New self-hosted runner. GitHub provides architecture-specific download commands:

# Create a directory for the runner
mkdir actions-runner && cd actions-runner

# Download the latest runner package for ARM64
curl -o actions-runner-linux-arm64-2.308.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.308.0/actions-runner-linux-arm64-2.308.0.tar.gz

# Extract the installer
tar xzf ./actions-runner-linux-arm64-2.308.0.tar.gz

Step 3: Configure the Runner

The configuration process connects your Pi to your GitHub repository:

# Configure the runner (this requires a token from GitHub)
./config.sh --url https://github.com/johnoct/johnoct.github.io --token YOUR_TOKEN_HERE

# You'll be prompted for:
# - Runner name (I used "raspberry-pi-runner")
# - Runner group (default)
# - Labels (I added "self-hosted,linux,ARM64,raspberry-pi")
# - Work folder (default _work)

Step 4: Keep It Running with Screen

Since I wanted the runner to persist across SSH sessions, I used screen to keep it alive:

# Start a new screen session
screen -S github-runner

# Start the runner
./run.sh

# Detach from screen (Ctrl+A, then D)

To reattach later: screen -r github-runner

Step 5: Update Workflows to Use Self-Hosted

The final step was updating my workflow YAML files to target the self-hosted runner:

name: Daily GitHub Stars

on:
  schedule:
    - cron: '0 9 * * *'  # 9 AM UTC daily
  workflow_dispatch:

jobs:
  collect-stars:
    runs-on: self-hosted  # Changed from 'ubuntu-latest'
    steps:
      - uses: actions/checkout@v3
      - name: Run daily stars script
        run: ./scripts/daily-stars.sh

What I Discovered

The performance was actually better than expected. My Pi 4 with 8GB RAM handled the workflows without any issues. The ARM64 architecture means most standard GitHub Actions work perfectly, and the local execution is often faster than waiting for a GitHub-hosted runner to spin up.

The setup process revealed a few interesting things about GitHub’s self-hosted runner architecture. The runner periodically polls GitHub for new jobs, and when it finds one, it downloads the workflow definition and executes it in an isolated environment. Each job gets its own working directory, and the runner handles cleanup automatically.

One thing I hadn’t anticipated was how satisfying it would be to see my own hardware executing the workflows. There’s something gratifying about watching the Pi’s activity LED blink as it processes my automation tasks.

graph TD A[GitHub Repository] --> B[Workflow Triggered] B --> C[Self-hosted Runner Polls] C --> D[Raspberry Pi Executes Job] D --> E[Results Pushed Back] E --> A subgraph "Raspberry Pi" D --> F[Download Dependencies] F --> G[Run Scripts] G --> H[Generate Artifacts] end

Key Learnings

  • Self-hosted runners eliminate GitHub Actions costs - Perfect for personal projects with regular automation needs
  • ARM64 compatibility is excellent - Most standard actions work without modification
  • Screen sessions keep runners persistent - Essential for maintaining the connection across SSH sessions
  • Local execution can be faster - No wait time for runner provisioning
  • Configuration is straightforward - GitHub’s documentation and auto-generated commands make setup simple
  • Raspberry Pi 4 handles CI workloads well - Even complex automation scripts run smoothly
  • Runner management is automatic - GitHub handles job distribution and cleanup seamlessly

The biggest win was eliminating my GitHub Actions billing while actually improving performance. My daily automation now runs on dedicated hardware that I control, and I can scale it up by adding more Pis if needed. Plus, I finally found a productive use for that Pi that was gathering dust on my shelf.