self hosted github runner
CI and build runners
Builds that finish before you have switched tabs.
What we would deploy
€44.03/mo
€62.90−30% annual
Hosted CI minutes are metered, queued and slow, and the cost curve gets ugly the moment a team is productive. A pair of dedicated-core instances with fast NVMe will out-build most hosted tiers and cost a fixed amount every month regardless of how often you push. Nested virtualisation is included, so Docker-in-Docker and KVM-based test suites work without a workaround.
Why this configuration
TORQUE gives you the core count, Gen5 NVMe gives you the artifact throughput, and nested virtualisation costs nothing. Register the runners against your forge and delete the hosted plan.
What we would deploy
Sizing
Two dedicated cores and 4 GB per concurrent job is the floor; compiled languages want 4 cores and 8 GB. A 32-core TORQUE node therefore runs 8 heavy jobs or about 14 light ones, not 32. Scratch disk matters more than either — budget 30-50 GB per concurrent job on NVMe for image layers, dependency trees and build output. Add a core per node if jobs need nested virtualisation. Queue wait time, not job duration, tells you when to add a node.
Nested virtualisation, and when you need it
Most CI runs fine in a container. Three cases need a real hypervisor inside the guest: testing VM images and cloud-init, running Android or embedded emulators with KVM acceleration, and any job where an untrusted pull request executes code that must not reach the runner. TORQUE exposes SVM to the guest, so KVM works inside your VPS and the emulator runs at native speed rather than through software emulation, which is roughly an order of magnitude difference on Android test suites. The cost is one core of overhead for the outer hypervisor and slightly slower disk in the inner guest. Budget for both.
The artifact path is usually the bottleneck
Profile a slow pipeline and the compile step is rarely the largest bar. Pulling base images, restoring caches, uploading artifacts and pushing the result account for most wall-clock time in typical builds. Three fixes, in order of return: keep a registry pull-through cache on the same private VLAN so layers arrive over local network rather than the public internet; put scratch on NVMe so layer unpack is not seek-bound; and make the cache key precise, so you restore what you need instead of a monolithic archive. Dense Bergamo cores are cheap. Waiting on the network is not.
What goes wrong
- Stacking overlayfs on overlayfs for Docker-in-Docker. Nested overlay drivers are slow and occasionally corrupt layers. Use a dedicated block device inside the job, or run the builder over nested KVM instead.
- Never pruning. Dangling images, build caches and stale volumes fill the scratch NVMe in weeks, and the first symptom is a build failing on a step that has worked for a year. Prune on a timer, not on failure.
- Letting cache transfer exceed build time. A 3 GB dependency cache pulled and pushed per job can cost more than compiling from scratch. Measure both, and keep the cache on the same private VLAN as the runners.
Tune the machine
- Everything else — Nested virtualisation (VMX/SVM) · Included
- CPU scheduling — Pinned physical cores · €9
- Storage and encryption — ZFS with hourly snapshots · €7
More
Built for specific jobs
Game server hosting
Tick rate is a single-thread problem. Everything else is noise.
Trading and low-latency VPS
Distance to the matching engine, and nothing between you and the wire.
Private VPN and proxy endpoints
Your own exit, in a jurisdiction you picked on purpose.
Seedboxes and bulk storage
Terabytes that stay cheap and a port that stays open.
Kubernetes worker nodes
Cheap per core, dense, and identical every time.
AI inference and fine-tuning
A whole GPU, passed through, on a commitment that makes the maths work.