itsez.dev
๐Ÿ“–Tutorial

How to install GitLab CI on a VPS

2026-07-30ยท4 min readยทCI/CD & code

GitLab CI is a pipeline feature, while the installable component on a VPS is usually GitLab Runner. This guide runs a Docker executor runner, registers it interactively with your GitLab project and keeps the runner configuration separate from application secrets.

Prerequisites

Provision a dedicated Ubuntu 22.04 or 24.04 VPS with SSH, Docker and at least 2 GB of RAM. Build-heavy jobs need more CPU, memory and disk. You also need a GitLab project or group where you have permission to create a runner, plus a policy for untrusted merge requests.

A runner executes code from CI jobs. Treat it as disposable infrastructure, keep production credentials away from untrusted pipelines and do not share one privileged runner across unrelated teams.

What this guide installs: This guide installs ONLY the GitLab Runner agent โ€” the lightweight daemon that executes CI/CD jobs. It requires an EXISTING GitLab instance (gitlab.com or self-hosted) to register against. This is NOT the full GitLab server.

Step 1, Connecting to your server

Patch the machine and create a runner directory:

ssh root@SERVER_IP
apt update && apt upgrade -y
apt install -y ca-certificates curl
mkdir -p /opt/gitlab-runner/config
cd /opt/gitlab-runner
chmod 750 /opt/gitlab-runner

Allow SSH only unless the host runs another service. A runner normally does not need public HTTP or HTTPS. Restrict outbound network access if your jobs do not need unrestricted internet access.

Step 2, Installing Docker and Docker Compose

Install Docker Engine and the Compose plugin from Docker's official Ubuntu instructions:

docker --version
docker compose version
systemctl enable --now docker

The Docker executor needs access to Docker. This is powerful, so use a dedicated VPS and do not mount unrelated host directories into jobs.

Warning: Mounting /var/run/docker.sock gives the runner container root-equivalent access to the host. Any CI job can escape to the host. Use this setup only for trusted projects.

Step 3, Running GitLab Runner with Docker Compose

Create compose.yaml:

services:
  runner:
    image: gitlab/gitlab-runner:alpine
    restart: unless-stopped
    volumes:
      - ./config:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock

Start the runner:

docker compose config
docker compose pull
docker compose up -d
docker compose ps

Register it from the runner container:

docker compose exec runner gitlab-runner register

You need a registration token from your GitLab project at Settings > CI/CD > Runners. The prompts ask for:

  • The GitLab instance URL (e.g., https://gitlab.com)
  • The registration token
  • A description and tags for the runner
  • The executor: choose Docker
  • The default image: enter alpine:latest

Enter the token interactively, never in a committed command or article.

Step 4, No reverse proxy needed

The GitLab Runner does not expose an HTTP port; it connects outbound to your GitLab instance. No reverse proxy is needed.

In GitLab, set tags and the protected or unprotected status according to your trust model. Do not allow untrusted forks to schedule jobs on a runner that can reach deployment credentials.

Step 5, First CI job

Trigger a pipeline in GitLab and verify the runner picks it up. Create a minimal .gitlab-ci.yml in a test project:

test:
  image: alpine:latest
  script:
    - echo "runner is working"

Push the file and watch the pipeline. Confirm that the job selects the expected runner, that the container starts and that logs contain no secrets. Test a failed job and a cancellation so operators know how the runner behaves.

If you build Docker images, choose the executor and Docker-in-Docker approach documented by GitLab for your security requirements. Avoid privileged mode unless the job absolutely needs it.

Security: The Docker executor gives CI jobs full access to the Docker daemon. For multi-project runners, consider using ephemeral VMs or the Docker Autoscaler executor.

Maintenance

Update the runner image after reading GitLab Runner release notes and test one project before updating shared runners. Back up the runner configuration only if you need to recreate its identity, and rotate registration credentials through GitLab when staff or trust boundaries change.

Monitor disk usage from old job images, runner connectivity and failed jobs. Clean caches through a controlled policy, not by deleting the whole config volume.

Tools mentioned

GitLab CI/CD

โ†—

Integrated pipelines, runners and environments for GitLab projects.

FreemiumNo cardOSS

400 compute minutes/month on GitLab-hosted runners; self-managed runners do not consume that quota.

ABOUT US

Honest, independent, no fluff.

No paid placements. Just a clear look at what this does, what it costs, and what to know before you commit.

Read moreโ†’

FAQ

Questions, answered.

Can GitLab CI run without a full GitLab server?๏ผ‹

A runner can execute jobs for GitLab.com or another GitLab instance. The runner is not the GitLab control plane or repository server.

Should the runner use the Docker socket?๏ผ‹

The Docker executor commonly needs it, but access to the socket is effectively privileged. Use a dedicated runner and restrict which projects can schedule jobs.

Where is the registration token entered?๏ผ‹

Enter it interactively during the official runner registration flow or inject it from a protected CI or administration secret store.