GitHub-hosted runners are convenient until the bill or the wait time starts to hurt. Past the free tier you pay per minute, cold-start every job, and re-download dependencies each run. A self-hosted runner on your own VPS flips all three: flat monthly cost, a warm machine with your caches already on disk, and a build environment you fully control — specific tool versions, more RAM, Docker layer cache that actually persists.
A runner only needs to reach GitHub outbound, so it works on our cheapest plans, pays in crypto, and needs no KYC.
What a runner needs
- CPU and RAM for your builds. Small ($8/mo — 4 vCPU, 4 GB RAM, 35 GB NVMe) is the comfortable default for most CI: Node/Go/Rust builds, test suites, Docker image builds. Heavy compilation or parallel jobs? Step up to Medium or a Pro plan.
- NVMe disk for caches. The whole point of self-hosting is persistence — dependency caches, Docker layers, build artifacts stay on disk between runs. NVMe keeps restores fast.
- No dedicated IP required. The runner dials out to GitHub over HTTPS; nothing needs to reach it inbound. A NAT plan (from $3/mo) is enough. Take a dedicated IP only if you also self-host something that serves traffic.
Set up a runner (Ubuntu 24.04)
Create the runner in your repo (or org): Settings → Actions → Runners → New self-hosted runner → Linux. GitHub shows a download command and a one-time registration token. On the VPS:
# as a non-root user (the runner refuses to run as root)
adduser --disabled-password --gecos "" runner
su - runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64.tar.gz -L \
https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64.tar.gz
tar xzf actions-runner-linux-x64.tar.gz
# register with the URL + token from the GitHub UI
./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO --token YOUR_TOKEN
Keep it running as a service
Don't run ./run.sh in a terminal — install it as a systemd service so it survives reboots:
sudo ./svc.sh install runner
sudo ./svc.sh start
sudo ./svc.sh status
The runner now appears as Idle in the GitHub UI and picks up any job that targets it.
Use it from a workflow
Point a job at your runner with runs-on:
jobs:
build:
runs-on: self-hosted # or a custom label you set at registration
steps:
- uses: actions/checkout@v4
- run: make build && make test
Docker-based jobs
Install Docker once and your workflows can build images or run service containers, with layer cache persisting between runs:
sudo apt update && sudo apt install -y docker.io
sudo usermod -aG docker runner # let the runner use Docker without sudo
For throwaway per-job isolation, run the runner itself inside a container and recreate it each run — a common pattern for untrusted or matrix builds.
Why EQVPS for CI
- Flat cost, unlimited minutes. No per-minute metering — a busy pipeline costs the same as an idle one.
- Warm caches. Dependencies and Docker layers stay on NVMe between runs; builds get faster, not slower.
- Root in ~60 seconds, clean images. Ubuntu, Debian and more via cloud-init; install exactly the toolchain you need.
- No KYC, crypto payment. Email to sign up, USDC/USDT to pay. Spin up extra runners for release crunches and cancel them after — unused paid time is refunded to your balance.
Comments
No comments yet. Be the first.