If you use GitLab for source control, self-hosting your own CI/CD runners gives you full control over the build environment and avoids shared-runner queue times. This guide covers setting up a GitLab Runner on a VPS.
Why Self-Host Runners
GitLab.com's shared runners work well for many projects, but a self-hosted runner gives you dedicated capacity, custom environment control, and can be more cost-effective for consistent, heavy build workloads — similar rationale to How to Set Up a Self-Hosted GitHub Actions Runner on a VPS.
Step 1 — Install GitLab Runner
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt install gitlab-runner -y
Step 2 — Get a Registration Token from GitLab
In your GitLab project or group: Settings > CI/CD > Runners — copy the registration token needed for the next step.
Step 3 — Register the Runner
sudo gitlab-runner register
Follow the interactive prompts: GitLab instance URL, registration token, description, and executor type.
Choosing an Executor Type
| Executor | Use Case |
|---|---|
| shell | Simple, runs directly on the VPS — least isolation |
| docker | Each job runs in an isolated container — recommended for most cases |
Step 4 — Configure the Docker Executor (Recommended)
Executor = "docker"
[runners.docker]
image = "ubuntu:22.04"
The Docker executor provides genuine job isolation — each pipeline run gets a clean container, avoiding state leaking between builds, similar in spirit to How to Set Up Zero-Downtime Deployments with Docker's isolation approach.
Step 5 — Verify the Runner Is Active
Check Settings > CI/CD > Runners in GitLab — your new runner should appear as active/online.
Creating a .gitlab-ci.yml Pipeline
stages:
- test
- deploy
test:
stage: test
script:
- npm install
- npm test
deploy:
stage: deploy
script:
- ./deploy.sh
only:
- main
Restricting the Runner to Specific Projects
By default, a project-registered runner only serves that specific project — if registering a group-level or shared runner, be deliberate about scope, since a broader-scope runner processes jobs from more sources, increasing the security surface if a job is malicious/compromised.
Security: Isolating Runner Jobs
See CI/CD Pipeline Security Best Practices — the same core principles apply regardless of CI platform: don't run untrusted code with excessive privileges, and isolate build environments from your production infrastructure's credentials.
Scaling Runners for Concurrent Jobs
concurrent = 4
Adjust the concurrent job limit in /etc/gitlab-runner/config.toml based on your VPS's actual capacity — too high a concurrency setting on limited hardware causes resource contention degrading all concurrent builds.
Common Errors
Runner shows as offline despite the service running — verify network connectivity to your GitLab instance, and check gitlab-runner logs (journalctl -u gitlab-runner) for connection or authentication errors.
Continue Reading
- How to Set Up a Self-Hosted GitHub Actions Runner on a VPS
- CI/CD Pipeline Security Best Practices
- How to Build a Simple CI/CD Pipeline with GitHub Actions
Browse more articles in DevOps & CI/CD.