Tags for GitLab CI and tags for Git are two different concepts.

When you write your .gitlab-ci.yml, you can specify some jobs with the tag testing. If a runner with this tag associated is available, it will pickup the job.

In Git, within your repository, tags are used to mark a specific commit. It is often used to tag a version.

The two concepts can be mixed up when you use tags (in Git) to start your pipeline in GitLab CI. In your .gitlab-ci.yml, you can specify the section only with tags.

Refer to GitLab documentation for tags and only.

An example is when you push a tag with git:

$ git tag -a 1.0.0 -m "1.0.0"
$ git push origin 1.0.0

And a job in .gitlab-ci.yml like this:

compile:
    stage: build
    only: [tags]
    script:
        - echo Working...
    tags: [testing]    

would start using a runner with the testing tag.

By my understanding, what is missing in your steps is to specify the tag testing to your runner. To do this, go in GitLab into your project. Next to Wiki, click on Settings. Go to CI/CD Pipelines and there you have your runner(s). Next to its Guid, click on the pen icon. On next page the tags can be modified.

Answer from sgy on Stack Overflow
🌐
GitLab
forum.gitlab.com › how to use gitlab
How to use tags to use a specific runner? - How to Use GitLab - GitLab Forum
March 11, 2021 - How to use tags to use a specific runner? I wrote tag in .gitlab-ci.yml: appBuild: stage: build tags: - android I wrote “android” tag in runner config. But job is in pending state and is waiting to be picked by…
🌐
Bitslovers
bitslovers.com › gitlab-runner-tags
GitLab Runner Tags – Complete Guide for Complex Scenarios | Bits Lovers' - Cloud Computing and DevOps
October 8, 2021 - Tags are set in the UI when you create the runner, not on the command line. For reference, here is the old approach. This still works on older GitLab versions but will stop working in a future release: sudo gitlab-runner register \ --url "https://gitlab.example.com/" \ --registration-token "THE_REGISTRATION_TOKEN" \ --executor "docker" \ --docker-image alpine:latest \ --description "docker-runner" \ --tag-list "docker,aws" \ --run-untagged="true" \ --locked="false"
Discussions

