Skip to main content

Self-hosted Runners:

 

Self-Hosted Runners: Complete Guide to Custom GitHub Actions Execution

📅 Published: July 2026
⏱️ Estimated Reading Time: 20 minutes
🏷️ Tags: GitHub Actions, Self-Hosted Runners, CI/CD, DevOps, Infrastructure


Introduction: What is a Self-Hosted Runner?

A self-hosted runner is a system that you deploy and manage to execute GitHub Actions workflows on your own infrastructure. Instead of using GitHub's managed runners, you run the runner application on your own machines—physical servers, virtual machines, cloud instances, or containers .

Why self-hosted runners matter:

  • Cost savings: No per-minute charges for GitHub Actions (you pay only for your infrastructure) 

  • Custom hardware: Use specific CPU architectures, GPUs, or large memory configurations 

  • Private network access: Connect to internal databases, APIs, and services 

  • Custom software: Install proprietary tools and specific software versions 

  • No job time limits: Jobs can run as long as needed 

  • Control over updates: Choose when to upgrade OS and tools 


Part 1: Self-Hosted vs GitHub-Hosted Runners

FeatureGitHub-HostedSelf-Hosted
Setup & maintenanceGitHub manages everythingYou install, configure, and maintain 
ScalabilityAutoscales dynamicallyManual provisioning required 
SecurityFresh environment per jobRequires manual hardening 
CustomizationLimited to preinstalled toolsFully customizable 
PerformanceStandard computeCan use high-performance hardware 
State persistenceResets after every jobCan persist data between jobs 
CostFree minutes then per-minuteNo GitHub costs, but infrastructure investment required 
Network accessNo direct internal accessCan access private networks 
Clean instanceYes, every jobNo, state persists 
Automatic updatesOS, tools, and runner appRunner app only; you manage OS and software 

When to use GitHub-hosted runners :

  • Quick setup without infrastructure management

  • Workflows don't need custom dependencies

  • Open source or public repositories

When to use self-hosted runners :

  • Custom dependencies or persistent state required

  • Private network access needed

  • High-performance hardware required

  • Cost optimization for high-volume workflows


Part 2: Setting Up a Self-Hosted Runner

Prerequisites 

  • Any machine (physical, virtual, or container)

  • Must be able to communicate with GitHub

  • Enough hardware resources for your workflows

  • Linux machines require Docker installed for container actions

  • Administrative access on the machine

  • GitHub repository or organization admin access

Step 1: Prepare the Machine

Linux (Ubuntu) :

bash
sudo apt update
sudo apt upgrade -y
sudo apt install -y curl tar git

Create a directory for the runner :

bash
mkdir actions-runner && cd actions-runner

Step 2: Download and Extract the Runner 

bash
# Replace X.Y.Z with latest version
curl -o actions-runner-linux-x64-X.Y.Z.tar.gz -L \
  https://github.com/actions/runner/releases/download/vX.Y.Z/actions-runner-linux-x64-X.Y.Z.tar.gz

tar xzf ./actions-runner-linux-x64-X.Y.Z.tar.gz

Step 3: Obtain a Runner Token 

  1. Go to your repository → SettingsActionsRunners

  2. Click New self-hosted runner

  3. Copy the configuration command provided (contains your token)

Step 4: Configure the Runner 

bash
./config.sh --url https://github.com/yourusername/yourrepository --token YOUR_TOKEN --labels self-hosted,my-runner

During configuration, you'll be prompted to:

  • Enter runner name (press Enter for default)

  • Enter runner group (press Enter for default)

  • Add additional labels (optional)

Step 5: Start the Runner 

Interactive mode:

bash
./run.sh

As a service (recommended for production) :

bash
sudo ./svc.sh install
sudo ./svc.sh start

Step 6: Verify Online Status 

Check in GitHub: Repository → Settings → Actions → Runners

  • 🟢 Online → runner is active

  • 🔴 Offline → runner is not running

Step 7: Test with a Workflow 

Create .github/workflows/test-runner.yml:

yaml
name: Self-Hosted Runner Test

on: [push]

jobs:
  test:
    runs-on: self-hosted
    steps:
      - name: Print message
        run: echo "Self-hosted runner is working!"

Part 3: Runner Organization and Labels

Scopes 

Self-hosted runners can be registered at three levels:

ScopeAccessUse Case
RepositorySingle repository onlyDevelopment/testing
OrganizationAll repositories in orgShared team resources
EnterpriseAll orgs in enterpriseCentralized management

Labels for Targeted Execution 

