Docker & DevOps

Set up a GitLab Runner on a VPS for your own CI/CD

Install GitLab Runner on your Linux VPS, register it against a GitLab project with an authentication token, and run a first pipeline with the Docker or shell executor.

AdvancedLinux server5 min read

This guide shows you how to set up a GitLab Runner on a VPS and connect it to a GitLab project, so every push runs your .gitlab-ci.yml pipeline on hardware you control. You will install the runner, register it with a project token, choose an executor and watch the first pipeline finish.

Before you start

Have these ready:

  • A Linux VPS running Ubuntu 22.04/24.04, Debian 12, AlmaLinux or Rocky 8–9, with root or sudo access.
  • A GitLab account with at least Maintainer rights on the project you want to build.
  • The project already pushed to GitLab, whether on GitLab.com or a self-managed instance.
  • Outbound HTTPS from the VPS to your GitLab instance and to any container registries your jobs pull from.

Log in over SSH and update the package index:

Bash
sudo apt update && sudo apt upgrade -y

Note: A runner is a build machine. Keep it patched and do not store personal data on it.

Install GitLab Runner on the VPS

Install the official package so systemd manages the service for you.

Ubuntu and Debian:

Bash
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt install -y gitlab-runner

AlmaLinux and Rocky:

Bash
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash
sudo dnf install -y gitlab-runner

Confirm the binary and the service:

Bash
gitlab-runner --version
systemctl status gitlab-runner --no-pager

The package creates a gitlab-runner system user and a systemd unit of the same name. The binary runs as that unprivileged user, which matters when you pick an executor.

Create the runner in GitLab and copy its token

Registration needs a token issued by GitLab. Recent releases use a runner authentication token starting with glrt-; older setups used a shared registration token. The UI wording changes between versions, so follow the current GitLab documentation for your release.

  1. Open your project in GitLab.
  2. Go to Settings → CI/CD → Runners.
  3. Select New project runner, add a description and tags such as vps,docker.
  4. Leave Run untagged jobs off unless you want this runner to take every job in the project.
  5. Create the runner and copy the token shown. You will not see it again.

Warning: That token lets a machine run jobs inside your project. Store it in a password manager or secrets store, never in the repository.

Register the runner on the VPS

Register with the token. The non-interactive form is easier to repeat and script.

Docker executor:

Bash
sudo gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.com/" \
  --token "glrt-REPLACE_WITH_YOUR_TOKEN" \
  --executor "docker" \
  --docker-image "alpine:latest" \
  --description "vps-docker-runner"

Shell executor:

Bash
sudo gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.com/" \
  --token "glrt-REPLACE_WITH_YOUR_TOKEN" \
  --executor "shell" \
  --description "vps-shell-runner"

For a self-managed instance, replace the URL with something like https://gitlab.example.com/. Registration writes the result to /etc/gitlab-runner/config.toml and restarts the service.

Which executor to choose

  • Docker runs every job in a fresh container built from the image you name. Jobs are isolated, cleanup is automatic and the host stays tidy.
  • Shell runs job scripts directly as the gitlab-runner user. It is simple, but jobs can touch anything that user can reach and leftovers stay on disk.

Docker is the better default on a VPS. Pick shell only when a job genuinely needs the host, such as building a native package against system libraries.

Prepare the Docker executor

The Docker executor talks to a local Docker daemon. Install Docker Engine and give the runner user access to the socket:

Bash
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker gitlab-runner
sudo systemctl restart gitlab-runner

Check both:

Bash
sudo docker info | head -n 5
id gitlab-runner

Warning: Membership of the docker group is effectively root on the host. Anyone who can push a job to this runner can mount / or read other containers' volumes. Enable it only for projects you trust.

Tune the runner in /etc/gitlab-runner/config.toml:

INI
concurrent = 4
check_interval = 3

[[runners]]
  name = "vps-docker-runner"
  url = "https://gitlab.com/"
  executor = "docker"
  [runners.docker]
    image = "alpine:latest"
    privileged = false
    volumes = ["/cache"]

concurrent caps how many jobs run at the same time — keep it close to your CPU count so one pipeline cannot starve the box. Apply changes with sudo systemctl restart gitlab-runner. If jobs need a shared build cache between containers, check the cache section of the GitLab Runner documentation.

Add a.gitlab-ci.yml pipeline

Commit a small pipeline to the default branch. This one builds and tests inside a throwaway Alpine container:

YAML
stages:
  - build
  - test

build-job:
  stage: build
  image: alpine:latest
  script:
    - echo "Building on $(hostname)"
    - apk add --no-cache curl
    - curl -sI https://example.com | head -n 1

test-job:
  stage: test
  image: alpine:latest
  script:
    - echo "Running tests"

Push it:

Bash
git add .gitlab-ci.yml
git commit -m "Add CI pipeline"
git push origin main

Open Build → Pipelines in GitLab. The job moves from pending to running, then passes. The first run pulls the image, so it takes longer than later ones.

Check it works

Run these on the VPS:

Bash
sudo gitlab-runner list
sudo gitlab-runner verify
systemctl is-active gitlab-runner

list prints the runners from config.toml with their executors, and verify contacts GitLab to confirm each token is still valid. The real confirmation is a green pipeline in the project view, with your runner's name shown next to the job.

Troubleshooting

  • "This job is stuck because you don't have any active runners online." The runner is registered but not picking up work. Run sudo gitlab-runner verify,
Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.