GitLab CI/CD – Runner Tags

In today’s blog post, we will discuss GitLab runner tags in .gitlab-ci.yml. Specifically, what is a GitLab runner?, Types of GitLab runners, What is a runner tag?, How to use runner tag in .gitlab-ci.yml. So without a further delay let us get started.

What is a GitLab Runner?

A GitLab Runner is the lightweight, standalone agent that runs your CI/CD jobs. When you define a pipeline in your .gitlab-ci.yml file and push code to your repository, GitLab CI/CD tells an available Runner to execute the specified commands. The Runner then picks up the job, sets up the environment, runs the scripts, and sends the results (logs, artifacts) back to GitLab.

Think of it as the “worker bee” of your CI/CD pipeline. Without Runners, your CI/CD configuration in .gitlab-ci.yml would just be a set of instructions with no one to carry them out.

Types of GitLab Runners

GitLab Runners can be deployed and configured in various ways, each suited for different use cases and infrastructure setups. The “executor” chosen during registration defines how the Runner executes jobs.

Here are the most common types of GitLab Runners based on their executors:

  1. Shell Executor:
    • How it works: Jobs are executed directly on the machine where the Runner is installed, using the system’s shell.
    • Pros: Simplest to set up, minimal overhead.
    • Cons: Less isolated (jobs share the same environment), potential for conflicts between job dependencies, difficult to scale or reproduce environments consistently.
    • Use Case: Small projects, development environments where full isolation is not critical.
  2. Docker Executor:
    • How it works: Each job runs inside a fresh Docker container. The Docker image specified in your .gitlab-ci.yml (image keyword) or configured on the Runner is used as the base environment.
    • Pros: Excellent job isolation (each job starts from a clean slate), highly reproducible environments, easy to scale.
    • Cons: Requires Docker to be installed on the Runner machine, slight overhead from container creation.
    • Use Case: Most common and recommended executor for robust CI/CD, microservices, polyglot projects.
  3. Docker Machine Executor:
    • How it works: Dynamically provisions virtual machines (VMs) on cloud providers (e.g., AWS, GCP, Azure) and runs Docker containers on them.
    • Pros: Highly scalable (VMs are spun up and down as needed), cost-effective for burstable workloads.
    • Cons: More complex to set up and manage.
    • Use Case: Large organizations needing auto-scaling CI/CD infrastructure in the cloud.
  4. Kubernetes Executor:
    • How it works: Jobs run as pods within a Kubernetes cluster.
    • Pros: Leverages Kubernetes’ powerful orchestration capabilities, highly scalable, cloud-agnostic.
    • Cons: Requires a Kubernetes cluster, more complex configuration.
    • Use Case: Organizations already using Kubernetes for their applications, high-scale CI/CD needs.
  5. SSH Executor:
    • How it works: Connects to a remote server via SSH and executes jobs there.
    • Pros: Can utilize existing remote infrastructure.
    • Cons: Less isolated than Docker, requires SSH access management.
    • Use Case: Legacy systems, specific hardware testing environments.

Regardless of the executor type, all GitLab Runners can be configured with tags, which is where we gain precise control over job placement.

What are GitLab Runner Tags?

When you register a GitLab Runner, you are prompted to assign one or more tags to it. These tags are arbitrary strings that describe the runner’s characteristics, capabilities, or purpose.

Examples of Runner Tags:

  • docker-executor (indicating it uses the Docker executor)
  • linux (indicating its OS)
  • windows
  • macos
  • gpu-enabled (for specialized hardware)
  • production-deployer (for a dedicated deployment runner)
  • java-11 (for specific software versions installed)
  • my-private-network (for network access)

A single runner can have multiple tags, for example: linux, docker-executor, java-11.

How Runner Tags Work in .gitlab-ci.yml

To direct a job to a specific runner (or a group of runners), you use the tags keyword at the job level within your .gitlab-ci.yml file.

Syntax:

job_name:
  tags:
    - tag_name_1
    - tag_name_2
  # ... other job configurations

When GitLab processes your pipeline, for any given job, it looks at the tags defined for that job. It will then assign the job to an available runner that possesses all of the tags specified by the job.

Example: Directing Jobs to Specific Environments

Let us say you have some runners configured for Linux-based builds and others for Windows-based builds, and a special one for GPU tests.

stages:
  - build
  - test

build_linux:
  stage: build
  script:
    - echo "Building on Linux..."
    - ./build.sh linux
  tags:
    - linux
    - docker-executor # This job requires a runner with both 'linux' AND 'docker-executor' tags

build_windows:
  stage: build
  script:
    - echo "Building on Windows..."
    - .\build.ps1 windows
  tags:
    - windows
    - shell-executor # This job requires a runner with both 'windows' AND 'shell-executor' tags

run_gpu_tests:
  stage: test
  script:
    - echo "Running GPU-accelerated tests..."
    - python run_gpu_tests.py
  tags:
    - linux
    - gpu-enabled
    - high-memory # This job needs all three tags to run

In this scenario:

  • build_linux will only be picked up by a runner that has both the linux and docker-executor tags.
  • build_windows will only be picked up by a runner that has both the windows and shell-executor tags.
  • run_gpu_tests requires a very specific runner tagged with linux, gpu-enabled, and high-memory.

If no runner with all the required tags is available or online, the job will remain in a “pending” state until such a runner becomes available. If no matching runner exists and the job cannot be picked up, it will eventually time out or fail.

