{"id":78321,"date":"2026-03-11T15:20:43","date_gmt":"2026-03-11T09:50:43","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=78321"},"modified":"2026-03-16T15:42:41","modified_gmt":"2026-03-16T10:12:41","slug":"running-gitlab-ci-at-scale-setting-up-kubernetes-based-runners","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/running-gitlab-ci-at-scale-setting-up-kubernetes-based-runners\/","title":{"rendered":"Running GitLab CI at Scale: Setting Up Kubernetes-Based Runners"},"content":{"rendered":"<h1><span style=\"color: #000000;\">Introduction<\/span><\/h1>\n<p><span style=\"color: #000000;\">As the project grows and traditional CI runners running on static virtual machines or shared runners often struggle to keep up with increasing workloads. Jobs may queue up, builds start taking longer than expected and developers are left waiting.<\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>what&#8217;s the solution?<\/strong><\/span><br \/>\n<span style=\"color: #000000;\">So, instead of using traditional executioners we can leverage the <strong>GitLab Runner Kubernetes Executor<\/strong> for this as it creates new pods for every new jobs and delete&#8217;s it as soon as the job&#8217;s get completed. Kubernetes Executor dynamically create pods to execute the pipeline jobs. Hence, This approach makes a scalable CI\/CD architecture where compute resources are gets used only when its needed. In this blog, we will explore how Setting Up Kubernetes-Based Runners.<\/span><\/p>\n<h2><span style=\"color: #000000;\">Step 1: Create Namespace and Add the Helm Repository<\/span><\/h2>\n<p><span style=\"color: #000000;\">Create a new namespace gitlab for runners and add the helm repository by using below commands-<\/span><\/p>\n<p><span style=\"color: #000000;\"><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-78317\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.14.29\u202fAM.png\" alt=\"cmd\" width=\"406\" height=\"140\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.14.29\u202fAM.png 876w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.14.29\u202fAM-300x103.png 300w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.14.29\u202fAM-768x265.png 768w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.14.29\u202fAM-624x215.png 624w\" sizes=\"(max-width: 406px) 100vw, 406px\" \/><\/span><\/p>\n<h2><span style=\"color: #000000;\">Step 2: Configure RBAC (Role Based Access Control)<\/span><\/h2>\n<p><span style=\"color: #000000;\">So, The gitlab runners needs the permissions to manage the pods in kubernetes cluster for which we need to create the RBAC.<\/span><br \/>\n<span style=\"color: #000000;\">RBAC defines what resources a service account can access within that cluster.<\/span><\/p>\n<p><span style=\"color: #000000;\">So, RBAC configuration includes below three main components-<\/span><\/p>\n<p><span style=\"color: #000000;\">1. <strong>Service Account<\/strong>: Represents the identity used by the runner.<\/span><br \/>\n<span style=\"color: #000000;\">2. <strong>Cluster Role<\/strong>: Defines the permissions required by the runner.<\/span><br \/>\n<span style=\"color: #000000;\">3. <strong>Cluster Role Binding<\/strong>: Binds the role with the service account.<\/span><\/p>\n<p><span style=\"color: #000000;\">RBAC configuration that we would be using is below &#8211;<\/span><\/p>\n<div id=\"attachment_78318\" style=\"width: 796px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-78318\" decoding=\"async\" loading=\"lazy\" class=\" wp-image-78318\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.24.27\u202fAM.png\" alt=\"RBAC\" width=\"786\" height=\"427\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.24.27\u202fAM.png 1664w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.24.27\u202fAM-300x163.png 300w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.24.27\u202fAM-1024x556.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.24.27\u202fAM-768x417.png 768w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.24.27\u202fAM-1536x834.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.24.27\u202fAM-624x339.png 624w\" sizes=\"(max-width: 786px) 100vw, 786px\" \/><p id=\"caption-attachment-78318\" class=\"wp-caption-text\"><span style=\"color: #000000;\">rbac.yaml<\/span><\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #000000;\">Lets apply this &#8211;<\/span><\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-78326\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-7.41.07\u202fAM.png\" alt=\"kubectl\" width=\"442\" height=\"60\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-7.41.07\u202fAM.png 442w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-7.41.07\u202fAM-300x41.png 300w\" sizes=\"(max-width: 442px) 100vw, 442px\" \/><\/p>\n<h2><span style=\"color: #000000;\">Step 3: Now, Register this Runner in GitLab<\/span><\/h2>\n<p><span style=\"color: #000000;\">Lets, create the new project runner-<\/span><\/p>\n<ol>\n<li><span style=\"color: #000000;\">Go to <strong>GitLab project Settings<\/strong> \u2192 <strong>CI\/CD<\/strong> \u2192 <strong>Runners<\/strong><\/span><\/li>\n<li><span style=\"color: #000000;\">Click on New project runner<\/span><\/li>\n<li><span style=\"color: #000000;\">Add tags like kubernetes. These tags are how your <strong>.gitlab-ci.yml<\/strong> assigns jobs to this runner<\/span><\/li>\n<li><span style=\"color: #000000;\">Give it a description and click Create runner<\/span><\/li>\n<li><span style=\"color: #000000;\">Now, Copy the <strong>Gitlab registration token<\/strong> that appears (it starts with <strong>glrt<\/strong>-). Make sure you copy that as you won&#8217;t seeing it again.<\/span><\/li>\n<\/ol>\n<div id=\"attachment_78319\" style=\"width: 732px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-78319\" decoding=\"async\" loading=\"lazy\" class=\" wp-image-78319\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.37.52\u202fAM.png\" alt=\"runner\" width=\"722\" height=\"396\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.37.52\u202fAM.png 2790w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.37.52\u202fAM-300x165.png 300w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.37.52\u202fAM-1024x562.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.37.52\u202fAM-768x422.png 768w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.37.52\u202fAM-1536x843.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.37.52\u202fAM-2048x1125.png 2048w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.37.52\u202fAM-624x343.png 624w\" sizes=\"(max-width: 722px) 100vw, 722px\" \/><p id=\"caption-attachment-78319\" class=\"wp-caption-text\"><span style=\"color: #000000;\">runner<\/span><\/p><\/div>\n<h2><span style=\"color: #000000;\">Step 4: Let&#8217;s Deploy the Runner with Helm<\/span><\/h2>\n<p><span style=\"color: #000000;\">create a file with name gitlab-runner.yaml, use the below configuration Replace &#8220;glrt-REPLACE_ME&#8221; with your actual token created in <code>Step 3<\/code><\/span><\/p>\n<div id=\"attachment_78320\" style=\"width: 752px\" class=\"wp-caption alignleft\"><img aria-describedby=\"caption-attachment-78320\" decoding=\"async\" loading=\"lazy\" class=\" wp-image-78320\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.49.34\u202fAM.png\" alt=\"Runner Configuration\" width=\"742\" height=\"888\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.49.34\u202fAM.png 1508w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.49.34\u202fAM-250x300.png 250w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.49.34\u202fAM-855x1024.png 855w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.49.34\u202fAM-768x920.png 768w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.49.34\u202fAM-1283x1536.png 1283w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-6.49.34\u202fAM-624x747.png 624w\" sizes=\"(max-width: 742px) 100vw, 742px\" \/><p id=\"caption-attachment-78320\" class=\"wp-caption-text\"><span style=\"color: #000000;\">Runner Configuration<\/span><\/p><\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #000000;\">Install the Helm chart with below command &#8211;<\/span><\/p>\n<pre><span style=\"color: #000000;\">helm install gitlab-runner \\\r\n--namespace gitlab \\\r\n--version 0.80.0 \\\r\n-f gitlab-runner.yaml \\\r\ngitlab\/gitlab-runner<\/span><\/pre>\n<p><span style=\"color: #000000;\">check if the pods are up and running &#8211;<\/span><\/p>\n<pre><span style=\"color: #000000;\">kubectl get pods -n gitlab --watch<\/span><\/pre>\n<h2><span style=\"color: #000000;\">Step 5: Test Your Runner<\/span><\/h2>\n<p><span style=\"color: #000000;\">Add the below <strong>.gitlab-ci.yml<\/strong> to your repository linked to your GitLab account to check if everything is working as expected.<\/span><\/p>\n<p><span style=\"color: #000000;\"><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-78323\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-7.14.23\u202fAM.png\" alt=\"sample gitlab cicd\" width=\"506\" height=\"188\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-7.14.23\u202fAM.png 986w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-7.14.23\u202fAM-300x111.png 300w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-7.14.23\u202fAM-768x285.png 768w, \/blog\/wp-ttn-blog\/uploads\/2026\/03\/Screenshot-2026-03-11-at-7.14.23\u202fAM-624x232.png 624w\" sizes=\"(max-width: 506px) 100vw, 506px\" \/><\/span><\/p>\n<h2><span style=\"font-size: 1.28571rem; color: #000000;\">Conclusion-<\/span><\/h2>\n<p><span style=\"color: #000000;\">So, If we need to scale our CI\/CD then running the GitLab Runner on Kubernetes is an effective way rather than relying on the traditional executioners the <strong>GitLab Runner Kubernetes Executor<\/strong>\u00a0provides pipelines execute dynamically, allowing it to significantly optimize infrastructure costs by utilizing cluster resources only when they are truly needed and provides a highly scalable pipeline architecture suitable for modern DevOps teams.<\/span><\/p>\n<p><span style=\"color: #000000;\">Kubernetes executor &#8211; <a href=\"https:\/\/docs.gitlab.com\/runner\/executors\/kuberneteshttps:\/\/docs.gitlab.com\/runner\/executors\/kubernetes\">https:\/\/docs.gitlab.com\/runner\/executors\/kubernetes<\/a><\/span><br \/>\n<span style=\"color: #000000;\">Troubleshooting the Kubernetes executor &#8211; <a href=\"https:\/\/docs.gitlab.com\/runner\/executors\/kubernetes\/troubleshooting\/\">https:\/\/docs.gitlab.com\/runner\/executors\/kubernetes\/troubleshooting\/<\/a><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction As the project grows and traditional CI runners running on static virtual machines or shared runners often struggle to keep up with increasing workloads. Jobs may queue up, builds start taking longer than expected and developers are left waiting. what&#8217;s the solution? So, instead of using traditional executioners we can leverage the GitLab Runner [&hellip;]<\/p>\n","protected":false},"author":1834,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":33},"categories":[5877],"tags":[4252,6073,8461],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/78321"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1834"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=78321"}],"version-history":[{"count":4,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/78321\/revisions"}],"predecessor-version":[{"id":78536,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/78321\/revisions\/78536"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=78321"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=78321"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=78321"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}