git - Understanding Gitlab CI tags - Stack Overflow
But of course it should be universal, ... the runner, but for example let him recognize numeric values. As you can see, I'm fairly confused... If you could elaborate how exactly tags work, so I would be able to understand the concept, I would be really grateful. ... Save this answer. ... Show activity on this post. Tags for GitLab CI and tags ... More on stackoverflow.com
🌐 stackoverflow.com
How to set tags on Gitlab runner instances? - Stack Overflow
With the previous example, how can I set "osx" as a tag on my runner? ... Save this answer. ... Show activity on this post. You can either set this either when registering the runner: Copygitlab-runner register --tag-list "osx,foo,bar" #... Or you can edit the tags of an already-registered runner using the GitLab ... More on stackoverflow.com
🌐 stackoverflow.com
Runner/Job tag best practices feedback
I have some opinions in this area that may help you reduce the number of tags and optimize your runner availability and bring delight to your engineering teams. On software I don't think software tags are needed at all. You should ideally have just one (current) image for each OS/architecture and maybe some special cases for things like device farms where emulation is not practical. That image should install a baseline set of software that includes all the runtimes/versions/etc. that you need across the org. You can control the default versions through environment variables in most cases. Docker-based executors are ideal for your untagged jobs, even better if you can define your software images using image: instead of tags: where possible. Use tags: for changing major os family (e.g., linux, windows, macos) or architecture where needed. You can cache all the predefined docker images on the runner host so jobs are quick to start (doesn't matter if the images are very large!). Tagging for your 'generations' of images (again, ideally as a docker image tag instead of runner tag!) is a good idea. Examples of this practice in the wild: GitHub-hosted actions runner pre-installed software for ubuntu , windows , and macos , and appveyor preinstalled software . On security By default, all your runners should have the same baseline (unprivileged) security context. Escalating the security context should always go through authn/authz. Do not use your runners for security contexts! It will only (1) introduce security issues at worst or (2) needlessly proliferate the number of required runners at best. To access security-sensitive contexts, you should always require authentication and authorization and, ideally, log the access recording the relevant job/user responsible for triggering the access. To cover the use cases you mentioned: Code signing (and secrets, generally) Use a secrets manager instead (Hasicorp Vault, AWS Secrets Manager, Azure KeyVault, etc.) Access to secrets should require authn/authz and record access. Avoid using CI/CD variables for secrets if you can help it. Use OIDC to authenticate jobs to cloud environments and/or appropriate secrets. Most cloud providers and products support OIDC directly (i.e., use CI_JOB_JWT_V2) Use "just in time" access where applicable (example: use AWS Systems Manager sessions instead of static SSH keys) For code signing specifically, ideally use an asymmetric signing mechanism to avoid exposing your private keys to jobs directly where applicable (e.g., using something like AWS KMS ) Networking For accessing various networks, ideally, you would use a proxy (with authentication) instead of needing to place your runners in a particular subnet or whatever it is you are doing today. If you have lots of these, you would need to place a runner in each context, which is inefficient. If you use proxies, all your runners can run all the same jobs, but only jobs with appropriate needs should be able to auth to the relevant proxies. Authenticating and/or obtaining credentials for the relevant proxies should follow the above advice regarding secrets. Ideally, use scoped credentials so your proxy can log activity back to the source job. This is not always straightforward and may require significant work to implement depending on your existing network architecture and other institutional requirements, but shouldn't be a huge problem for a small org. Closing thoughts One last anecdotal note: I administer a GitLab instance for a large global 500 company. Everyone uses runners managed by my team. Our company develops software for just about everything under the sun (Windows, MacOS, iOS, Android, most Linux distributions, custom hardware, and more)... we have just a handful of tags -- one for each major OS family and architecture and a few tags for our in-house device farms and hardware racks. Beyond that, we have a few docker images (one for each OS/distro we need with various architecture variants within the same image manifest) but all the images have all the same installed software list where possible. Untagged jobs run on Linux/AMD64 (because that's what most of our development targets) and use our latest build image by default when no image: key is defined. Containers are a key part of how we make this strategy work with such little effort. Many of our teams very much enjoy the ability to create their own purpose-built docker images for GitLab CI builds. Highly recommend you use container-based executors if you are not doing so already. More on reddit.com
🌐 r/gitlab
16
8
August 25, 2022
How to run a CI script on a runner with any of given tags?
I have a list of specific runners with the unique tag each. I want to be able to run a reusable CI script on any of these runners. For example: job: stage: job script: - script.sh tags: - runner_one - runner_two I want to run this script on runner_one and runner_two. More on forum.gitlab.com
🌐 forum.gitlab.com
3
0
November 11, 2022
🌐
GitLab
docs.gitlab.com › gitlab docs › administer › administer gitlab runner › configure runners
Configuring runners | GitLab Docs
To the right of the runner you want to edit, select Edit ( ). ... To run tagged jobs, in the Tags field, enter the job tags separated with a comma. For example, macos, rails.
🌐
GitLab
docs.gitlab.com › gitlab docs › use gitlab › use ci/cd to build your application › runners
Runners | GitLab Docs
A runner must first be registered with GitLab, which establishes a persistent connection between the runner and GitLab.
🌐
Bitslovers
bitslovers.com › gitlab-runner-tags-2026
GitLab Runner Tags: The Complete Guide for 2026 | Bits Lovers' - Cloud Computing and DevOps
April 10, 2026 - You can stack environment, resource, and team dimensions together — a runner tagged prod,high-memory,team:backend is a valid target for any job that requires any subset of those tags.
🌐
Medium
medium.com › @aymenfarhani28 › gitlab-runners-ff2be56a1985
Gitlab Runners. In GitLab CI/CD, runners are the agents… | by Aymen FARHANI | Medium
December 19, 2024 - 1- How to Use Runner Tags: — When you register a runner, you can assign it one or more tags. — In your .gitlab-ci.yml configuration file, you can specify the tags for jobs. Only runners with the specified tags will pick up those jobs. 2- Example: Here’s an example of how you might set up a job to deploy to EC2 after a Docker image is tagged:
Find elsewhere
🌐
Programster
blog.programster.org › gitlab-pipeline-tags
GitLab Pipeline Tags | Programster's Blog
April 24, 2024 - So for example: ... That will bring up the following form to edit the runner. Input a comma-separated list of tags into the first form field. Tick the checkbox if you also wish this runner to run untagged jobs (2). I generally don't, and make sure everything is tagged appropriately. Click save. You should now see the tags listed as little blue pills for your runner. Now in your codebase, edit the .gitlab-ci.yml file (1) to add the tags (2) to the jobs that you want to run on specific runners like so:
🌐
GitLab
docs.gitlab.com › gitlab docs › administer › administer gitlab runner › register a runner
Registering runners | GitLab Docs
sudo gitlab-runner register \ --non-interactive \ --url "https://gitlab.com/" \ --registration-token "$PROJECT_REGISTRATION_TOKEN" \ --executor "docker" \ --docker-image alpine:latest \ --docker-pull-policy "if-not-present" \ --description "docker-runner" \ --maintenance-note "Free-form maintainer notes about this runner" \ --tag-list "docker,aws" \ --run-untagged="true" \ --locked="false" \ --access-level="not_protected"
🌐
Reddit
reddit.com › r/gitlab › runner/job tag best practices feedback
r/gitlab on Reddit: Runner/Job tag best practices feedback
August 25, 2022 -

