CI/CD·8 min read·

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.

NB

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.

bash
# 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-01

Run 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.

bash
# install and start the runner as a system service
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status

Once 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.

yaml
# 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.sh

Jobs 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.

git push event CI platform agent polls runner-01 (VPS) build + test warm local cache upload artifact to self-host keeps secrets local

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