Runners server
GitLab CI jobs run on their own VPS, not on the GitLab server - see Infrastructure.
Written for Ubuntu 24.04 LTS.
Server
- 4 vCPU - 3 jobs at once, each compiles and pulls images in parallel
- 8 GB RAM - ~2.5 GB per job plus Docker and the runner
- 100 GB SSD - images, caches and volumes of 3 jobs, GitLab gives each hosted job 30 GB
GitLab's default hosted runner gives one job 2 vCPU and 8 GB - move to 16 GB RAM once jobs start failing out of memory
jobs run arbitrary code - next to GitLab they would share its 16 GB and reach
gitlab-secrets.json
Base setup
Set up the VPS as a new server.
the runner has no domain - skip the DNS record
Firewall
Create a cloud firewall named runners in the provider panel - by role, not by ports, since the rules change with the server.
Inbound
| Description | Protocol | Port | Sources |
|---|---|---|---|
| SSH | TCP | 22 | All IPv4, All IPv6 |
the runner polls GitLab over HTTPS for jobs - it only connects out, nothing connects in
Apply to
- tag
runners, then put the tag on the VPS - a rebuilt VPS gets the firewall back with the tag
Docker
Follow Install Docker Engine on Ubuntu - not the docker.io package from Ubuntu repositories.
GitLab Runner
Follow Install GitLab Runner using the official GitLab repositories:
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" -o script.deb.sh
sudo bash script.deb.sh
sudo apt install gitlab-runnerRegister
One instance runner serves every project, see Instance runners.
Admin→CI/CD→Runners→Create instance runner→Linux, checkRun untagged→Create runner
The next page shows the glrt- token only until registration - register with the Docker executor right away:
sudo gitlab-runner register --non-interactive --url https://<gitlab_host> --token <glrt_token> --executor docker --docker-image alpine:latestConcurrent jobs
One runner process runs 3 jobs at once - concurrent caps jobs across the whole server.
sudo vim /etc/gitlab-runner/config.tomlconcurrent = 3the runner reloads
config.tomlevery 3 seconds - no restart
Docker builds
docker build inside a job needs Docker-in-Docker with privileged = true under [runners.docker], or socket binding.
WARNING
both give a job root on the VPS - one more reason it holds no secrets but the runner token
Docker cache
Job containers and volumes stay on disk until the disk is full. Run clear-docker-cache from the runner package once a week:
echo '0 4 * * 0 root /usr/share/gitlab-runner/clear-docker-cache' | sudo tee /etc/cron.d/clear-docker-cacheit keeps images - pulled job images are removed only by
docker system prune