A self-hosted GitHub Actions runner is a machine you control that executes workflows from your GitHub repository. Instead of using GitHub’s hosted runners, you run jobs on your own hardware inside your homelab or private network. This matters for cost, speed, access to local resources, and privacy-sensitive workloads.
In this guide, we set up a self-hosted runner on a Linux machine, connect it to a GitHub repository, and run a workflow that deploys to your homelab.
What a Self-Hosted Runner Does
When you push code or open a pull request, GitHub can send jobs to your runner instead of GitHub’s cloud runners. Your runner executes the steps defined in the workflow YAML and reports results back to GitHub.
Self-hosted runners are useful for homelab deployments, builds that need access to local Docker registries, tests that require internal network access, or workloads with custom licensing requirements.
Requirements
- A Linux machine on your homelab: Ubuntu, Debian, or any systemd-based distro works
- GitHub repository with admin or write access
- Port 443 outbound to github.com
- Static IP or reliable DNS for the runner machine
Create the Runner
Go to your repository or organization settings, then Actions > Runners > New self-hosted runner. Choose Linux, then copy the download and configure commands shown on the page.
On your homelab machine:
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.317.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.317.0/actions-runner-linux-x64-2.317.0.tar.gz
tar xzf actions-runner-linux-x64-2.317.0.tar.gz
Configure and run:
./config.sh --url https://github.com/your-username/your-repo --token YOUR_TOKEN
./run.sh
Keep the process running. The easiest production method is a systemd service.
Run as a systemd Service
Create /etc/systemd/system/github-runner.service:
[Unit]
Description=GitHub Actions Self-Hosted Runner
After=network.target
[Service]
Type=simple
User=runner
WorkingDirectory=/home/runner/actions-runner
ExecStart=/home/runner/actions-runner/run.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now github-runner
Workflow Example
Create .github/workflows/deploy.yml in your repository:
name: Deploy to Homelab
on:
push:
branches: [main]
jobs:
deploy:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- run: docker compose -f /opt/myapp/docker-compose.yml up -d --build
When you push to main, the job runs on your runner and deploys directly to your homelab.
Security Considerations
A self-hosted runner has access to your machine and network. Treat it like a privileged account.
- Run the runner under a dedicated user, not root
- Restrict network access with a firewall
- Use secrets for credentials instead of hardcoding them
- Keep the runner OS and packages updated
- Avoid using runners on machines that store sensitive data unless isolated
For public repositories, prefer organization-scoped runners over repository-scoped runners so contributors cannot request jobs on your hardware without approval.
Scaling to Multiple Runners
If you have multiple homelab servers, add a runner on each machine. Use labels in your workflow to target specific runners:
runs-on: [self-hosted, linux, proxmox]
Labels let you route database migrations to a database server, container builds to a builder machine, and production deployments to a specific target.
Final Thoughts
Self-hosted GitHub Actions runners are one of the most practical additions to a homelab. They connect your code repository directly to your infrastructure, reduce dependency on cloud runners, and give you full control over execution environment.
If you want to expand your automation setup, read our guides on Ansible homelab automation, self-hosted monitoring stack, and home network segmentation with VLANs.