Key Considerations for Using Runner Tags

  1. Logical Grouping: Assign tags based on a runner’s installed software, operating system, hardware capabilities (e.g., GPU, high RAM), network access, or dedicated purpose (e.g., production-deployer).
  2. Granularity: Be as specific or as general with your tags as needed. If a job needs a very particular setup, use more specific tags. If it can run on any general-purpose runner, use broader tags or no tags at all.
  3. “No Tags” Means Any Untagged Runner (by default): If a job has no tags defined, it will be picked up by any available runner that has no tags assigned to it, or by any runner that has the “run untagged jobs” option enabled during its registration (which is the default for newly registered runners). This is crucial for general-purpose jobs.
  4. Tag Matching is AND-based: Remember, a job requesting tags: ['A', 'B'] will only run on a runner that has both tag A and tag B. It is not an OR condition.
  5. Preventing Unintended Execution: Runner tags are a powerful security mechanism. By tagging sensitive jobs (e.g., production deployments) and registering dedicated runners with those tags (and limiting who can access/register those runners), you ensure that only authorized infrastructure can execute those critical tasks.
  6. Scalability and Flexibility: As your project grows, you can add more specialized runners with specific tags without having to modify your CI/CD configuration extensively. You just need to ensure the new runner has the correct tags.
  7. Troubleshooting: If a job is stuck in “pending,” the first thing to check is if there is an available runner with all the required tags. Also, confirm the tags in your .gitlab-ci.yml exactly match those on your runners (case-sensitive).

Managing Runners and Tags in GitLab

You can manage your GitLab Runners and their assigned tags in the GitLab UI:

  • Project-specific Runners: Navigate to Project > Settings > CI/CD > Runners
  • Group-specific Runners: Navigate to Group > Settings > CI/CD > Runners
  • Instance-wide Runners: (For GitLab administrators) Go to Admin Area > CI/CD > Runners

Here, you can see all registered runners, their current status, and crucially, their assigned tags. You can also edit a runner to add or remove tags, as well as toggle the “run untagged jobs” option.

GitLab Runners
GitLab Runners

FAQs – Tags


What is the tags keyword in GitLab CI/CD?
The tags keyword is used in .gitlab-ci.yml to specify which GitLab Runner(s) should execute a job. Tags match jobs with compatible runners. This is useful when you have different runners for different environments (e.g., Docker, shell, Windows, GPU, etc.).


Why do I need to use runner tags in my jobs?
You need to use tags when:

  • You have multiple runners and want to assign jobs to specific ones.
  • The runner is configured to require tags (common in shared runner setups).
  • You want to segregate workloads (e.g., build jobs vs. deploy jobs).

If a job does not specify the required tags, GitLab would not run it, and the job will remain in pending status.


How do I assign tags to a GitLab Runner?
You assign tags when you register a GitLab Runner:

gitlab-runner register

During the process, you will be prompted to enter tag(s) like this:

Please enter the executor: docker
Please enter the default Docker image: alpine:latest
Please enter the tags associated with the runner (comma-separated): docker,linux

These tags are then used to match jobs in your .gitlab-ci.yml.


How do I use tags in .gitlab-ci.yml?
Specify the tags under your job definition:

build:
  script:
    - make build
  tags:
    - docker
    - linux

This job will only run on a runner that has both docker and linux tags.


Can I define multiple tags for a job?
Yes. You can define multiple tags, and GitLab will look for a runner that has all of them:

test:
  script:
    - npm test
  tags:
    - nodejs
    - ubuntu

The job runs only on a runner that has both nodejs and ubuntu tags.


What happens if no runner matches the tags in a job?
If no runner has all the specified tags, the job will remain in pending state indefinitely.
To fix this:

  • Add appropriate tags to a runner.
  • Update the job to use correct/available tags.
  • Use gitlab-runner list to verify runner tag availability.

Can I run a job on any available runner without tags?
Yes, if the runner is not configured to require tags. If the runner is configured with run_untagged = true, it can pick up untagged jobs.

job:
  script: echo "This works if any runner accepts untagged jobs"

How do I enforce tag usage for runners?
When configuring the GitLab Runner, you can enforce it to run only tagged jobs by setting:

[runners]
  run_untagged = false

This prevents untagged jobs from being picked up by that runner.


Are tags case-sensitive in GitLab CI/CD?
Yes. Runner tags are case-sensitive. Docker and docker are treated as different tags. Be consistent in usage when assigning and referencing tags.


Can I use tags to restrict runners to specific projects?
Indirectly, yes. Although tags do not directly restrict project access, you can:

  • Use specific runners with specific tags.
  • Assign runners only to certain projects or groups.
  • Ensure jobs in those projects use matching tags.

This effectively creates a controlled execution environment per project.


How can I see what tags are assigned to my runners?
To view runner tags:

  • Go to GitLab UI → Settings → CI/CD → Runners.
  • Tags are listed next to each runner.

Or use the CLI:

gitlab-runner list

Can I define global tags for all jobs in the pipeline?
No. The tags keyword must be specified per job. If all jobs share the same tags, you need to repeat the tag list in each job or use YAML anchors to DRY your configuration:

.default-tags: &default-tags
  tags:
    - docker

build:
  <<: *default-tags
  script: make build

test:
  <<: *default-tags
  script: make test

What is the difference between job tags and Git tags?

  • Runner/job tags (tags: keyword): Used to route CI/CD jobs to specific GitLab Runners.
  • Git tags: Used for tagging code versions (e.g., v1.0.0). They are unrelated to job execution and are used in version control.

Author

Debjeet Bhowmik

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins. In my free time, I write blogs on ckdbtech.com

Leave a Comment