GitLab Runner hosting, on a machine that is yours
A self-managed runner in Prague or Covilha with the whole disk to itself, so the Docker layer cache is still there on the second pipeline of the day and the invoice does not move when the build does.
Who it is for
A team buying compute minutes
The test suite grew, so the pipeline got longer, so the bill went up - and the only lever the plan offers is buying more of exactly the same minutes.
A pipeline that wants the same machine twice
Every job starts from an empty disk. The base image is pulled again, the dependencies are fetched again, and uploading the cache takes longer than the step it was meant to save.
A repository under a residency rule
The source, the artifacts and the deploy keys all pass through the runner, and "a shared fleet, somewhere" is the answer your auditor keeps circling in red.
What a self-managed runner gives you back
- A warm Docker layer cache on local NVMe, so the second pipeline of the day starts where the first one finished instead of from an empty disk.
- Any executor you like - docker, shell, docker-autoscaler, instance or kubernetes - chosen per runner rather than handed to you by a plan.
- Your own concurrency, set as a number in config.toml instead of bought as the next tier up.
- Privileged mode when you genuinely need Docker-in-Docker, which no shared fleet will ever give you.
- A static outbound IPv4 from AS204057, so a deploy target, a package mirror or a database firewall can allowlist the runner by address.
- Whatever the build actually needs on the host: an old toolchain, a licensed SDK, or a fixture set nobody wants to download once per job.
Sized by what the pipeline actually does
GitLab publishes no hardware requirement for the runner itself, and that is not an oversight: the runner process is small and the build is not, so the number that matters is the job. Start from the heaviest job you run today, multiply by the concurrency you want, and be generous with disk - the layer cache and the build workspace are what fill it, and a full disk fails a pipeline in a way that reads like a broken test. One thing a flat monthly host does not do is disappear on a quiet week: hosted minutes cost nothing when nobody pushes, and this costs the same. It pays off from the point where the pipeline runs most days.
How to install GitLab Runner on a fresh host
Debian or Ubuntu, from a clean server, in the order GitLab documents it. Everything below is their sequence, not ours.
-
Add the official package repository
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" -o script.deb.sh less script.deb.sh sudo bash script.deb.shGitLab ships the repository as a setup script and asks you to read it before you run it, which is why their own instructions download it first instead of piping it straight into a shell.
-
Install the runner
sudo apt install gitlab-runnerThe package creates a gitlab-runner system user whose home directory is created empty, with no skeleton files. To pin a version, install gitlab-runner and gitlab-runner-helper-images together at the same version - since 17.7.1 they are a pair, and naming only one fails on a dependency error.
-
Install Docker, if the jobs run in containers
sudo apt install docker.io sudo systemctl enable --now dockerThe docker executor talks to a local Docker Engine over API v1.25, so the daemon has to be on the runner host itself. The distribution package is enough to start; Docker own repository carries the newer releases if a build needs one.
-
Register the runner
sudo gitlab-runner register \ --non-interactive \ --url "https://gitlab.com/" \ --token "$RUNNER_TOKEN" \ --executor "docker" \ --docker-image alpine:latest \ --docker-pull-policy "if-not-present" \ --description "docker-runner"Create the runner in GitLab first, under Settings then CI/CD then Runners, and it hands back an authentication token beginning glrt- which goes in --token. Set the job tags and the run-untagged option in that same form: with authentication tokens they belong to the runner, not to the register command. The older --registration-token flow is deprecated and scheduled for removal in GitLab 20.0.
-
Set the concurrency and start it
sudo sed -i "s/^concurrent = .*/concurrent = 4/" /etc/gitlab-runner/config.toml sudo gitlab-runner restart sudo gitlab-runner statusconfig.toml lives in /etc/gitlab-runner when the runner runs as root. concurrent is a ceiling across every runner registered on this host rather than a per-runner setting, and each runner can carry a smaller limit of its own underneath it.
-
Prove it with one job
stages: [test] smoke: stage: test tags: [docker-runner] script: - cat /etc/os-release - echo "built on $CI_RUNNER_DESCRIPTION"Commit that as .gitlab-ci.yml, push, and the job should land on your host within a few seconds. If it stays queued, the tags do not match the ones you set in the UI, or the runner is locked to a different project.
Checked against the official documentation on 2026-08-15 — read the source
What we are not
- We are not GitLab. GitLab, GitLab CI/CD and GitLab Runner are theirs, and nothing on this page is endorsed by them. What we sell is the machine a runner runs on.
- This page is not about hosting GitLab itself. A self-managed GitLab instance is a larger box and a different conversation - ask and we will size one, but it is not what is priced here.
- We do not write your .gitlab-ci.yml. The pipeline is yours; the host, the network and the address are ours.
- The runner is not managed for you unless you ask. You install it, or you add our administration service and we keep it patched, registered and monitored.
- Your GitLab subscription stays with GitLab. A self-managed runner does not consume their compute minutes, which is the point, but it does not replace the seats either.
The parts teams discover late
- The registration-token flow is on its way out. A runner created in the UI hands back a glrt- authentication token that gitlab-runner register takes as --token, and --registration-token is scheduled for removal in GitLab 20.0.
- Tags moved with it. With an authentication token, the job tags, the run-untagged option and the lock belong to the runner object you created in the UI, so the flags that used to set them on the command line no longer decide anything.
- Pinning a version means pinning two packages. Since 17.7.1, an explicit gitlab-runner version also needs gitlab-runner-helper-images at the same version, or apt refuses the install.
- check_interval defaults to 3 seconds. On a host carrying many registered runners that is a great deal of polling; raise it before you blame the network.
- Docker-in-Docker needs privileged mode, which effectively hands the job root on the host. If the pipeline only builds images, a rootless builder such as Kaniko or Buildah is the cheaper trade.
- Artifacts and caches still travel to GitLab unless you point the cache at your own S3-compatible bucket. On a runner you moved to the EU on purpose, that is usually the last hop left to move.
Hosted CI minutes versus a runner of your own
| Provider-hosted CI minutes | A runner on DCXV | |
|---|---|---|
| What you pay for | Every minute every job runs, for as long as the project exists | A flat monthly host, whatever the pipeline does that month |
| A build that gets slower | Costs more every month it stays slow | Costs nothing more - the machine is already paid for |
| Docker layer cache | Cold on each job unless you upload and download it yourself | Warm on local NVMe, between jobs and between days |
| Concurrency | A plan tier you upgrade | A number you set in your own configuration file |
| Where the checkout lands | A shared fleet, often in a region you cannot pin | One machine in Prague or Covilha, under a Cyprus contract |
| Outbound address | A large shared range that nothing can allowlist | One static IPv4 from AS204057, yours to allowlist |
| What the machine can hold | Whatever the runner image happens to ship with | Any toolchain, licence or fixture set you install once |
How this gets going on your host
Say what the pipeline does
The heaviest job you run today, how many you want running at once, and whether it builds container images. That is enough to size a host without guessing.
We hand over the machine
Prague or Covilha, root access and a static IPv4, in under ten minutes. Bring your own image if the runner is already baked into one.
Follow the walkthrough on this page
It is the vendor sequence, taken from their current documentation rather than from a blog post, and it takes a few minutes on a clean host.
Snapshot it, then add the second one
Take a snapshot once the first pipeline is green, so the next machine is a restore instead of a rebuild. A pool grows by adding hosts, not by making one enormous.
Why choose us
- Tier III certified facilities, 99.982% facility SLA
- Own network, AS204057, IPv4 and IPv6 dual-stack
- 24/7/365 support with ~10 minute average response
- Cyprus company, EU jurisdiction, GDPR-native since 2007
FAQ
- What is the difference between a shared runner and a self-managed one?
A shared runner is GitLab machine and you pay for the minutes it spends on your job. A self-managed runner is your machine: you install gitlab-runner on it, register it against your project, group or instance, and it consumes no compute minutes at all. GitLab free-tier namespaces get 400 compute minutes a month, and a busy pipeline goes through those in days
- How do I install and register GitLab Runner?
Add their apt repository from the setup script at packages.gitlab.com, install the gitlab-runner package, then run gitlab-runner register in non-interactive mode with the runner authentication token GitLab gives you when you create the runner in the UI. The full sequence with the exact commands is on this page, taken from their documentation rather than from a blog post
- Is the registration token still how you register a runner?
No, and this is the change that breaks most older guides. Create the runner in the GitLab UI first and it hands back an authentication token starting with glrt-, which gitlab-runner register takes as --token. The old --registration-token flow is deprecated and scheduled for removal in GitLab 20.0. Job tags, the run-untagged option and the lock now belong to the runner object you created in the UI, not to the register command
- What size server does a GitLab Runner need?
GitLab publishes no hardware requirement for the runner itself, because the runner process is small and your build is not. Size from the heaviest job multiplied by the concurrency you want. In practice 4 vCPU, 8 GB and 120 GB of NVMe carries two or three jobs at a time; 8 vCPU, 16 GB and 240 GB carries six to eight. Be generous with disk - the Docker layer cache and the workspace are what fill it
- Do I need Docker on the runner host?
Only if you use the docker executor, which most people do. It talks to a local Docker Engine over API v1.25, so the daemon has to be installed on the runner host itself. The shell executor needs no Docker at all, and the instance and docker-autoscaler executors create machines elsewhere
- Can several runners share one host?
Yes, and that is the normal shape. Register as many as you like against the same installation; the concurrent setting in /etc/gitlab-runner/config.toml is a ceiling across all of them, and each runner can carry a smaller limit of its own. Watch check_interval, which defaults to 3 seconds and becomes a lot of polling once a host carries a dozen runners
If you require assistance or have additional questions, please contact the managers or write to the support team at support@dcxv.com
Ready to get started?
Monthly billing, no setup fee, no lock-in