[{"data":1,"prerenderedAt":758},["ShallowReactive",2],{"/en-us/blog/best-practices-for-kubernetes-runners":3,"navigation-en-us":36,"banner-en-us":463,"footer-en-us":480,"Sara Kassabian":725,"next-steps-en-us":737,"footer-source-/en-us/blog/best-practices-for-kubernetes-runners/":752},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":26,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},"/en-us/blog/best-practices-for-kubernetes-runners","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Best practices to keep your Kubernetes runners moving","In a presentation at GitLab Commit San Francisco, a senior software engineer from F5 Networks shares some best practices for working with Kubernetes runners.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749681341/Blog/Hero%20Images/trackandfield.jpg","https://about.gitlab.com/blog/best-practices-for-kubernetes-runners","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Best practices to keep your Kubernetes runners moving\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Sara Kassabian\"}],\n        \"datePublished\": \"2020-05-27\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Sara Kassabian","2020-05-27","Sometimes in software engineering, you have to learn the hard way. GitLab CI\nis extremely powerful and flexible, but it’s also easy to make mistakes that\ncould take out a GitLab runner, which can clog up Sidekiq and bring down\nyour entire GitLab instance.\n\n\nLuckily, Sean Smith, senior software engineer for F5 Networks has been\nthrough it, and summarizes some of their learnings in [his talk at GitLab\nCommit San Francisco](https://www.youtube.com/watch?v=Hks5ElUxkP4). In the\npresentation, Sean goes in-depth about a past incident that clogged up F5\nNetwork's GitLab runner, and shares tips on setting limits for Kubernetes\n(K8s) runners.\n\n\nSean is a GitLab administrator for [F5 Networks](https://www.f5.com/), a\ncompany with about 1,800 users worldwide running 7,500 projects each month –\nexcluding forks. That’s roughly 350,000 - 400,000 CI jobs going through the\nK8s runners each month. Until some recent hires, there were only three\nengineers to handle it all.\n\n\nInstead of running a giant GitLab instance on one VM, F5 broke up their\ninstance into seven different servers: Two HA web servers, one PostGres\nserver, PostGres replica, Sidekiq, Gitaly (our Git filesystem), and Redis.\n\n\n## Keep your GitLab runners up and moving\n\n\nF5 uses two types of GitLab runners:\n\n\n*   Kubernetes: About 90% of F5 jobs go through K8s\n\n*   Docker: Docker machine is run on-prem and in the cloud\n\n\n**Why use Docker?** F5 uses Docker to configure cluster networks in\ndifferent jobs as well as for unit testing. Since the Docker machine can run\non-prem and also in the cloud, it’s easy to have a VM dedicated to the job\nthat allows you to manage those Docker images and Docker containers and set\nup your cluster networking topology within Docker, so you can run your tests\nand tear it down afterward without affecting other users. This isn’t\nsomething that is really possible in Kubernetes runners.\n\n\nOtherwise, F5 Networks uses Kubernetes, but keeping your K8s up and running\nisn’t necessarily foolproof.\n\n\n### CI jobs can spawn\n\n\nSometimes, a seemingly benign coding error can create unanticipated\nconsequences for your Kubernetes runners.\n\n\nOne time, an F5 Engineer decided to use a GitLab CI job to automatically\nconfigure different settings on various jobs and projects. It made sense to\nconfigure using GitLab CI because the engineer wanted to be able to use [Git\nfor version control](/topics/version-control/). Version control makes it\neasier for the team to iterate on the code transparently. He wrote the code\nto run the job.\n\n\nBut, he didn’t read the fine print in the library he was using. The code he\nwrote looked for the project ID, and if it found the project ID, runs the\npipeline once per hour at the 30-minute mark. The assumption was that if\nthere was already a matching scheduled task, the create function would not\ncreate a duplicate. Unfortunately, this was not the case. The code he ran\ncaused the number of CI jobs to grow exponentially.\n\n\n![The code that clogged the K8s runner with GitLab CI jobs for F5\nNetworks](https://about.gitlab.com/images/blogimages/problemcode.png){:\n.shadow}\n\nThe code that clogged the K8s runner with GitLab CI jobs for F5 Networks.\nCan you see the problem yet?\n\n{: .note.text-center}\n\n\n\"You schedule a job, then next you schedule another job so now you've got\ntwo jobs scheduled, and then you've got four jobs scheduled, and then eight,\nafter 10 iterations, you get around the 1,024 jobs scheduled and after\n1,532,000 jobs, if this was allowed to run for 24 hours, you would end up\nwith 16.7 million jobs being scheduled by the 24th hour,\" says Sean.\n\n\nIn short: Chaos. Remember, F5 Networks has a CI pipeline capacity of 350,000\nto 400,000 jobs per month, so 16.7 million jobs in 24 hours could easily\nclog the system, taking down the K8s nodes, as well as GitLab nodes.\n\n\nLuckily, there’s a simple enough fix. First, identify which project is\ncausing the problem, and disable CI on the project so it can’t create any\nnew jobs. Next, kill all the pending jobs by [running this\nsnippet](https://gitlab.com/snippets/1924269).\n\n\n```\n\n# gitlab-rails console\n\np = Project.find_by_full_path(‘rogue-group/rogue-project’)\n\nCi::Pipeline.where(project_id: p.id).where(status: ‘pending’).each {|p|\np.cancel}\n\nexit\n\n```\n\n\nIt’s really a judgment call whether to kill a running job or not. If a job\nis currently running and is going to take all of 30 seconds then maybe don’t\nbother killing it, but if the job is going to take 30 minutes then consider\nkilling it to free up resources for your users.\n\n\nF5 learned a lesson here and set up a monitoring alert to help ensure the\njob queue doesn’t back up like that again. The Cron job checks to make sure\nF5 is not exceeding a preestablished threshold on the number of jobs in a\npending state. The alert links to a dashboard and also includes the full\nplaybook for how to resolve the problem (because let’s face it, nobody is at\ntheir best when troubleshooting bleary-eyed at 3 a.m.). At first there were\nsome false positives, but now the alerting has been fine-tuned and the\nsystem saved F5 from two outages so far.\n\n\n### Push it to the limit\n\n\nThe fact is, nobody has an unlimited cloud budget, and even if you're\non-prem, resources are even more constrained for users that rely upon\nhardware. Sean says that F5 soon realized that, to meet the needs of all\nusers, sensible limits had to be established so one or two mega-users didn't\ndevour all their resources. He has some tips on how to set limits in your\nKubernetes and GitLab runners.\n\n\nWhile some users may be disgruntled that cloud limits exist and are\nenforced, the best method is to keep an open dialogue with users about the\nlimits while recognizing that projects expand and grow over a period of\ntime.\n\n\nFortunately you can set the limits yourself and don’t have to rely on the\ngoodwill of your users to conserve CPU. Kubernetes allows limits by default,\nand GitLab supports K8s request and limits. The K8s scheduler uses requests\nto determine which nodes to run the workload on. Limits will kill a job if\nthe job exceeds the predefined limit – there can be different requests and\nlimits but if requests aren’t specified and limits are, the scheduler will\nuse the limits to determine the request value.\n\n\n[Take a peek at what F5 configured the limits for their Kubernetes GitLab\nrunner](https://gitlab.com/snippets/1926912).\n\n\n```ruby\n\nconcurrent = 200\n\nlog_format = \"json\"\n\n[[runners]]\n  name = \"Kubernetes Gitlab Runner\"\n  url = \"https://gitlab.example.com/ci\"\n  token = \"insert token here\"\n  executor = \"kubernetes\"\n  [runners.kubernetes]\n    namespace = \"gitlab-runner\"\n    service-account = \"gitlab-runner-user\"\n    pull_policy = \"always\"\n\n    # build container\n    cpu_limit = \"2\"\n    memory_limit = \"6Gi\"\n\n    # service containers\n    service_cpu_limit = \"1\"\n    service_memory_limit = \"1Gi\"\n\n    # helper container\n    helper_cpu_limit = \"1\"\n    helper_memory_limit = \"1Gi\"\n```\n\n\n\"We have got currency of 200 jobs, so it will at max spawn 200 jobs and\nyou'll see that we are limiting the CPU use on the build container to two\nand memory to six gigabytes, and on the helper and service CPU and memory\nlimits, we have one CPU and one gig of memory each,\" says Sean. \"And so it\ngives you that flexibility to break it out because generally, you don't\nnecessarily need as much CPU or as much memory on a service that you're\nspending up in your CI job.\"\n\n\n## What comes first: Setting up Kubernetes runners or establishing limits?\n\n\n[DevOps](/topics/devops/) is a data-driven practice, so the idea of setting\nlimits to conserve resources without any underlying data about what users\nare doing can seem counterintuitive. If you’re migrating to Kubernetes\nrunners from a Docker runner or a shell runner, it’s easy enough to\nextrapolate the numbers to establish limits as you set up your Kuberntes\nrunners.\n\n\nIf you’re brand-new to GitLab and GitLab CI, then it’s kind of a shot in the\ndark. Think about your bills and resource constraints: How much memory and\nCPU is available? Is anything else running on your K8s cluster. Chances are,\nyour guesses will be incorrect – but that’s OK.\n\n\nIt might sound obvious, but if you’re running a hosted application on the\nsame K8s cluster as your GitLab CI jobs, don’t set limits based on the\ncapacity of a full K8s cluster. Ideally, you’d have a separate K8s cluster\nfor GitLab CI jobs, but that isn’t always possible.\n\n\n### How F5 Networks did it\n\n\nF5 Networks started with a small team of roughly 50 people and maybe 100\nprojects in GitLab – so setting a limit on K8s wasn’t a major concern until\nthe company and, as a result, projects, started to grow.\n\n\nOnce it came time to set limits to their preexisting K8s runners, the first\nstep was to enable the K8s metric server to monitor how their users consume\nresources. The next step was to determine what users are doing. Sean\nrecommends using a tool like Grafana or Prometheus, which has a native\nintegration within GitLab (although, F5 used a tool called K9), to extract\nthe data from the K8s metric server and display it on some sort of dashboard\nusing Grafana or Prometheus.\n\n\n## Some more tips for Kubernetes runners\n\n\n### Cutting them off: Enforcing limits\n\n\nOnce a user hits their limit, most of the time the end result is their job\ngets killed. Usually the user will notice a mistake, go in, and fix their\ncode, but most likely they will just ask for more resources.\n\n\nThe best way to determine whether or not to allocate more of your finite\nresources to a user is to determine need, Sean explains. Ask the user to\nreturn to you with concrete numbers about the amount of RAM or CPU they\nrequire. But if you don’t have the resources, then don’t overextend\nyourselves to the detriment of your other users.\n\n\n### Use labels to reveal more data\n\n\n[Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set)\nmake it easier to identify workloads in Kubernetes, and can be expanded to\nenvironmental variables within GitLab, for example, job = \"$CI_JOB_ID\" and\nproject = \"$CI_PROJECT_ID\". Labels can be used by admins who are manually\ndoing Quebectal commands against K8s or they can be used in reporting tools\nlike Prometheus or Grafana for setting limits. But labels are the most\nvaluable when it comes to debugging purposes.\n\n\nBear in mind, labels are finicky in Kubernetes. [There are certain\ncharacters (stay away from \"?\") that can cause jobs to\nfail](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4565). There is a\n63 character limit on labels. If there is an unsupported character or the\nlabel is too long, the job won’t start. There won’t be a really good\nindication as to why your job wouldn’t start either, which can be a pain for\ntroubleshooting. [Bookmark this page to learn more about labels in\nKubernetes](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set)\n(including its limitations).\n\n\nGitLab users that run on K8s need to be cautious not to overburden the\nrunner with GitLab CI jobs, and ought to consider setting limits on CPU to\nconserve valuable resources.\n\n\nWant to learn more about how F5 manages their Kubernetes runners on their\nGitLab instance? Watch Sean's presentation at GitLab Commit San Francisco in\nthe video below.\n\n\n\u003C!-- blank line -->\n\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube-nocookie.com/embed/Hks5ElUxkP4\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\n\u003C!-- blank line -->\n\n\n## Learn more\n\n\n* [Read on](/solutions/kubernetes/) to learn more about how GitLab and\nKubernetes work together, and explore our plans for future integration with\nKubernetes.\n\n\n* Explore the official documentation on [Kubernetes\nexecutor](https://docs.gitlab.com/runner/executors/kubernetes.html), which\ncovers everything from choosing options in your configuration file to giving\nGitLab Runner access to the Kubernetes API, environment variables, volumes,\nhelper containers, security context, privileged mode, secret volume, and\nremoving old runner pods.\n\n\nCover Photo by [Kolleen\nGladden](https://unsplash.com/@rockthechaos?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)\non\n[Unsplash](https://unsplash.com/s/photos/track-and-field?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)\n\n{: .note.text-center}\n","engineering",[23,24,25],"kubernetes","CI/CD","user stories",{"slug":27,"featured":6,"template":28},"best-practices-for-kubernetes-runners","BlogPost","content:en-us:blog:best-practices-for-kubernetes-runners.yml","yaml","Best Practices For Kubernetes Runners","content","en-us/blog/best-practices-for-kubernetes-runners.yml","en-us/blog/best-practices-for-kubernetes-runners","yml",{"_path":37,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":39,"_id":459,"_type":30,"title":460,"_source":32,"_file":461,"_stem":462,"_extension":35},"/shared/en-us/main-navigation","en-us",{"logo":40,"freeTrial":45,"sales":50,"login":55,"items":60,"search":390,"minimal":421,"duo":440,"pricingDeployment":449},{"config":41},{"href":42,"dataGaName":43,"dataGaLocation":44},"/","gitlab logo","header",{"text":46,"config":47},"Get free trial",{"href":48,"dataGaName":49,"dataGaLocation":44},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":51,"config":52},"Talk to sales",{"href":53,"dataGaName":54,"dataGaLocation":44},"/sales/","sales",{"text":56,"config":57},"Sign in",{"href":58,"dataGaName":59,"dataGaLocation":44},"https://gitlab.com/users/sign_in/","sign in",[61,105,201,206,311,371],{"text":62,"config":63,"cards":65,"footer":88},"Platform",{"dataNavLevelOne":64},"platform",[66,72,80],{"title":62,"description":67,"link":68},"The most comprehensive AI-powered DevSecOps Platform",{"text":69,"config":70},"Explore our Platform",{"href":71,"dataGaName":64,"dataGaLocation":44},"/platform/",{"title":73,"description":74,"link":75},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":76,"config":77},"Meet GitLab Duo",{"href":78,"dataGaName":79,"dataGaLocation":44},"/gitlab-duo/","gitlab duo ai",{"title":81,"description":82,"link":83},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":84,"config":85},"Learn more",{"href":86,"dataGaName":87,"dataGaLocation":44},"/why-gitlab/","why gitlab",{"title":89,"items":90},"Get started with",[91,96,101],{"text":92,"config":93},"Platform Engineering",{"href":94,"dataGaName":95,"dataGaLocation":44},"/solutions/platform-engineering/","platform engineering",{"text":97,"config":98},"Developer Experience",{"href":99,"dataGaName":100,"dataGaLocation":44},"/developer-experience/","Developer experience",{"text":102,"config":103},"MLOps",{"href":104,"dataGaName":102,"dataGaLocation":44},"/topics/devops/the-role-of-ai-in-devops/",{"text":106,"left":107,"config":108,"link":110,"lists":114,"footer":183},"Product",true,{"dataNavLevelOne":109},"solutions",{"text":111,"config":112},"View all Solutions",{"href":113,"dataGaName":109,"dataGaLocation":44},"/solutions/",[115,139,162],{"title":116,"description":117,"link":118,"items":123},"Automation","CI/CD and automation to accelerate deployment",{"config":119},{"icon":120,"href":121,"dataGaName":122,"dataGaLocation":44},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[124,127,131,135],{"text":24,"config":125},{"href":126,"dataGaLocation":44,"dataGaName":24},"/solutions/continuous-integration/",{"text":128,"config":129},"AI-Assisted Development",{"href":78,"dataGaLocation":44,"dataGaName":130},"AI assisted development",{"text":132,"config":133},"Source Code Management",{"href":134,"dataGaLocation":44,"dataGaName":132},"/solutions/source-code-management/",{"text":136,"config":137},"Automated Software Delivery",{"href":121,"dataGaLocation":44,"dataGaName":138},"Automated software delivery",{"title":140,"description":141,"link":142,"items":147},"Security","Deliver code faster without compromising security",{"config":143},{"href":144,"dataGaName":145,"dataGaLocation":44,"icon":146},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[148,152,157],{"text":149,"config":150},"Application Security Testing",{"href":144,"dataGaName":151,"dataGaLocation":44},"Application security testing",{"text":153,"config":154},"Software Supply Chain Security",{"href":155,"dataGaLocation":44,"dataGaName":156},"/solutions/supply-chain/","Software supply chain security",{"text":158,"config":159},"Software Compliance",{"href":160,"dataGaName":161,"dataGaLocation":44},"/solutions/software-compliance/","software compliance",{"title":163,"link":164,"items":169},"Measurement",{"config":165},{"icon":166,"href":167,"dataGaName":168,"dataGaLocation":44},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[170,174,178],{"text":171,"config":172},"Visibility & Measurement",{"href":167,"dataGaLocation":44,"dataGaName":173},"Visibility and Measurement",{"text":175,"config":176},"Value Stream Management",{"href":177,"dataGaLocation":44,"dataGaName":175},"/solutions/value-stream-management/",{"text":179,"config":180},"Analytics & Insights",{"href":181,"dataGaLocation":44,"dataGaName":182},"/solutions/analytics-and-insights/","Analytics and insights",{"title":184,"items":185},"GitLab for",[186,191,196],{"text":187,"config":188},"Enterprise",{"href":189,"dataGaLocation":44,"dataGaName":190},"/enterprise/","enterprise",{"text":192,"config":193},"Small Business",{"href":194,"dataGaLocation":44,"dataGaName":195},"/small-business/","small business",{"text":197,"config":198},"Public Sector",{"href":199,"dataGaLocation":44,"dataGaName":200},"/solutions/public-sector/","public sector",{"text":202,"config":203},"Pricing",{"href":204,"dataGaName":205,"dataGaLocation":44,"dataNavLevelOne":205},"/pricing/","pricing",{"text":207,"config":208,"link":210,"lists":214,"feature":298},"Resources",{"dataNavLevelOne":209},"resources",{"text":211,"config":212},"View all resources",{"href":213,"dataGaName":209,"dataGaLocation":44},"/resources/",[215,248,270],{"title":216,"items":217},"Getting started",[218,223,228,233,238,243],{"text":219,"config":220},"Install",{"href":221,"dataGaName":222,"dataGaLocation":44},"/install/","install",{"text":224,"config":225},"Quick start guides",{"href":226,"dataGaName":227,"dataGaLocation":44},"/get-started/","quick setup checklists",{"text":229,"config":230},"Learn",{"href":231,"dataGaLocation":44,"dataGaName":232},"https://university.gitlab.com/","learn",{"text":234,"config":235},"Product documentation",{"href":236,"dataGaName":237,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":239,"config":240},"Best practice videos",{"href":241,"dataGaName":242,"dataGaLocation":44},"/getting-started-videos/","best practice videos",{"text":244,"config":245},"Integrations",{"href":246,"dataGaName":247,"dataGaLocation":44},"/integrations/","integrations",{"title":249,"items":250},"Discover",[251,256,260,265],{"text":252,"config":253},"Customer success stories",{"href":254,"dataGaName":255,"dataGaLocation":44},"/customers/","customer success stories",{"text":257,"config":258},"Blog",{"href":259,"dataGaName":5,"dataGaLocation":44},"/blog/",{"text":261,"config":262},"Remote",{"href":263,"dataGaName":264,"dataGaLocation":44},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":266,"config":267},"TeamOps",{"href":268,"dataGaName":269,"dataGaLocation":44},"/teamops/","teamops",{"title":271,"items":272},"Connect",[273,278,283,288,293],{"text":274,"config":275},"GitLab Services",{"href":276,"dataGaName":277,"dataGaLocation":44},"/services/","services",{"text":279,"config":280},"Community",{"href":281,"dataGaName":282,"dataGaLocation":44},"/community/","community",{"text":284,"config":285},"Forum",{"href":286,"dataGaName":287,"dataGaLocation":44},"https://forum.gitlab.com/","forum",{"text":289,"config":290},"Events",{"href":291,"dataGaName":292,"dataGaLocation":44},"/events/","events",{"text":294,"config":295},"Partners",{"href":296,"dataGaName":297,"dataGaLocation":44},"/partners/","partners",{"backgroundColor":299,"textColor":300,"text":301,"image":302,"link":306},"#2f2a6b","#fff","Insights for the future of software development",{"altText":303,"config":304},"the source promo card",{"src":305},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":307,"config":308},"Read the latest",{"href":309,"dataGaName":310,"dataGaLocation":44},"/the-source/","the source",{"text":312,"config":313,"lists":315},"Company",{"dataNavLevelOne":314},"company",[316],{"items":317},[318,323,329,331,336,341,346,351,356,361,366],{"text":319,"config":320},"About",{"href":321,"dataGaName":322,"dataGaLocation":44},"/company/","about",{"text":324,"config":325,"footerGa":328},"Jobs",{"href":326,"dataGaName":327,"dataGaLocation":44},"/jobs/","jobs",{"dataGaName":327},{"text":289,"config":330},{"href":291,"dataGaName":292,"dataGaLocation":44},{"text":332,"config":333},"Leadership",{"href":334,"dataGaName":335,"dataGaLocation":44},"/company/team/e-group/","leadership",{"text":337,"config":338},"Team",{"href":339,"dataGaName":340,"dataGaLocation":44},"/company/team/","team",{"text":342,"config":343},"Handbook",{"href":344,"dataGaName":345,"dataGaLocation":44},"https://handbook.gitlab.com/","handbook",{"text":347,"config":348},"Investor relations",{"href":349,"dataGaName":350,"dataGaLocation":44},"https://ir.gitlab.com/","investor relations",{"text":352,"config":353},"Trust Center",{"href":354,"dataGaName":355,"dataGaLocation":44},"/security/","trust center",{"text":357,"config":358},"AI Transparency Center",{"href":359,"dataGaName":360,"dataGaLocation":44},"/ai-transparency-center/","ai transparency center",{"text":362,"config":363},"Newsletter",{"href":364,"dataGaName":365,"dataGaLocation":44},"/company/contact/","newsletter",{"text":367,"config":368},"Press",{"href":369,"dataGaName":370,"dataGaLocation":44},"/press/","press",{"text":372,"config":373,"lists":374},"Contact us",{"dataNavLevelOne":314},[375],{"items":376},[377,380,385],{"text":51,"config":378},{"href":53,"dataGaName":379,"dataGaLocation":44},"talk to sales",{"text":381,"config":382},"Support portal",{"href":383,"dataGaName":384,"dataGaLocation":44},"https://support.gitlab.com","support portal",{"text":386,"config":387},"Customer portal",{"href":388,"dataGaName":389,"dataGaLocation":44},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":391,"login":392,"suggestions":399},"Close",{"text":393,"link":394},"To search repositories and projects, login to",{"text":395,"config":396},"gitlab.com",{"href":58,"dataGaName":397,"dataGaLocation":398},"search login","search",{"text":400,"default":401},"Suggestions",[402,404,408,410,414,418],{"text":73,"config":403},{"href":78,"dataGaName":73,"dataGaLocation":398},{"text":405,"config":406},"Code Suggestions (AI)",{"href":407,"dataGaName":405,"dataGaLocation":398},"/solutions/code-suggestions/",{"text":24,"config":409},{"href":126,"dataGaName":24,"dataGaLocation":398},{"text":411,"config":412},"GitLab on AWS",{"href":413,"dataGaName":411,"dataGaLocation":398},"/partners/technology-partners/aws/",{"text":415,"config":416},"GitLab on Google Cloud",{"href":417,"dataGaName":415,"dataGaLocation":398},"/partners/technology-partners/google-cloud-platform/",{"text":419,"config":420},"Why GitLab?",{"href":86,"dataGaName":419,"dataGaLocation":398},{"freeTrial":422,"mobileIcon":427,"desktopIcon":432,"secondaryButton":435},{"text":423,"config":424},"Start free trial",{"href":425,"dataGaName":49,"dataGaLocation":426},"https://gitlab.com/-/trials/new/","nav",{"altText":428,"config":429},"Gitlab Icon",{"src":430,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":428,"config":433},{"src":434,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":436,"config":437},"Get Started",{"href":438,"dataGaName":439,"dataGaLocation":426},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":441,"mobileIcon":445,"desktopIcon":447},{"text":442,"config":443},"Learn more about GitLab Duo",{"href":78,"dataGaName":444,"dataGaLocation":426},"gitlab duo",{"altText":428,"config":446},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":448},{"src":434,"dataGaName":431,"dataGaLocation":426},{"freeTrial":450,"mobileIcon":455,"desktopIcon":457},{"text":451,"config":452},"Back to pricing",{"href":204,"dataGaName":453,"dataGaLocation":426,"icon":454},"back to pricing","GoBack",{"altText":428,"config":456},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":458},{"src":434,"dataGaName":431,"dataGaLocation":426},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":464,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"title":465,"button":466,"image":471,"config":475,"_id":477,"_type":30,"_source":32,"_file":478,"_stem":479,"_extension":35},"/shared/en-us/banner","is now in public beta!",{"text":467,"config":468},"Try the Beta",{"href":469,"dataGaName":470,"dataGaLocation":44},"/gitlab-duo/agent-platform/","duo banner",{"altText":472,"config":473},"GitLab Duo Agent Platform",{"src":474},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":476},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":481,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":482,"_id":721,"_type":30,"title":722,"_source":32,"_file":723,"_stem":724,"_extension":35},"/shared/en-us/main-footer",{"text":483,"source":484,"edit":490,"contribute":495,"config":500,"items":505,"minimal":713},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":485,"config":486},"View page source",{"href":487,"dataGaName":488,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":491,"config":492},"Edit this page",{"href":493,"dataGaName":494,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":496,"config":497},"Please contribute",{"href":498,"dataGaName":499,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":501,"facebook":502,"youtube":503,"linkedin":504},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[506,553,606,650,679],{"title":202,"links":507,"subMenu":522},[508,512,517],{"text":509,"config":510},"View plans",{"href":204,"dataGaName":511,"dataGaLocation":489},"view plans",{"text":513,"config":514},"Why Premium?",{"href":515,"dataGaName":516,"dataGaLocation":489},"/pricing/premium/","why premium",{"text":518,"config":519},"Why Ultimate?",{"href":520,"dataGaName":521,"dataGaLocation":489},"/pricing/ultimate/","why ultimate",[523],{"title":524,"links":525},"Contact Us",[526,529,531,533,538,543,548],{"text":527,"config":528},"Contact sales",{"href":53,"dataGaName":54,"dataGaLocation":489},{"text":381,"config":530},{"href":383,"dataGaName":384,"dataGaLocation":489},{"text":386,"config":532},{"href":388,"dataGaName":389,"dataGaLocation":489},{"text":534,"config":535},"Status",{"href":536,"dataGaName":537,"dataGaLocation":489},"https://status.gitlab.com/","status",{"text":539,"config":540},"Terms of use",{"href":541,"dataGaName":542,"dataGaLocation":489},"/terms/","terms of use",{"text":544,"config":545},"Privacy statement",{"href":546,"dataGaName":547,"dataGaLocation":489},"/privacy/","privacy statement",{"text":549,"config":550},"Cookie preferences",{"dataGaName":551,"dataGaLocation":489,"id":552,"isOneTrustButton":107},"cookie preferences","ot-sdk-btn",{"title":106,"links":554,"subMenu":562},[555,559],{"text":556,"config":557},"DevSecOps platform",{"href":71,"dataGaName":558,"dataGaLocation":489},"devsecops platform",{"text":128,"config":560},{"href":78,"dataGaName":561,"dataGaLocation":489},"ai-assisted development",[563],{"title":564,"links":565},"Topics",[566,571,576,581,586,591,596,601],{"text":567,"config":568},"CICD",{"href":569,"dataGaName":570,"dataGaLocation":489},"/topics/ci-cd/","cicd",{"text":572,"config":573},"GitOps",{"href":574,"dataGaName":575,"dataGaLocation":489},"/topics/gitops/","gitops",{"text":577,"config":578},"DevOps",{"href":579,"dataGaName":580,"dataGaLocation":489},"/topics/devops/","devops",{"text":582,"config":583},"Version Control",{"href":584,"dataGaName":585,"dataGaLocation":489},"/topics/version-control/","version control",{"text":587,"config":588},"DevSecOps",{"href":589,"dataGaName":590,"dataGaLocation":489},"/topics/devsecops/","devsecops",{"text":592,"config":593},"Cloud Native",{"href":594,"dataGaName":595,"dataGaLocation":489},"/topics/cloud-native/","cloud native",{"text":597,"config":598},"AI for Coding",{"href":599,"dataGaName":600,"dataGaLocation":489},"/topics/devops/ai-for-coding/","ai for coding",{"text":602,"config":603},"Agentic AI",{"href":604,"dataGaName":605,"dataGaLocation":489},"/topics/agentic-ai/","agentic ai",{"title":607,"links":608},"Solutions",[609,611,613,618,622,625,629,632,634,637,640,645],{"text":149,"config":610},{"href":144,"dataGaName":149,"dataGaLocation":489},{"text":138,"config":612},{"href":121,"dataGaName":122,"dataGaLocation":489},{"text":614,"config":615},"Agile development",{"href":616,"dataGaName":617,"dataGaLocation":489},"/solutions/agile-delivery/","agile delivery",{"text":619,"config":620},"SCM",{"href":134,"dataGaName":621,"dataGaLocation":489},"source code management",{"text":567,"config":623},{"href":126,"dataGaName":624,"dataGaLocation":489},"continuous integration & delivery",{"text":626,"config":627},"Value stream management",{"href":177,"dataGaName":628,"dataGaLocation":489},"value stream management",{"text":572,"config":630},{"href":631,"dataGaName":575,"dataGaLocation":489},"/solutions/gitops/",{"text":187,"config":633},{"href":189,"dataGaName":190,"dataGaLocation":489},{"text":635,"config":636},"Small business",{"href":194,"dataGaName":195,"dataGaLocation":489},{"text":638,"config":639},"Public sector",{"href":199,"dataGaName":200,"dataGaLocation":489},{"text":641,"config":642},"Education",{"href":643,"dataGaName":644,"dataGaLocation":489},"/solutions/education/","education",{"text":646,"config":647},"Financial services",{"href":648,"dataGaName":649,"dataGaLocation":489},"/solutions/finance/","financial services",{"title":207,"links":651},[652,654,656,658,661,663,665,667,669,671,673,675,677],{"text":219,"config":653},{"href":221,"dataGaName":222,"dataGaLocation":489},{"text":224,"config":655},{"href":226,"dataGaName":227,"dataGaLocation":489},{"text":229,"config":657},{"href":231,"dataGaName":232,"dataGaLocation":489},{"text":234,"config":659},{"href":236,"dataGaName":660,"dataGaLocation":489},"docs",{"text":257,"config":662},{"href":259,"dataGaName":5,"dataGaLocation":489},{"text":252,"config":664},{"href":254,"dataGaName":255,"dataGaLocation":489},{"text":261,"config":666},{"href":263,"dataGaName":264,"dataGaLocation":489},{"text":274,"config":668},{"href":276,"dataGaName":277,"dataGaLocation":489},{"text":266,"config":670},{"href":268,"dataGaName":269,"dataGaLocation":489},{"text":279,"config":672},{"href":281,"dataGaName":282,"dataGaLocation":489},{"text":284,"config":674},{"href":286,"dataGaName":287,"dataGaLocation":489},{"text":289,"config":676},{"href":291,"dataGaName":292,"dataGaLocation":489},{"text":294,"config":678},{"href":296,"dataGaName":297,"dataGaLocation":489},{"title":312,"links":680},[681,683,685,687,689,691,693,697,702,704,706,708],{"text":319,"config":682},{"href":321,"dataGaName":314,"dataGaLocation":489},{"text":324,"config":684},{"href":326,"dataGaName":327,"dataGaLocation":489},{"text":332,"config":686},{"href":334,"dataGaName":335,"dataGaLocation":489},{"text":337,"config":688},{"href":339,"dataGaName":340,"dataGaLocation":489},{"text":342,"config":690},{"href":344,"dataGaName":345,"dataGaLocation":489},{"text":347,"config":692},{"href":349,"dataGaName":350,"dataGaLocation":489},{"text":694,"config":695},"Sustainability",{"href":696,"dataGaName":694,"dataGaLocation":489},"/sustainability/",{"text":698,"config":699},"Diversity, inclusion and belonging (DIB)",{"href":700,"dataGaName":701,"dataGaLocation":489},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":352,"config":703},{"href":354,"dataGaName":355,"dataGaLocation":489},{"text":362,"config":705},{"href":364,"dataGaName":365,"dataGaLocation":489},{"text":367,"config":707},{"href":369,"dataGaName":370,"dataGaLocation":489},{"text":709,"config":710},"Modern Slavery Transparency Statement",{"href":711,"dataGaName":712,"dataGaLocation":489},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":714},[715,717,719],{"text":539,"config":716},{"href":541,"dataGaName":542,"dataGaLocation":489},{"text":544,"config":718},{"href":546,"dataGaName":547,"dataGaLocation":489},{"text":549,"config":720},{"dataGaName":551,"dataGaLocation":489,"id":552,"isOneTrustButton":107},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[726],{"_path":727,"_dir":728,"_draft":6,"_partial":6,"_locale":7,"content":729,"config":732,"_id":734,"_type":30,"title":18,"_source":32,"_file":735,"_stem":736,"_extension":35},"/en-us/blog/authors/sara-kassabian","authors",{"name":18,"config":730},{"headshot":7,"ctfId":731},"skassabian",{"template":733},"BlogAuthor","content:en-us:blog:authors:sara-kassabian.yml","en-us/blog/authors/sara-kassabian.yml","en-us/blog/authors/sara-kassabian",{"_path":738,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"header":739,"eyebrow":740,"blurb":741,"button":742,"secondaryButton":746,"_id":748,"_type":30,"title":749,"_source":32,"_file":750,"_stem":751,"_extension":35},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":46,"config":743},{"href":744,"dataGaName":49,"dataGaLocation":745},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":51,"config":747},{"href":53,"dataGaName":54,"dataGaLocation":745},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":753,"content":754,"config":757,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},{"title":9,"description":10,"authors":755,"heroImage":11,"date":19,"body":20,"category":21,"tags":756},[18],[23,24,25],{"slug":27,"featured":6,"template":28},1761814420005]