How to install GitLab CI on a VPS
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-runnerAllow 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 dockerThe 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.sockStart the runner:
docker compose config
docker compose pull
docker compose up -d
docker compose psRegister it from the runner container:
docker compose exec runner gitlab-runner registerYou 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.
400 compute minutes/month on GitLab-hosted runners; self-managed runners do not consume that quota.