I was tasked with upgrading old runners (Win7 yikes!) and found our tag practices a but uncoordinated. The way things are setup I often had to pause a runner and unpause the new runner hoping to catch my test job. Untagged jobs, a crazy mix of instance/project runners with overlapping configurations, tags like "win" and "win_new".. made it difficult to figure out what will run where.

So I thought I would try to propose some best practices, and figured this would be a good place for feedback.

Broad tag categories and examples

  • Base Functionality, OS information. As much detail as necessary:

    • win_server_2019

    • win_desktop_10

    • linux

    • aix

  • Software installed, with version (in the case of msvs, workloads supported):

    • InstallShield9

    • msvs2019_c#_c++_vb

    • msvs2017_vb

    • python2.7

  • Security Scope - define capabilities that have security implications:

    • public_internet

    • local_network

    • external_wan_to_other_business_unit

    • code_signing

  • Arbitrary tags to assist runner migrations. This is purely self serving, if every job and runner had a gen_1 tag it would pin the jobs to 'gen_1' runners. When I need to migrate a runner, stand up a copy incrementing the gen_x tag and I can start migrating/testing jobs in a very controlled manner:

    • gen_1

    • gen_2

    • you get the idea.

Hope that all makes sense. We have a lot of small teams scattered all over the world, 142 projects in a single GitLab instance and no standards ;-)

Thanks for any feedback...

