Self-Hosted GitLab Runner Setup Guide¶
This guide explains how to set up a self-hosted GitLab Runner on the VPS to reduce shared runner compute minutes consumption.
Prerequisites¶
- VPS access (31.97.159.7)
- Root or sudo access
- GitLab project registration token
Step 1: Install GitLab Runner¶
On Ubuntu/Debian:¶
Option A: Using Installation Script (Recommended)
# Download the GitLab Runner repository
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
# Install GitLab Runner
sudo apt-get install gitlab-runner
Option B: Manual Installation (If script hangs)
If the installation script hangs at apt-transport-https (common on Ubuntu 24.04+), use manual installation:
# 1. Cancel the stuck process (Ctrl+C) and check for locks
sudo lsof /var/lib/dpkg/lock-frontend
sudo killall apt-get # if needed
# 2. Remove any failed repository configuration
sudo rm -f /etc/apt/sources.list.d/gitlab-runner.list
# 3. Add GitLab GPG key using modern method
# Try the official GitLab GPG key (works for all GitLab packages)
curl -fsSL "https://packages.gitlab.com/gitlab/gitlab-ce/gpgkey" | sudo gpg --dearmor -o /usr/share/keyrings/gitlab-runner-archive-keyring.gpg
# 4. Add repository to sources with proper keyring reference
echo "deb [signed-by=/usr/share/keyrings/gitlab-runner-archive-keyring.gpg] https://packages.gitlab.com/runner/gitlab-runner/ubuntu/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gitlab-runner.list
# 5. Update package list
sudo apt-get update
# 6. Install GitLab Runner (this will also install helper images)
sudo apt-get install gitlab-runner
Option C: Direct Download (Not Recommended)
The direct .deb download requires helper images that may not be available. Use Option B instead.
If you must use direct download, you'll need to also download helper images:
# Download latest .deb package
curl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_amd64.deb"
# Download helper images package (version must match)
# First, check what version you downloaded:
dpkg -I gitlab-runner_amd64.deb | grep Version
# Then download matching helper images (replace VERSION):
# curl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner-helper-images_VERSION_amd64.deb"
# Install both packages
sudo dpkg -i gitlab-runner*.deb
# Fix any dependency issues
sudo apt-get install -f
Note: Option B (repository installation) is recommended as it handles all dependencies automatically.
Troubleshooting Installation Issues¶
If script hangs at apt-transport-https:
- Cancel the process: Press
Ctrl+C - Check for package manager locks:
- Remove locks if needed (be careful):
- Use manual installation (Option B above)
Note: On Ubuntu 24.04 (Noble), apt-transport-https is built into apt and doesn't need separate installation. The script may hang because it's trying to install something unnecessary.
Verify Installation:¶
Step 2: Create Runner in GitLab UI¶
Important: GitLab has deprecated registration tokens. You must create the runner in GitLab UI first, then register it using the authentication token.
Note: The GitLab UI only creates the runner definition. Docker executor settings (image, privileged mode) are configured during registration on the VPS, not in the UI.
- Create the runner in GitLab:
- Go to your GitLab project
- Navigate to Settings → CI/CD → Runners
- Click "New project runner" button (or "Create project runner")
- Fill in the runner details:
- Tags:
self-hosted,vps,docker(comma-separated) - These tags will be used to route jobs to this runner
- Run untagged jobs: ✅ Check this box (optional, allows runner to pick up jobs without tags)
- Runner description:
VPS Self-Hosted Runner(optional) - Paused: Leave unchecked (runner should be active)
- Protected: Leave unchecked (unless you want runner only for protected branches)
- Lock to current projects: Leave unchecked (unless you want to restrict to this project only)
- Maximum job timeout: Leave default or set to desired value (minimum 600 seconds)
- Tags:
-
Click "Create runner"
-
Copy the authentication token:
- After creating the runner, GitLab will display an authentication token (starts with
glrt-) - Copy this token immediately - you'll need it to register the runner
- You can also find it later in the runner details page by clicking on the runner
Step 3: Register Runner on VPS¶
Now register the runner on your VPS using the authentication token. This is where you configure the Docker executor settings (image, privileged mode, etc.):
Non-interactive registration (recommended):
sudo gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
--token "glrt-xxxxxxxxxxxxxxxxxxxx" \
--description "VPS Self-Hosted Runner" \
--tag-list "self-hosted,vps,docker" \
--executor "docker" \
--docker-image "docker:24" \
--docker-privileged \
--docker-volumes "/var/run/docker.sock:/var/run/docker.sock" \
--docker-network-mode "bridge"
Replace glrt-xxxxxxxxxxxxxxxxxxxx with your actual authentication token from GitLab.
Key configuration options:
- --executor "docker": Use Docker executor (required for Docker builds)
- --docker-image "docker:24": Default Docker image for jobs
- --docker-privileged: Enable privileged mode (required for Docker-in-Docker)
- --docker-volumes: Mount Docker socket for Docker-in-Docker support
Alternative: Interactive registration
If you prefer interactive mode:
When prompted:
- GitLab URL: https://gitlab.com/
- Authentication token: (paste the glrt- token from GitLab UI)
- Description: VPS Self-Hosted Runner
- Tags: self-hosted,vps,docker (comma-separated)
- Executor: docker
- Default Docker image: docker:24
- Privileged mode: true (required for Docker-in-Docker)
- Docker volumes: /var/run/docker.sock:/var/run/docker.sock
Step 4: Configure Runner¶
Edit the runner configuration:
Ensure the configuration includes:
concurrent = 2 # Adjust based on VPS resources (2-4 recommended)
check_interval = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "VPS Self-Hosted Runner"
url = "https://gitlab.com/"
token = "YOUR_RUNNER_TOKEN"
executor = "docker"
[runners.docker]
tls_verify = false
image = "docker:24"
privileged = true
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
shm_size = 0
# Note: Docker-in-Docker service is configured in CI YAML (.gitlab-ci/services.yml)
# Do NOT add services configuration here - it will conflict with CI YAML
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
Important: The services configuration ensures Docker-in-Docker (dind) starts with the correct environment variables that your CI jobs expect (DOCKER_HOST=tcp://docker:2376).
Step 5: Start and Enable Runner¶
# Start the runner
sudo gitlab-runner start
# Enable auto-start on boot
sudo systemctl enable gitlab-runner
# Check status
sudo gitlab-runner status
Step 6: Verify Runner is Active¶
- Go to GitLab project → Settings → CI/CD → Runners
- You should see your runner listed with a green circle (active)
- The runner should show tags:
self-hosted,vps,docker
Step 7: Test the Runner¶
Trigger a pipeline and verify:
- Build jobs are picked up by the self-hosted runner
- Jobs complete successfully
- Check runner logs: sudo gitlab-runner --debug run
Troubleshooting¶
Runner not picking up jobs:¶
- Verify tags match in
.gitlab-ci/services.yml(tags: [self-hosted]) - Check runner is active in GitLab UI (should show green status)
- Verify runner has sufficient resources
- Ensure runner is not paused in GitLab UI
Registration token errors:¶
- Error: "Creating runners with runner registration tokens is disabled"
- Solution: Use the new authentication token method (create runner in UI first, then register with
glrt-token) - Registration tokens are deprecated as of GitLab 16.0 and will be removed in GitLab 18.0
Docker-in-Docker issues:¶
Error: "FATAL: No HOST or PORT found" or "Health check error"
This usually means there's a conflict between service configuration in config.toml and the CI YAML. Fix by:
- Remove any service configuration from
/etc/gitlab-runner/config.toml: - The Docker-in-Docker service is already configured in
.gitlab-ci/services.yml - Having it in both places causes conflicts
-
Remove any
[[runners.docker.services]]sections from config.toml -
Clean up any leftover Docker containers:
-
Ensure privileged mode is enabled in config.toml:
-
Restart the runner:
-
Verify Docker socket is accessible:
-
Check runner logs:
Note: The CI YAML (.gitlab-ci/services.yml) already defines the docker:24-dind service with the correct environment variables. The runner just needs privileged = true to support it.
Resource constraints:¶
- Adjust
concurrentin config.toml based on VPS CPU/RAM - Monitor VPS resources during builds:
htopordocker stats
Monitoring¶
View Runner Logs:¶
Check Runner Status:¶
List Registered Runners:¶
Expected Results¶
After setup:
- Build jobs (docker-build-*) will run on self-hosted runner
- Deployment jobs (deploy-qual, deploy-prod) will run on self-hosted runner
- Lightweight jobs (validation, security scans) remain on shared runners
- 70-85% reduction in shared runner compute minutes
Notes¶
- The runner uses VPS resources but doesn't consume shared compute minutes
- Ensure VPS has sufficient resources (CPU, RAM, disk) for concurrent builds
- Monitor VPS performance and adjust
concurrentsetting as needed - Keep GitLab Runner updated:
sudo apt-get update && sudo apt-get upgrade gitlab-runner