More Control with a Self-Hosted CI Runner
Set up your own build runner on a VPS to control the environment, cache, and cost of your CI pipeline.
Netbay Infrastructure Team
Netbay Engineering
On this page
Hosted CI runners are convenient, but they come with trade-offs. You share compute with other tenants, you ship your source and secrets to a third party, and you pay per-minute for heavy builds. A self-hosted runner gives you a machine you control: your own OS image, your own disk cache, your own network, and predictable cost. This post walks through setting one up on a VPS and the operational realities of keeping it healthy.
Why Self-Host, Honestly
Before you adopt a self-hosted runner, weigh what you get and what you give up.
- **Control:** pin a specific OS, install any tool, keep secrets in your own boundary.
- **Speed from cache:** a persistent local disk keeps dependency and build caches warm across runs.
- **Cost:** a flat monthly VPS beats per-minute runner billing for constant builds.
- **Custom hardware:** more RAM or more cores than the free shared tier, when tests need it.
The usual trade-offs are maintenance, uptime, and isolation. You now patch, restart, and monitor the box yourself, and a crashed runner means CI is down. Plan for those before you commit.
The Runner Agent
A self-hosted runner runs a small agent that polls your CI platform, pulls jobs, and executes them. For GitHub Actions the agent is a Node-based program you download from the repository settings page. The setup flow is interactive: copy a registration token, run the config script, and answer a few prompts about labels and the work folder.
# fetch the runner agent for Linux x64
mkdir -p /opt/github-runner && cd /opt/github-runner
curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64-2.319.1.tar.gz
tar xzf actions-runner-linux-x64.tar.gz
# register with a repo-level token
./config.sh --url https://github.com/YOUR-ORG/YOUR-REPO --token REGISTRATION_TOKEN --labels self-hosted,linux --name runner-01Run config.sh as a dedicated service user, never as root. Create a system account, give it only the permissions the build needs, and run the agent from that account. That limits the damage if one of your build steps is compromised or simply buggy.
Running It as a Service
The agent ships with a service installation script that wraps it in systemd on most distributions.
# install and start the runner as a system service
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh statusOnce it is a service, it survives reboots and you manage it with standard systemd commands. Confirm it appears in your repository's runners list marked as online, and then any job that asks for your runner's labels will be picked up.
Routing Jobs to Your Runner
Published CI jobs specify the platform with runs-on. To send a job to your self-hosted runner, name your labels there.
# ci.yaml — use the self-hosted runner for heavy builds
jobs:
build:
runs-on: [self-hosted, linux]
steps:
- uses: actions/checkout@v4
- run: scripts/build.shJobs without a matching label fall back to hosted runners, so you can mix: quick lint on hosted, heavy integration tests on your own box. Assign labels thoughtfully and keep a naming convention across the fleet.
Isolation Between Jobs
Because you own the box, jobs that run on it share one filesystem. That is a real security consideration: a malicious job could read another job's cache or environment. Mitigate by giving each major project its own runner or its own service user, and by treating the runner environment as untrusted if you accept jobs from outside contributors. Never run a self-hosted runner that processes untrusted pull requests from strangers without strict isolation.
Sizing and Maintenance
Size the runner for your slowest job, not your average. A typical suite wants 4–8 vCPUs and 8–16GB of RAM, but start small and watch peak utilization. Keep at least half the CPU free or your tests will time out mysteriously. Maintenance is the actual cost of self-hosting:
- Patch the OS weekly and reboot on kernel updates.
- Watch disk space; logs and caches grow silently.
- Set a cleanup job for stale working directories and caches.
- Monitor the agent and alert if it goes offline.
Takeaway
A self-hosted runner trades a little operational work for control, cache warmth, and cost predictability. Set it up as a service under a dedicated user, route labels in your workflow YAML, and keep segmentation between untrusted jobs. It is a solid upgrade once your build volume justifies owning a box.
A Netbay VPS with SSD storage and plenty of cores is a natural home for a self-hosted runner — you can provision one in under a minute at netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance