GitHub Actions' hosted runners work well for most projects, but a self-hosted runner on your own VPS gives you more control — direct access to internal services, custom hardware, or simply avoiding GitHub's usage limits on private repositories.
Why Self-Host a Runner
- Deploy directly to internal/private infrastructure without exposing it publicly
- Avoid GitHub Actions minutes limits on private repos
- Custom hardware/software requirements not available on hosted runners
- Persistent caching between builds without re-downloading dependencies each time
Prerequisites
- A dedicated VPS (separate from production, ideally): 2 vCPU, 4 GB RAM minimum
- A GitHub repository or organization where you have admin access
Step 1 — Create a Dedicated User for the Runner
sudo useradd -m -s /bin/bash github-runner
sudo su - github-runner
Step 2 — Get the Runner Registration Details
In your GitHub repository: Settings → Actions → Runners → New self-hosted runner, select Linux, and copy the provided download and configuration commands (these include a repository-specific registration token).
Step 3 — Download and Extract the Runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/download/vX.X.X/actions-runner-linux-x64-X.X.X.tar.gz
tar xzf actions-runner-linux-x64.tar.gz
Use the exact version and URL provided by GitHub's setup page, since it changes with each release.
Step 4 — Configure the Runner
./config.sh --url https://github.com/your-org/your-repo --token YOUR_REGISTRATION_TOKEN
Accept the default name and labels, or customize them for your setup.
Step 5 — Install as a systemd Service
exit
cd /home/github-runner/actions-runner
sudo ./svc.sh install github-runner
sudo ./svc.sh start
Step 6 — Verify the Runner Is Online
Check Settings → Actions → Runners in your repository — it should show as "Idle" and ready.
Using the Self-Hosted Runner in a Workflow
jobs:
build:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- run: npm install
- run: npm test
Security Considerations (Critical)
Self-hosted runners execute arbitrary code from your workflows on your VPS — for public repositories, this is a significant risk, since anyone who can open a pull request can potentially run code on your runner. Only use self-hosted runners for private repositories, or with strict workflow approval requirements for public repos.
Isolating the Runner (Recommended)
Run the runner on a dedicated VPS separate from production infrastructure, or within a container/VM with limited access to sensitive systems, so a compromised workflow can't directly reach production data.
Restricting the Runner's Network Access
sudo ufw default deny outgoing
sudo ufw allow out to github.com
sudo ufw allow out 443/tcp
Adjust based on your actual deployment needs, minimizing unnecessary outbound access.
Removing the Runner
sudo ./svc.sh stop
sudo ./svc.sh uninstall
./config.sh remove --token YOUR_REMOVAL_TOKEN
Common Errors
Runner shows offline — check the service status: sudo systemctl status actions.runner.*
Workflow can't access a required tool — unlike hosted runners, self-hosted runners don't come pre-loaded with common tools; install what your workflows need directly on the VPS.
Best Practices
- Use self-hosted runners only for private/trusted repositories
- Run on infrastructure isolated from production
- Restrict outbound network access to only what's genuinely needed
Continue Reading
- How to Set Up a Deployment User with Restricted SSH Access
- CI/CD Pipeline Security Best Practices
- How to Install GitLab CE on a VPS
Browse more articles in DevOps & CI/CD.