Labels allow workflows to select specific runners:

yaml
jobs:
  build:
    runs-on: [self-hosted, linux, x64, gpu]
    steps:
      - name: Build with GPU
        run: ./build-with-cuda.sh

Labeling during configuration:

bash
./config.sh --labels self-hosted,linux,production,large

Example with multiple runners :

  • Runner 1: linux, ubuntu, medium

  • Runner 2: linux, ubuntu, large

Workflow runs-on: [self-hosted, ubuntu] can run on either, while runs-on: [self-hosted, ubuntu, large] only runs on Runner 2.


Part 4: Running Runners at Scale

Deployment Options 

Option 1: EC2 Auto Scaling Groups

  • Simple to deploy and understand

  • Use spot instances for cost savings

  • Auto-scaling based on CPU utilization

Option 2: Kubernetes (Recommended) 

  • Use actions-runner-controller

  • Advanced horizontal scaling with HorizontalRunnerAutoscaler

  • Scale to zero when idle

  • Custom runner images without rebuilding AMIs

Option 3: Docker Compose 

yaml
version: '3.8'
services:
  github-runner-1:
    build: .
    environment:
      - GITHUB_ORG_TOKEN=${GITHUB_ORG_TOKEN}
      - GITHUB_URL=${GITHUB_URL}
      - RUNNER_LABELS=docker,self-hosted,linux
    mem_limit: 2G
    cpus: 2.0

Containerized Runners Setup 

Environment file (.env) :

text
GITHUB_ORG_TOKEN=ghp_your_actual_token_here
GITHUB_URL=https://github.com/YOUR-ORG
RUNNER_LABELS=docker,self-hosted,linux,production
MEMORY_LIMIT=2G
CPU_LIMIT=2.0

Management commands:

bash
# Start runners
docker-compose up -d

# Check status
docker-compose ps

# View logs
docker-compose logs -f github-runner-1

Part 5: Security Best Practices

Critical Security Warning

⚠️ Never use self-hosted runners with public repositories. This creates a serious security vulnerability where malicious actors could execute code on your infrastructure through pull requests .

Security Model 

When a workflow runs on your self-hosted runner, it can:

  • Execute arbitrary code as the runner user

  • Access the Docker socket to run any container

  • Access the host network

  • Read/write files in the runner's workspace

Workflows CANNOT (by default):

  • Access files outside the runner's home directory

  • Execute as root (unless explicitly granted)

1. Restrict Runner Access 

  • Only run workflows from trusted repositories

  • Require manual approval for external PRs

  • Use organization-level runners with strict scoping

  • Never allow untagged jobs (run_untagged=false)

2. Use Dedicated Runner Infrastructure 

  • BAD: Run runners on production database servers

  • GOOD: Isolated VMs or bare metal for runner fleet

3. Network Security 

bash
# Outbound: Allow HTTPS to GitHub
# Inbound: No ports need to be exposed (runners poll GitHub)

sudo ufw default deny incoming
sudo ufw allow out 443/tcp
sudo ufw enable

Consider:

  • Network segmentation (separate VLAN for runners)

  • Egress filtering to block access to internal services

  • Logging network connections for audit

4. Docker Socket Exposure 

  • AVOID: Mounting Docker socket unnecessarily

  • BETTER: Use specialized runners for Docker builds

yaml
# For runners that need Docker:
runs-on: [self-hosted, linux, docker]

5. Secrets Management 

  • ✅ Use GitHub secrets for sensitive data

  • ❌ Never hardcode secrets in workflows

  • ✅ Use environment-specific secrets

  • ✅ Rotate secrets regularly

6. Container Image Trust 

yaml
# ✅ GOOD: Pin exact versions
container:
  image: node:20.11.0

# ❌ BAD: Using latest
container:
  image: node:latest

7. Resource Limits 

Configure resource limits to prevent resource exhaustion:

yaml
sizes:
  medium:
    cpus: 6.0         # CPU quota (6 cores)
    mem_limit: "16g"  # Memory limit
    pids_limit: 4096  # Max processes

8. Ephemeral Runners 

For maximum security, use ephemeral runners that are destroyed after each job. This prevents persistent threats and ensures a clean environment for every run.

Key consideration: Ephemeral runners require more infrastructure overhead but provide better security isolation .

9. Monitoring and Auditing 

View runner logs:

bash
sudo journalctl -u gha-* -f

Monitor for:

  • Failed login attempts

  • Unusual network connections

  • Resource usage spikes

  • Container activity (docker ps, docker logs)

10. Regular Updates 

