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:
- 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.
- 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.
- How it works: Each job runs inside a fresh Docker container. The Docker image specified in your
- 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.
- 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.
- 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 thelinux
anddocker-executor
tags.build_windows
will only be picked up by a runner that has both thewindows
andshell-executor
tags.run_gpu_tests
requires a very specific runner tagged withlinux
,gpu-enabled
, andhigh-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
- 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
). - 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.
- “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. - Tag Matching is AND-based: Remember, a job requesting
tags: ['A', 'B']
will only run on a runner that has both tagA
and tagB
. It is not an OR condition. - 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.
- 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.
- 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.

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

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