Top answer
1 of 3
9
I have some opinions in this area that may help you reduce the number of tags and optimize your runner availability and bring delight to your engineering teams. On software I don't think software tags are needed at all. You should ideally have just one (current) image for each OS/architecture and maybe some special cases for things like device farms where emulation is not practical. That image should install a baseline set of software that includes all the runtimes/versions/etc. that you need across the org. You can control the default versions through environment variables in most cases. Docker-based executors are ideal for your untagged jobs, even better if you can define your software images using image: instead of tags: where possible. Use tags: for changing major os family (e.g., linux, windows, macos) or architecture where needed. You can cache all the predefined docker images on the runner host so jobs are quick to start (doesn't matter if the images are very large!). Tagging for your 'generations' of images (again, ideally as a docker image tag instead of runner tag!) is a good idea. Examples of this practice in the wild: GitHub-hosted actions runner pre-installed software for ubuntu , windows , and macos , and appveyor preinstalled software . On security By default, all your runners should have the same baseline (unprivileged) security context. Escalating the security context should always go through authn/authz. Do not use your runners for security contexts! It will only (1) introduce security issues at worst or (2) needlessly proliferate the number of required runners at best. To access security-sensitive contexts, you should always require authentication and authorization and, ideally, log the access recording the relevant job/user responsible for triggering the access. To cover the use cases you mentioned: Code signing (and secrets, generally) Use a secrets manager instead (Hasicorp Vault, AWS Secrets Manager, Azure KeyVault, etc.) Access to secrets should require authn/authz and record access. Avoid using CI/CD variables for secrets if you can help it. Use OIDC to authenticate jobs to cloud environments and/or appropriate secrets. Most cloud providers and products support OIDC directly (i.e., use CI_JOB_JWT_V2) Use "just in time" access where applicable (example: use AWS Systems Manager sessions instead of static SSH keys) For code signing specifically, ideally use an asymmetric signing mechanism to avoid exposing your private keys to jobs directly where applicable (e.g., using something like AWS KMS ) Networking For accessing various networks, ideally, you would use a proxy (with authentication) instead of needing to place your runners in a particular subnet or whatever it is you are doing today. If you have lots of these, you would need to place a runner in each context, which is inefficient. If you use proxies, all your runners can run all the same jobs, but only jobs with appropriate needs should be able to auth to the relevant proxies. Authenticating and/or obtaining credentials for the relevant proxies should follow the above advice regarding secrets. Ideally, use scoped credentials so your proxy can log activity back to the source job. This is not always straightforward and may require significant work to implement depending on your existing network architecture and other institutional requirements, but shouldn't be a huge problem for a small org. Closing thoughts One last anecdotal note: I administer a GitLab instance for a large global 500 company. Everyone uses runners managed by my team. Our company develops software for just about everything under the sun (Windows, MacOS, iOS, Android, most Linux distributions, custom hardware, and more)... we have just a handful of tags -- one for each major OS family and architecture and a few tags for our in-house device farms and hardware racks. Beyond that, we have a few docker images (one for each OS/distro we need with various architecture variants within the same image manifest) but all the images have all the same installed software list where possible. Untagged jobs run on Linux/AMD64 (because that's what most of our development targets) and use our latest build image by default when no image: key is defined. Containers are a key part of how we make this strategy work with such little effort. Many of our teams very much enjoy the ability to create their own purpose-built docker images for GitLab CI builds. Highly recommend you use container-based executors if you are not doing so already.
2 of 3
2
Thought I should provide a bit more background. I was hired as a software engineer, they knew I had experience managing servers and build infrastructure (20 or so years worth) and nobody else in engineering wanted to (or was skilled to) handle this work. I'm in essence 'part time' on this type of work, big initiatives such as moving to containers are likely off the table at least short term. Prime driver for recent work was to get rid of 2 windows 7 runner boxes (that's almost done). My goal with the above 'best practices' is to try and make sense of the mess we currently have, within the confines of the current landscape/culture. Perhaps in the future I'll be in a position to make big changes, for now I'm eating this elephant one bite at a time. I appreciate the feedback, and believe me I'm taking notes!
🌐
GitLab
docs.gitlab.com › gitlab docs › administer › administer gitlab runner
GitLab Runner | GitLab Docs
Runner token: A unique identifier that allows a runner to authenticate with GitLab. Tags: Labels assigned to runners that determine which jobs they can execute.
🌐
GitLab
gitlab.com › gitlab.org › gitlab-runner › tags
Tags · GitLab.org / gitlab-runner · GitLab
Tags give the ability to mark specific points in history as being important
🌐
University of Toronto
microfluidics.utoronto.ca › help › help
Configure runners · Runners · Ci · Help · GitLab
... windows job: stage: build tags: - windows script: - echo Hello, %USERNAME%! osx job: stage: build tags: - osx script: - echo "Hello, $USER!" In the .gitlab-ci.yml file, use CI/CD variables with tags for dynamic runner selection:
🌐
GitLab
forum.gitlab.com › gitlab ci/cd
How to run a CI script on a runner with any of given tags? - GitLab CI/CD - GitLab Forum
November 11, 2022 - I want to be able to run a reusable CI script on any of these runners. For example: job: stage: job script: - script.sh tags: - runner_one - runner_two I want to run this script on runner_one and runner_two.
🌐
OpenMSCG
software.rcc.uchicago.edu › help
Readme · Runners · Ci · Help · GitLab
By tagging a runner for the types ... are equipped to run. For instance, at GitLab we have runners tagged with rails if they contain the appropriate dependencies to run Rails test suites....
🌐
GitLab
forum.gitlab.com › gitlab ci/cd
Gitlab runner on kubernetes, how to add tags - GitLab CI/CD - GitLab Forum
February 9, 2024 - Hello, We have gitlab runner setup using helm chart to run on top of kubernetes (AWS EKS). I’m trying to figure out how to automatically add certain tags to runners. When I’m looking examples from https://gitlab.com/gitlab-org/charts/gitlab-runner/blob/main/values.yaml It says that tags: is deprecated.
🌐
GitLab
forum.gitlab.com › gitlab ci/cd
A question about Tags when creating a runner - GitLab CI/CD - GitLab Forum
March 10, 2025 - Hello, When creating a runner, there is a section called Tags. Is this section mandatory? If so, how should its name be chosen? Thank you.
🌐
Docker Hub
hub.docker.com › r › gitlab › gitlab-runner › tags
gitlab/gitlab-runner - Docker Image
TAG · ubuntu-bleeding · Last pushed 40 minutes by gitlabcibuild · docker pull gitlab/gitlab-runner:ubuntu-bleedingCopy · TAG · 4b924dfb · Last pushed 40 minutes by gitlabcibuild · docker pull gitlab/gitlab-runner:4b924dfbCopy · TAG · ubuntu-4b924dfb ·