| | |

GitLab 15 🦊 Registering and Configuring Self-Hosted GitLab Runners

⏱️ Estimated read: 9 min | |

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

PlatformCommand
Debian/Ubuntuapt install gitlab-runner
RHEL/Fedorayum install gitlab-runner
BinaryDownload from S3

Registration Commands

FlagPurpose
--urlGitLab instance URL
--tokenRunner authentication token (glrt-)
--executorExecutor type
--docker-imageDefault Docker image
--descriptionRunner name

Executors

ExecutorIsolationUse Case
dockerHighReproducible builds
shellNoneDirect host execution
kubernetesHighCloud-native scaling
sshMediumRemote execution

Configuration File

SettingPurpose
concurrentMax simultaneous jobs
check_intervalPoll frequency (seconds)
[[runners]]Single runner config
[runners.docker]Docker executor config
privilegedContainer 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

PitfallWhy It HappensFix
410 Gone on registerLegacy token usedUse glrt- token
Runner not picking jobsTags do not matchVerify tags in UI and .gitlab-ci.yml
Docker permission deniedUser not in docker groupusermod -aG docker gitlab-runner
Config not appliedService not restartedsystemctl restart gitlab-runner
Pipeline pendingNo runner availableRegister 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

ItemValue
Installationapt install gitlab-runner
Token prefixglrt-
Registration flag--token
Config file/etc/gitlab-runner/config.toml
Servicesystemctl restart gitlab-runner
Default executordocker
Concurrencyconcurrent in config
Poll frequencycheck_interval

Key takeaways:

  • The registration workflow uses authentication tokens. The glrt- token is created in the GitLab UI and passed to gitlab-runner register with the --token flag. The legacy registration token is deprecated and scheduled for removal .
  • The executor determines the job environment. The docker executor runs jobs in containers. The shell executor runs them on the host. The kubernetes executor creates pods. The choice depends on the project’s isolation and infrastructure requirements .
  • The config.toml file 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-runner command ensures it starts at boot. The systemctl start command starts it immediately.
  • Tags control job routing. A job with tags: [docker] only runs on a runner that has the docker tag. 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.

Was this helpful?


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!