bash
# Update runner binaries
# Update version in config, then re-deploy

# Update host OS
sudo apt update && sudo apt upgrade -y

# Update container images
docker pull node:20

Schedule:

  • Weekly: Review security advisories

  • Monthly: Update runner binaries, OS patches

  • Quarterly: Audit runner configurations, review access


Part 6: Real-World Experiences

The Guardian's Migration 

Why they moved from GitHub-hosted runners:

  • Cost: iOS builds required macOS runners with 10x minute multiplier

  • Timeouts: Builds sometimes ran frustratingly slowly

  • Unpredictable changes: GitHub runner image updates occasionally caused extreme slowdowns

Results:

  • ⚡ Unit tests ran 50% faster

  • ⚡ Upload builds were 60% faster

  • 💰 Monthly bill dropped by roughly £400

  • 🔧 More control over OS upgrades

Challenges faced:

  • Non-ephemeral runners meant old job data could stick around

  • Needed cleanup strategies for DerivedData folders

  • Required physical hardware maintenance

  • Concurrency management needed

Learnings:

  • Bigger is better: easier to manage one powerful machine than a fleet

  • Always-on power supply and remote access are essential

  • Plan for proper cleanup routines


Quick Reference

bash
# Setup commands
mkdir actions-runner && cd actions-runner
curl -o runner.tar.gz -L https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64.tar.gz
tar xzf runner.tar.gz
./config.sh --url https://github.com/ORG/REPO --token YOUR_TOKEN
./run.sh

# Service management
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh stop
sudo ./svc.sh status

# View logs
sudo journalctl -u actions.runner.* -f

Summary

Self-hosted runners give you complete control over your CI/CD execution environment. They offer significant cost savings for high-volume workflows, enable access to private networks, and allow custom hardware configurations. However, they come with increased maintenance responsibilities and security considerations.

AspectKey Points
When to useCost optimization, custom hardware, private network access
SecurityNever use with public repos, restrict access, audit regularly
MaintenanceOS updates, runner updates, disk space monitoring
ScalingUse Kubernetes or EC2 ASG for production fleets

Learn More

Practice self-hosted runner setup with hands-on exercises in our interactive labs:
https://devops.trainwithsky.com/

Comments

Popular posts from this blog

🌐 Holographic Communications & 6G: The Future of Immersive Connectivity

  🌐 Holographic Communications & 6G: The Future of Immersive Connectivity 🚀 Introduction As the world moves towards 6G , a revolutionary technology is set to redefine digital interactions: Holographic Communications . Imagine real-time, 3D holographic video calls, immersive remote collaboration, and lifelike virtual experiences —all powered by ultra-fast, ultra-low-latency 6G networks . This topic explores Holographic Communications , its impact on various industries, key enabling technologies, and how 6G will bring this futuristic concept to reality . Shape Your Future with AI & Infinite Knowledge...!! Want to Generate Text-to-Voice, Images & Videos? http://www.ai.skyinfinitetech.com Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! 🔍 1. What is Holographic Communication? Hologr...

How to Use SKY TTS: The Complete, Step-by-Step Guide for 2025

 What is SKY TTS? SKY TTS  is a free, next-generation  AI audio creation platform  that brings together high-quality  Text-to-Speech ,  Speech-to-Text , and a full suite of professional  audio editing tools  in one seamless experience. Our vision is simple — to make advanced audio technology  free, accessible, and effortless  for everyone. From creators and educators to podcasters, developers, and businesses, SKY TTS helps users produce  studio-grade voice content  without expensive software or technical skills. With support for  70+ languages, natural voices, audio enhancement, waveform generation, and batch automation , SKY TTS has become a trusted all-in-one toolkit for modern digital audio workflows. Why Choose SKY TTS? Instant Conversion:  Enjoy rapid text-to-speech generation, even with large documents. Advanced Voice Settings:   Adjust speed, pitch, and style for a personalized listening experience. Multi-...

📊 Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd

  Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd Monitoring and logging are essential for maintaining a healthy and well-performing Kubernetes cluster. In this guide, we’ll cover why monitoring is important, key monitoring tools like Prometheus and Grafana, and logging tools like Fluentd to help you gain visibility into your cluster’s performance and logs. Shape Your Future with AI & Infinite Knowledge...!! Want to Generate Text-to-Voice, Images & Videos? http://www.ai.skyinfinitetech.com Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! 🚀 Introduction In today’s fast-paced cloud-native environment, Kubernetes has emerged as the de-facto container orchestration platform. But deploying and managing applications in Kubernetes is just half the ba...