GitLab 15 🦊 Registering and Configuring Self-Hosted GitLab Runners
A GitLab Runner is the agent that executes the jobs defined in the .gitlab-ci.yml file. Without a runner, a pipeline stays in the pending state indefinitely. The runner is a separate process from the GitLab server. It can run on the same machine as the server, on a dedicated build machine, or on a fleet of machines managed by an autoscaler. The registration workflow has changed significantly: the legacy registration token is deprecated and scheduled for removal in GitLab 20.0. The new workflow uses a runner authentication token with the glrt- prefix, which is created in the GitLab UI before the runner is registered . The exam tests the installation, the registration workflow, and the executor selection.
Key point: A runner is registered with a runner authentication token (glrt- prefix), not the legacy registration token. The token is created in the GitLab UI (Settings → CI/CD → Runners) and passed to gitlab-runner register with the --token flag. The executor determines how the job runs: docker for container isolation, shell for direct execution, kubernetes for pod-based execution.
Why the registration workflow changed
The legacy token problem. The legacy registration token allowed anyone who possessed it to register an unlimited number of runners. There was no per-runner ownership record, and the token could not be scoped to a specific configuration. The new workflow creates a runner record in GitLab first, assigns it an authentication token (glrt-), and then registers the runner process with that token. The runner process inherits the configuration (tags, access level, description) that was set in the UI . The token is scoped to a specific runner record, which limits the blast radius if it is leaked.
The migration problem. In GitLab 17.0 and later, administrators can disable the legacy registration workflow entirely. A registration attempt with the old token returns 410 Gone - runner registration disallowed. To avoid this, the token in the registration command must be replaced with the authentication token . Existing runners continue to work after the upgrade; the change only affects new registrations.
The executor problem. The executor determines the environment in which the job runs. The docker executor runs each job in a container. The shell executor runs the job directly on the host, using the host’s shell and installed tools. The kubernetes executor creates a pod for each job. The ssh executor runs the job on a remote machine over SSH. The choice depends on the project’s requirements: container isolation, direct host access, Kubernetes integration, or remote execution .
The installation problem. The runner is installed from the official GitLab repository. On Debian/Ubuntu, the script.deb.sh script adds the repository and the apt install gitlab-runner command installs the package. On RHEL/Fedora, the script.rpm.sh script and the yum install or dnf install command serve the same purpose . The package installs the gitlab-runner binary and a systemd service.
The trade-off. The new registration workflow is more secure but requires a two-step process: create the runner in the UI, then register the process. The legacy workflow was a single command. The additional step is the cost of the per-runner ownership record and the scoped token.
a. Installing GitLab Runner
The installation depends on the operating system. The official repository is the recommended method.
# Debian/Ubuntu: add the repository
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" -o script.deb.sh
sudo bash script.deb.sh
# Install
sudo apt install gitlab-runner
# RHEL/Fedora: add the repository
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" -o script.rpm.sh
sudo bash script.rpm.sh
# Install
sudo yum install gitlab-runner
The package installs the binary at /usr/bin/gitlab-runner and a systemd unit at /etc/systemd/system/gitlab-runner.service. The service is enabled and started automatically. The configuration is stored in /etc/gitlab-runner/config.toml .
The gitlab-runner --version command verifies the installation. The gitlab-runner list command shows the registered runners and their configurations.
b. Registering with a runner authentication token
The registration requires a token from the GitLab UI. The token is created by navigating to Settings → CI/CD → Runners → New project runner (or the group/instance equivalent). The form sets the tags, the description, and the access level. The token is displayed once and must be copied .
# Register with the authentication token
sudo gitlab-runner register \
--url "https://gitlab.example.com/" \
--token "glrt-xxxxxxxxxxxxxxxxxxxx" \
--executor "docker" \
--docker-image "alpine:latest" \
--description "docker-runner"
The --url is the GitLab instance URL. The --token is the authentication token (glrt- prefix). The --executor is the executor type. The --docker-image is the default image for the docker executor. The --description is the runner name that appears in the UI .
The interactive mode (sudo gitlab-runner register without arguments) prompts for the same values. The non-interactive mode with --non-interactive is the correct form for scripts and CI pipelines.
After registration, the configuration is written to /etc/gitlab-runner/config.toml. The runner appears in the GitLab UI under the project or group where it was created. The pipeline can now pick up jobs that match the runner’s tags.
c. Configuring the executor and the config.toml
The executor is the most consequential configuration. The docker executor runs each job in a container, which provides a clean environment and isolates the job from the host. The shell executor runs the job directly on the host, which is simpler but provides no isolation.
# /etc/gitlab-runner/config.toml
concurrent = 4
check_interval = 0
[[runners]]
name = "docker-runner"
url = "https://gitlab.example.com/"
token = "glrt-xxxxxxxxxxxxxxxxxxxx"
executor = "docker"
[runners.docker]
image = "alpine:latest"
privileged = false
volumes = ["/cache"]
The concurrent setting is the maximum number of jobs that the runner can execute simultaneously. The check_interval is the frequency (in seconds) at which the runner polls for new jobs. The [[runners]] section is the configuration for a single registered runner. The [runners.docker] section is the executor-specific configuration .
The privileged = false setting is the default and should remain false unless the job requires Docker-in-Docker. A privileged container has access to the host’s kernel capabilities, which is a security risk. The volumes setting mounts a host directory into the container; the /cache volume is used for caching between jobs .
The runner service must be restarted after a configuration change: sudo systemctl restart gitlab-runner.
Complete Example Session
# ============================================
# PART 1: INSTALL GITLAB RUNNER
# ============================================
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-runner
# ============================================
# PART 2: VERIFY INSTALLATION
# ============================================
gitlab-runner --version
# Output: Version: 18.x.x
# ============================================
# PART 3: CREATE A RUNNER IN THE UI
# ============================================
# Settings → CI/CD → Runners → New project runner
# Tags: docker, linux
# Description: docker-runner
# Create runner
# Copy the glrt- token.
# ============================================
# PART 4: REGISTER THE RUNNER
# ============================================
sudo gitlab-runner register \
--url "https://gitlab.example.com/" \
--token "glrt-xxxxxxxxxxxxxxxxxxxx" \
--executor "docker" \
--docker-image "alpine:latest" \
--description "docker-runner"
# ============================================
# PART 5: VERIFY THE CONFIGURATION
# ============================================
sudo cat /etc/gitlab-runner/config.toml
# [[runners]]
# name = "docker-runner"
# url = "https://gitlab.example.com/"
# token = "glrt-xxxx"
# executor = "docker"
# ============================================
# PART 6: START THE SERVICE
# ============================================
sudo systemctl enable gitlab-runner
sudo systemctl start gitlab-runner
sudo systemctl status gitlab-runner
# active (running)
# ============================================
# PART 7: VERIFY IN THE UI
# ============================================
# Settings → CI/CD → Runners
# The runner appears with a green status.
# ============================================
# PART 8: TRIGGER A PIPELINE
# ============================================
# Push a commit to a branch with a .gitlab-ci.yml file.
# The runner picks up the job and executes it.
# ============================================
# PART 9: CHECK THE JOB LOG
# ============================================
# Build → Pipelines → click the job
# The log shows the runner name and the executor.
The nine parts trace the runner lifecycle: installation, verification, UI creation, registration, configuration inspection, service start, UI verification, pipeline trigger, and log inspection.
Quick Reference
Installation Commands
| Platform | Command |
|---|---|
| Debian/Ubuntu | apt install gitlab-runner |
| RHEL/Fedora | yum install gitlab-runner |
| Binary | Download from S3 |
Registration Commands
| Flag | Purpose |
|---|---|
--url | GitLab instance URL |
--token | Runner authentication token (glrt-) |
--executor | Executor type |
--docker-image | Default Docker image |
--description | Runner name |
Executors
| Executor | Isolation | Use Case |
|---|---|---|
docker | High | Reproducible builds |
shell | None | Direct host execution |
kubernetes | High | Cloud-native scaling |
ssh | Medium | Remote execution |
Configuration File
| Setting | Purpose |
|---|---|
concurrent | Max simultaneous jobs |
check_interval | Poll frequency (seconds) |
[[runners]] | Single runner config |
[runners.docker] | Docker executor config |
privileged | Container privileges |
Best Practices
✅ Do This:
sudo gitlab-runner register --token "glrt-xxxx"
<!-- Use the authentication token --> # ✅
privileged = false
<!-- Keep privileged mode disabled --> # ✅
sudo systemctl restart gitlab-runner
<!-- Restart after config changes --> # ✅
❌ Don’t Do This:
# Use the legacy registration token
<!-- Deprecated, removed in GitLab 20.0 --> # ❌
# Enable privileged mode without a reason
<!-- Security risk: container can access host --> # ❌
# Run the runner as root without isolation
<!-- Use the docker executor for isolation --> # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
410 Gone on register | Legacy token used | Use glrt- token |
| Runner not picking jobs | Tags do not match | Verify tags in UI and .gitlab-ci.yml |
| Docker permission denied | User not in docker group | usermod -aG docker gitlab-runner |
| Config not applied | Service not restarted | systemctl restart gitlab-runner |
| Pipeline pending | No runner available | Register a runner or check status |
Real-World Examples
1. Install
sudo apt install gitlab-runner
2. Create Runner in UI
Settings → CI/CD → Runners → New project runner
3. Register
sudo gitlab-runner register --token "glrt-xxxx" --executor "docker"
4. Check Config
sudo cat /etc/gitlab-runner/config.toml
5. Start Service
sudo systemctl start gitlab-runner
6. Verify Status
sudo systemctl status gitlab-runner
7. List Runners
gitlab-runner list
8. Unregister
gitlab-runner unregister --name "docker-runner"
9. Docker Executor
[runners.docker]
image = "alpine:latest"
10. Shell Executor
executor = "shell"
Visual
Registration Workflow
┌──────────────────────────────────────────────┐
│ GitLab UI │
│ Settings → CI/CD → Runners → New │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Runner record created │
│ Authentication token: glrt-xxxx │
└──────────────────┬───────────────────────────┘
│
│ copy token
▼
┌──────────────────────────────────────────────┐
│ gitlab-runner register --token glrt-xxxx │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ config.toml updated │
│ Runner appears in UI │
└──────────────────────────────────────────────┘
Executor Selection
┌──────────────────────────────────────────────┐
│ Need container isolation? │
└──────────┬───────────────────────┬───────────┘
│ YES │ NO
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ docker executor │ │ Need Kubernetes? │
└──────────────────────┘ └──────────┬───────────┘
│ YES
▼
┌──────────────────────┐
│ kubernetes executor │
└──────────────────────┘
Summary
| Item | Value |
|---|---|
| Installation | apt install gitlab-runner |
| Token prefix | glrt- |
| Registration flag | --token |
| Config file | /etc/gitlab-runner/config.toml |
| Service | systemctl restart gitlab-runner |
| Default executor | docker |
| Concurrency | concurrent in config |
| Poll frequency | check_interval |
Key takeaways:
- The registration workflow uses authentication tokens. The
glrt-token is created in the GitLab UI and passed togitlab-runner registerwith the--tokenflag. The legacy registration token is deprecated and scheduled for removal . - The executor determines the job environment. The
dockerexecutor runs jobs in containers. Theshellexecutor runs them on the host. Thekubernetesexecutor creates pods. The choice depends on the project’s isolation and infrastructure requirements . - The
config.tomlfile is the runner’s configuration. It defines the concurrency, the polling interval, and the executor-specific settings. The service must be restarted after a change. - The runner must be enabled and started. The
systemctl enable gitlab-runnercommand ensures it starts at boot. Thesystemctl startcommand starts it immediately. - Tags control job routing. A job with
tags: [docker]only runs on a runner that has thedockertag. The runner’s tags are set during registration or in the UI.
Remember: The runner is the worker. The GitLab server is the coordinator. The .gitlab-ci.yml is the instruction set. The runner polls for jobs, picks up the ones that match its tags, executes the script in the executor’s environment, and reports the result. The registration token is the credential. The executor is the environment. The config is the policy.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!