[{"data":1,"prerenderedAt":703},["ShallowReactive",2],{"/en-us/blog/a-brief-history-of-gitlab-workhorse/":3,"navigation-en-us":32,"banner-en-us":449,"footer-en-us":464,"Jacob Vosmaer":675,"next-steps-en-us":688},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":22,"_id":25,"_type":26,"title":27,"_source":28,"_file":29,"_stem":30,"_extension":31},"/en-us/blog/a-brief-history-of-gitlab-workhorse","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"A Brief History of GitLab Workhorse","A Brief History of GitLab Workhorse - in this blog post I will reflect on how we got there.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749684809/Blog/Hero%20Images/gopher.jpg","https://about.gitlab.com/blog/a-brief-history-of-gitlab-workhorse","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"A Brief History of GitLab Workhorse\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Jacob Vosmaer\"}],\n        \"datePublished\": \"2016-04-12\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21},[18],"Jacob Vosmaer","2016-04-12","\nIn the past 8 months gitlab-workhorse, a 'weekend project' written in Go\ninstead of our preferred Ruby, grew from a tiny program that addressed\n`git clone` timeouts into a critical component that touches almost all\nHTTP requests to GitLab. In this blog post I will reflect on how we got\nhere.\n\n\u003C!-- more -->\n\n## Technical and personal motivations\n\nGitLab is a Ruby on Rails web application that uses the\n[Unicorn](http://unicorn.bogomips.org/) Ruby web server. I am a fan of\nUnicorn because it makes application resource leaks manageable and\nbecause it has served GitLab well for a long time by patching up\nproblems which we found or find too hard to solve 'properly'. (I am\nknown to growl at people who suggest swapping out Unicorn for another\nweb server in GitLab.)\n\nAt the same time, the design of Unicorn is incompatible with one of\nGitLab's main functions, namely Git repository access (`git clone`,\n`git push`, etc.) via HTTP(S). The reason it is incompatible is that\nUnicorn heavily relies on (relatively) short request timeouts. If you\nconfigure Unicorn to time out slowly rather than quickly then it starts\nto become a lot less pleasant to work with. A `git clone` on the other\nhand may take quite a long time if you are fetching a large Git\nrepository. In my previous role as a service engineer at GitLab I\nregularly had to explain this tension to customers. The only solution we\ncould offer was 'use Git over SSH instead'.\n\nAnother factor that led to gitlab-workhorse was my unfulfilled curiosity\nabout the [Go programming language](https://golang.org/). Go is\nsometimes credited with (or discredited for) having a strong marketing\npush behind it. The marketing worked on me: I have had a plush Go mascot\nstaring at me on my desk for almost three years now.\n\n![Gopher](https://about.gitlab.com/images/brief-history-of-gitlab-workhorse/gopher.jpg)\n\n## A weekend project gets merged into master\n\nSo one weekend in July last year I found myself with an itch to build\nsomething in Go and a lack of imagination which led me to ask myself:\ncould I rewrite\n[gitlab-grack](https://gitlab.com/gitlab-org/gitlab-grack), the GitLab\ncomponent that responds to Git HTTP clients, in Go? For the record I am\nnot proud of having used my own (non-work) time to drive development of\nthis project for the first few months, I think it sets a bad example for\nmyself and others. But that is how it went.\n\nThe result was a very short and mostly correct Go program called\n'gitlab-git-http-server' that suffered from none of the timeout issues\nthat `git clone` via Unicorn had. It integrated with GitLab in a sneaky\nway by letting NGINX divert Git HTTP requests away from Unicorn to\ngitlab-git-http-server. The required changes in the GitLab Rails\ncodebase were so minor that I could easily hide them behind a [feature\nflag](https://en.wikipedia.org/wiki/Feature_toggle). To top it off I\nannounced gitlab-git-http-server to the team on a day the CTO was on\nvacation: the ultimate sneak attack. Just kidding, but I thought it was\na funny coincidence about [Dmitriy](https://gitlab.com/dzaporozhets)'s\nvacation.\n\nThe team somehow let me 'try gitlab-git-http-server out' (read: merge it\ninto master and deploy it on our staging server) and so it got started.\nLess than a month later GitLab 7.14 shipped with gitlab-git-http-server\nbehind a feature flag. This allowed us to test it on GitLab.com for a\nmonth. Between our staging environment and GitLab.com we were able to\ncatch the worst bugs. In GitLab 8.0 (released the month after 7.14)\nwe made gitlab-git-http-server an official (and required!) component of\nGitLab.\n\nThe acceptance of gitlab-git-http-server by the team was probably helped\nby a shared understanding that GitLab's Git-over-HTTP solution was just\nnot quite cutting it, and by the fact that we already used Go for\n[gitlab-ci-multi-runner](https://gitlab.com/gitlab-org/gitlab-runner).\nBut there was no up-front decision to solve the problem of Git-over-HTTP\nat this particular time, or using these means.\n\n## Feature creep\n\nUntil now gitlab-git-http-server was a one-trick pony: it only handled\nGit HTTP clients. But as usually happens when you have a new hammer,\nother things started looking like nails. In GitLab 8.1 we changed\ngitlab-git-http-server to also handle the 'download zip' button from the\nGitLab web interface. In retrospect this seems obvious but at the time\nit felt like a big leap: in our minds, gitlab-git-http-server was a\ndaemon that understood (stateless) [HTTP Basic\nAuthentication](https://en.wikipedia.org/wiki/Basic_access_authentication)\nbut not the session cookies used by GitLab to identify individual users.\nBut a session cookie is just an HTTP header so nothing stopped\ngitlab-git-http-server from 'impersonating' a logged-in user and\ngenerating a zip file for them on the fly. I have a bit of a hard time\nexplaining now why but we thought this was very neat at the time.\n\n## Time for a new name\n\nIn GitLab 8.2 we wanted to ship two new features for which we expected\nthe same sort of Unicorn timeout problems that had plagued Git-over-HTTP\nin the past: [Git LFS](https://git-lfs.github.com/) support (developed\nby [Marin](https://gitlab.com/marin)) and [CI build\nartifacts](http://doc.gitlab.com/ce/ci/build_artifacts/README.html)\n(developed by [Kamil](https://gitlab.com/ayufan)). Both of these\nfeatures depended on users uploading and downloading arbitrarily large\nfiles.\n\nThis development brought many improvements to gitlab-git-http-server.\nFirst of all, the more people had to say or write\n'gitlab-git-http-server', the more obvious it became that the name was\ntoo awkward. And with all these new features it was also no longer\nappropriate, because the program did more than dealing with\nGit-over-HTTP. We have Marin to thank for coming up with\n'gitlab-workhorse' which I especially like because it pokes fun at\nUnicorn.\n\nIt was also a great development for gitlab-workhorse to be getting\nattention from Kamil because he is our resident Go expert. This was very\nwelcome: I felt confident enough that gitlab-workhorse functioned\ncorrectly, but I am not an experienced Go programmer. Having Kamil in\nthe game helped us make gitlab-workhorse a better Go program.\n\nFor a short while, Marin and I were on the one hand trying to implement\nfile uploads/downloads in gitlab-workhorse, while Kamil on the other\nhand was implementing the same thing for CI artifacts using NGINX\nplugins. Luckily we spotted the duplication of efforts before the code\nwent out the door so we were able to implement this in gitlab-workhorse\ntogether for GitLab 8.2.\n\nWe ended up with an especially nice solution for file downloads in\ngitlab-workhorse, inspired by the mechanism Kamil intended to use in\nNGINX: `X-Sendfile` headers. Most of the time when you want to use\ngitlab-workhorse to make something faster or more robust in GitLab you\nhave to write both Ruby code and Go code. But because [Ruby on Rails\nunderstands `X-Sendfile`\nalready](http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file),\nGitLab developers can reap the benefits of gitlab-workhorse for file\ndownloads without writing any Go code!\n\n## Betting the farm\n\nBy this time the success with which we could build new GitLab features\nby doing part of the work in gitlab-workhorse started to cause problems\nof its own. Each time we added a feature to gitlab-workhorse that meant\ndiverting more HTTP requests to gitlab-workhorse in the NGINX\nconfiguration. This complexity was hidden from people who installed\nGitLab using our [Omnibus packages](https://packages.gitlab.com/gitlab)\nbut I could tell from the gitlab-workhorse issue tracker that this was a\nrecurring source of problems for installations from source.\n\nPrior to gitlab-workhorse, NGINX served static files or forwarded\nrequests to Unicorn:\n\n    +----------+      +-------------+\n    |          |      |             |\n    |  NGINX   +----> |   Unicorn   |\n    |          |      |             |\n    +------+---+      +-------------+\n           |\n           |\n           |          +------------+\n           |          |            |\n           +--------> |   static   |\n                      |   files    |\n                      |            |\n                      +------------+\n\nNow with gitlab-workhorse in the picture, NGINX had to know which\nrequests to send to Unicorn, which to gitlab-workhorse, and which to\nstatic files.\n\n                    +--------------------+\n                    |                    |\n           +------> |  gitlab-workhorse  |\n           |        |                    |\n           |        +---------+----------+\n           |                  |\n           |                  v\n           |\n    +------+---+      +-------------+\n    |          |      |             |\n    |  NGINX   +----> |   Unicorn   |\n    |          |      |             |\n    +------+---+      +-------------+\n           |\n           |\n           |          +------------+\n           |          |            |\n           +--------> |   static   |\n                      |   files    |\n                      |            |\n                      +------------+\n\nKamil half-jokingly suggested at one point that we could route all HTTP\ntraffic to GitLab through gitlab-workhorse. Over time I started to\nbelieve this was a good idea: it would radically simplify the NGINX\nconfiguration for GitLab, and consequently make it easier to deploy\nGitLab behind other web servers (like Apache) which some people prefer\nstrongly to using NGINX. It seems the idea grew on Kamil too because we\nsoon saw a huge merge request from him to gitlab-workhorse which turned\nit into a 'smart proxy' that serves static files, injects error pages,\nimplements Git-over-HTTP plus other features, *and* proxies traffic to\nGitLab.\n\n    +-------------+         +---------------------+        +------------+\n    |             |         |                     |        |            |\n    |   NGINX     +-------> |  gitlab-workhorse   +------> |  Unicorn   |\n    |             |         |                     |        |            |\n    +-------------+         +---------------------+        +------------+\n\nThis change went out in GitLab 8.3. In little over four months\ngitlab-workhorse went from being a little helper daemon on the side to a\ntraffic cop that routes all HTTP requests going into GitLab.\n\nThis work immediately paid off in GitLab 8.4 when\n[Grzegorz](https://gitlab.com/grzesiek) added the CI artifact browsing\nfeature and GitLab 8.5 where we started serving 'raw' Git blobs via\ngitlab-workhorse. Neither of these changes forced GitLab administrators\nto update their NGINX configuration.\n\n## What to do next\n\nThe most important next step for gitlab-workhorse is to make it less\ndependent on me by getting more of my team members to contribute to the\ncode. I am trying to do this by purposely leaving some [nice\nchanges](https://gitlab.com/gitlab-org/gitlab-ce/issues/13999) for\nothers to implement.\n\nBecause of the gradual (and sneaky :) ) way gitlab-workhorse was added\nto GitLab we still have some technical debt in GitLab in the [Git HTTP\nauthentication / authorization\ncode](https://gitlab.com/gitlab-org/gitlab-ce/issues/14501). It would be\nnice to clean this up.\n\nFinally, it is sub-optimal that we still buffer Git pushes in NGINX\nbefore forwarding them to gitlab-workhorse. We could avoid this\nunncessary delay and give people who use Apache instead of NGINX a\nbetter experience if we [implement selective request buffering in\ngitlab-workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse/issues/1#note_2681403).\n\nIf you want to know what is coming next in GitLab, check out our\n[Direction](/direction/) page, or follow developments on\nour latest milestones for 8.7 [CE](https://gitlab.com/gitlab-org/gitlab-ce/milestones/23)\nand [EE](https://gitlab.com/gitlab-org/gitlab-ee/milestones/9).\n","engineering",{"slug":23,"featured":6,"template":24},"a-brief-history-of-gitlab-workhorse","BlogPost","content:en-us:blog:a-brief-history-of-gitlab-workhorse.yml","yaml","A Brief History Of Gitlab Workhorse","content","en-us/blog/a-brief-history-of-gitlab-workhorse.yml","en-us/blog/a-brief-history-of-gitlab-workhorse","yml",{"_path":33,"_dir":34,"_draft":6,"_partial":6,"_locale":7,"data":35,"_id":445,"_type":26,"title":446,"_source":28,"_file":447,"_stem":448,"_extension":31},"/shared/en-us/main-navigation","en-us",{"logo":36,"freeTrial":41,"sales":46,"login":51,"items":56,"search":386,"minimal":417,"duo":436},{"config":37},{"href":38,"dataGaName":39,"dataGaLocation":40},"/","gitlab logo","header",{"text":42,"config":43},"Get free trial",{"href":44,"dataGaName":45,"dataGaLocation":40},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":47,"config":48},"Talk to sales",{"href":49,"dataGaName":50,"dataGaLocation":40},"/sales/","sales",{"text":52,"config":53},"Sign in",{"href":54,"dataGaName":55,"dataGaLocation":40},"https://gitlab.com/users/sign_in/","sign in",[57,101,197,202,307,367],{"text":58,"config":59,"cards":61,"footer":84},"Platform",{"dataNavLevelOne":60},"platform",[62,68,76],{"title":58,"description":63,"link":64},"The most comprehensive AI-powered DevSecOps Platform",{"text":65,"config":66},"Explore our Platform",{"href":67,"dataGaName":60,"dataGaLocation":40},"/platform/",{"title":69,"description":70,"link":71},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":72,"config":73},"Meet GitLab Duo",{"href":74,"dataGaName":75,"dataGaLocation":40},"/gitlab-duo/","gitlab duo ai",{"title":77,"description":78,"link":79},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":80,"config":81},"Learn more",{"href":82,"dataGaName":83,"dataGaLocation":40},"/why-gitlab/","why gitlab",{"title":85,"items":86},"Get started with",[87,92,97],{"text":88,"config":89},"Platform Engineering",{"href":90,"dataGaName":91,"dataGaLocation":40},"/solutions/platform-engineering/","platform engineering",{"text":93,"config":94},"Developer Experience",{"href":95,"dataGaName":96,"dataGaLocation":40},"/developer-experience/","Developer experience",{"text":98,"config":99},"MLOps",{"href":100,"dataGaName":98,"dataGaLocation":40},"/topics/devops/the-role-of-ai-in-devops/",{"text":102,"left":103,"config":104,"link":106,"lists":110,"footer":179},"Product",true,{"dataNavLevelOne":105},"solutions",{"text":107,"config":108},"View all Solutions",{"href":109,"dataGaName":105,"dataGaLocation":40},"/solutions/",[111,136,158],{"title":112,"description":113,"link":114,"items":119},"Automation","CI/CD and automation to accelerate deployment",{"config":115},{"icon":116,"href":117,"dataGaName":118,"dataGaLocation":40},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[120,124,128,132],{"text":121,"config":122},"CI/CD",{"href":123,"dataGaLocation":40,"dataGaName":121},"/solutions/continuous-integration/",{"text":125,"config":126},"AI-Assisted Development",{"href":74,"dataGaLocation":40,"dataGaName":127},"AI assisted development",{"text":129,"config":130},"Source Code Management",{"href":131,"dataGaLocation":40,"dataGaName":129},"/solutions/source-code-management/",{"text":133,"config":134},"Automated Software Delivery",{"href":117,"dataGaLocation":40,"dataGaName":135},"Automated software delivery",{"title":137,"description":138,"link":139,"items":144},"Security","Deliver code faster without compromising security",{"config":140},{"href":141,"dataGaName":142,"dataGaLocation":40,"icon":143},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[145,148,153],{"text":146,"config":147},"Security & Compliance",{"href":141,"dataGaLocation":40,"dataGaName":146},{"text":149,"config":150},"Software Supply Chain Security",{"href":151,"dataGaLocation":40,"dataGaName":152},"/solutions/supply-chain/","Software supply chain security",{"text":154,"config":155},"Compliance & Governance",{"href":156,"dataGaLocation":40,"dataGaName":157},"/solutions/continuous-software-compliance/","Compliance and governance",{"title":159,"link":160,"items":165},"Measurement",{"config":161},{"icon":162,"href":163,"dataGaName":164,"dataGaLocation":40},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[166,170,174],{"text":167,"config":168},"Visibility & Measurement",{"href":163,"dataGaLocation":40,"dataGaName":169},"Visibility and Measurement",{"text":171,"config":172},"Value Stream Management",{"href":173,"dataGaLocation":40,"dataGaName":171},"/solutions/value-stream-management/",{"text":175,"config":176},"Analytics & Insights",{"href":177,"dataGaLocation":40,"dataGaName":178},"/solutions/analytics-and-insights/","Analytics and insights",{"title":180,"items":181},"GitLab for",[182,187,192],{"text":183,"config":184},"Enterprise",{"href":185,"dataGaLocation":40,"dataGaName":186},"/enterprise/","enterprise",{"text":188,"config":189},"Small Business",{"href":190,"dataGaLocation":40,"dataGaName":191},"/small-business/","small business",{"text":193,"config":194},"Public Sector",{"href":195,"dataGaLocation":40,"dataGaName":196},"/solutions/public-sector/","public sector",{"text":198,"config":199},"Pricing",{"href":200,"dataGaName":201,"dataGaLocation":40,"dataNavLevelOne":201},"/pricing/","pricing",{"text":203,"config":204,"link":206,"lists":210,"feature":294},"Resources",{"dataNavLevelOne":205},"resources",{"text":207,"config":208},"View all resources",{"href":209,"dataGaName":205,"dataGaLocation":40},"/resources/",[211,244,266],{"title":212,"items":213},"Getting started",[214,219,224,229,234,239],{"text":215,"config":216},"Install",{"href":217,"dataGaName":218,"dataGaLocation":40},"/install/","install",{"text":220,"config":221},"Quick start guides",{"href":222,"dataGaName":223,"dataGaLocation":40},"/get-started/","quick setup checklists",{"text":225,"config":226},"Learn",{"href":227,"dataGaLocation":40,"dataGaName":228},"https://university.gitlab.com/","learn",{"text":230,"config":231},"Product documentation",{"href":232,"dataGaName":233,"dataGaLocation":40},"https://docs.gitlab.com/","product documentation",{"text":235,"config":236},"Best practice videos",{"href":237,"dataGaName":238,"dataGaLocation":40},"/getting-started-videos/","best practice videos",{"text":240,"config":241},"Integrations",{"href":242,"dataGaName":243,"dataGaLocation":40},"/integrations/","integrations",{"title":245,"items":246},"Discover",[247,252,256,261],{"text":248,"config":249},"Customer success stories",{"href":250,"dataGaName":251,"dataGaLocation":40},"/customers/","customer success stories",{"text":253,"config":254},"Blog",{"href":255,"dataGaName":5,"dataGaLocation":40},"/blog/",{"text":257,"config":258},"Remote",{"href":259,"dataGaName":260,"dataGaLocation":40},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":262,"config":263},"TeamOps",{"href":264,"dataGaName":265,"dataGaLocation":40},"/teamops/","teamops",{"title":267,"items":268},"Connect",[269,274,279,284,289],{"text":270,"config":271},"GitLab Services",{"href":272,"dataGaName":273,"dataGaLocation":40},"/services/","services",{"text":275,"config":276},"Community",{"href":277,"dataGaName":278,"dataGaLocation":40},"/community/","community",{"text":280,"config":281},"Forum",{"href":282,"dataGaName":283,"dataGaLocation":40},"https://forum.gitlab.com/","forum",{"text":285,"config":286},"Events",{"href":287,"dataGaName":288,"dataGaLocation":40},"/events/","events",{"text":290,"config":291},"Partners",{"href":292,"dataGaName":293,"dataGaLocation":40},"/partners/","partners",{"backgroundColor":295,"textColor":296,"text":297,"image":298,"link":302},"#2f2a6b","#fff","Insights for the future of software development",{"altText":299,"config":300},"the source promo card",{"src":301},"/images/navigation/the-source-promo-card.svg",{"text":303,"config":304},"Read the latest",{"href":305,"dataGaName":306,"dataGaLocation":40},"/the-source/","the source",{"text":308,"config":309,"lists":311},"Company",{"dataNavLevelOne":310},"company",[312],{"items":313},[314,319,325,327,332,337,342,347,352,357,362],{"text":315,"config":316},"About",{"href":317,"dataGaName":318,"dataGaLocation":40},"/company/","about",{"text":320,"config":321,"footerGa":324},"Jobs",{"href":322,"dataGaName":323,"dataGaLocation":40},"/jobs/","jobs",{"dataGaName":323},{"text":285,"config":326},{"href":287,"dataGaName":288,"dataGaLocation":40},{"text":328,"config":329},"Leadership",{"href":330,"dataGaName":331,"dataGaLocation":40},"/company/team/e-group/","leadership",{"text":333,"config":334},"Team",{"href":335,"dataGaName":336,"dataGaLocation":40},"/company/team/","team",{"text":338,"config":339},"Handbook",{"href":340,"dataGaName":341,"dataGaLocation":40},"https://handbook.gitlab.com/","handbook",{"text":343,"config":344},"Investor relations",{"href":345,"dataGaName":346,"dataGaLocation":40},"https://ir.gitlab.com/","investor relations",{"text":348,"config":349},"Trust Center",{"href":350,"dataGaName":351,"dataGaLocation":40},"/security/","trust center",{"text":353,"config":354},"AI Transparency Center",{"href":355,"dataGaName":356,"dataGaLocation":40},"/ai-transparency-center/","ai transparency center",{"text":358,"config":359},"Newsletter",{"href":360,"dataGaName":361,"dataGaLocation":40},"/company/contact/","newsletter",{"text":363,"config":364},"Press",{"href":365,"dataGaName":366,"dataGaLocation":40},"/press/","press",{"text":368,"config":369,"lists":370},"Contact us",{"dataNavLevelOne":310},[371],{"items":372},[373,376,381],{"text":47,"config":374},{"href":49,"dataGaName":375,"dataGaLocation":40},"talk to sales",{"text":377,"config":378},"Get help",{"href":379,"dataGaName":380,"dataGaLocation":40},"/support/","get help",{"text":382,"config":383},"Customer portal",{"href":384,"dataGaName":385,"dataGaLocation":40},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":387,"login":388,"suggestions":395},"Close",{"text":389,"link":390},"To search repositories and projects, login to",{"text":391,"config":392},"gitlab.com",{"href":54,"dataGaName":393,"dataGaLocation":394},"search login","search",{"text":396,"default":397},"Suggestions",[398,400,404,406,410,414],{"text":69,"config":399},{"href":74,"dataGaName":69,"dataGaLocation":394},{"text":401,"config":402},"Code Suggestions (AI)",{"href":403,"dataGaName":401,"dataGaLocation":394},"/solutions/code-suggestions/",{"text":121,"config":405},{"href":123,"dataGaName":121,"dataGaLocation":394},{"text":407,"config":408},"GitLab on AWS",{"href":409,"dataGaName":407,"dataGaLocation":394},"/partners/technology-partners/aws/",{"text":411,"config":412},"GitLab on Google Cloud",{"href":413,"dataGaName":411,"dataGaLocation":394},"/partners/technology-partners/google-cloud-platform/",{"text":415,"config":416},"Why GitLab?",{"href":82,"dataGaName":415,"dataGaLocation":394},{"freeTrial":418,"mobileIcon":423,"desktopIcon":428,"secondaryButton":431},{"text":419,"config":420},"Start free trial",{"href":421,"dataGaName":45,"dataGaLocation":422},"https://gitlab.com/-/trials/new/","nav",{"altText":424,"config":425},"Gitlab Icon",{"src":426,"dataGaName":427,"dataGaLocation":422},"/images/brand/gitlab-logo-tanuki.svg","gitlab icon",{"altText":424,"config":429},{"src":430,"dataGaName":427,"dataGaLocation":422},"/images/brand/gitlab-logo-type.svg",{"text":432,"config":433},"Get Started",{"href":434,"dataGaName":435,"dataGaLocation":422},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":437,"mobileIcon":441,"desktopIcon":443},{"text":438,"config":439},"Learn more about GitLab Duo",{"href":74,"dataGaName":440,"dataGaLocation":422},"gitlab duo",{"altText":424,"config":442},{"src":426,"dataGaName":427,"dataGaLocation":422},{"altText":424,"config":444},{"src":430,"dataGaName":427,"dataGaLocation":422},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":450,"_dir":34,"_draft":6,"_partial":6,"_locale":7,"title":451,"button":452,"image":456,"config":459,"_id":461,"_type":26,"_source":28,"_file":462,"_stem":463,"_extension":31},"/shared/en-us/banner","is now in public beta!",{"text":80,"config":453},{"href":454,"dataGaName":455,"dataGaLocation":40},"/gitlab-duo/agent-platform/","duo banner",{"config":457},{"src":458},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":460},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":465,"_dir":34,"_draft":6,"_partial":6,"_locale":7,"data":466,"_id":671,"_type":26,"title":672,"_source":28,"_file":673,"_stem":674,"_extension":31},"/shared/en-us/main-footer",{"text":467,"source":468,"edit":474,"contribute":479,"config":484,"items":489,"minimal":663},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":469,"config":470},"View page source",{"href":471,"dataGaName":472,"dataGaLocation":473},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":475,"config":476},"Edit this page",{"href":477,"dataGaName":478,"dataGaLocation":473},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":480,"config":481},"Please contribute",{"href":482,"dataGaName":483,"dataGaLocation":473},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":485,"facebook":486,"youtube":487,"linkedin":488},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[490,513,570,599,633],{"title":58,"links":491,"subMenu":496},[492],{"text":493,"config":494},"DevSecOps platform",{"href":67,"dataGaName":495,"dataGaLocation":473},"devsecops platform",[497],{"title":198,"links":498},[499,503,508],{"text":500,"config":501},"View plans",{"href":200,"dataGaName":502,"dataGaLocation":473},"view plans",{"text":504,"config":505},"Why Premium?",{"href":506,"dataGaName":507,"dataGaLocation":473},"/pricing/premium/","why premium",{"text":509,"config":510},"Why Ultimate?",{"href":511,"dataGaName":512,"dataGaLocation":473},"/pricing/ultimate/","why ultimate",{"title":514,"links":515},"Solutions",[516,521,524,526,531,536,540,543,547,552,554,557,560,565],{"text":517,"config":518},"Digital transformation",{"href":519,"dataGaName":520,"dataGaLocation":473},"/topics/digital-transformation/","digital transformation",{"text":146,"config":522},{"href":141,"dataGaName":523,"dataGaLocation":473},"security & compliance",{"text":135,"config":525},{"href":117,"dataGaName":118,"dataGaLocation":473},{"text":527,"config":528},"Agile development",{"href":529,"dataGaName":530,"dataGaLocation":473},"/solutions/agile-delivery/","agile delivery",{"text":532,"config":533},"Cloud transformation",{"href":534,"dataGaName":535,"dataGaLocation":473},"/topics/cloud-native/","cloud transformation",{"text":537,"config":538},"SCM",{"href":131,"dataGaName":539,"dataGaLocation":473},"source code management",{"text":121,"config":541},{"href":123,"dataGaName":542,"dataGaLocation":473},"continuous integration & delivery",{"text":544,"config":545},"Value stream management",{"href":173,"dataGaName":546,"dataGaLocation":473},"value stream management",{"text":548,"config":549},"GitOps",{"href":550,"dataGaName":551,"dataGaLocation":473},"/solutions/gitops/","gitops",{"text":183,"config":553},{"href":185,"dataGaName":186,"dataGaLocation":473},{"text":555,"config":556},"Small business",{"href":190,"dataGaName":191,"dataGaLocation":473},{"text":558,"config":559},"Public sector",{"href":195,"dataGaName":196,"dataGaLocation":473},{"text":561,"config":562},"Education",{"href":563,"dataGaName":564,"dataGaLocation":473},"/solutions/education/","education",{"text":566,"config":567},"Financial services",{"href":568,"dataGaName":569,"dataGaLocation":473},"/solutions/finance/","financial services",{"title":203,"links":571},[572,574,576,578,581,583,585,587,589,591,593,595,597],{"text":215,"config":573},{"href":217,"dataGaName":218,"dataGaLocation":473},{"text":220,"config":575},{"href":222,"dataGaName":223,"dataGaLocation":473},{"text":225,"config":577},{"href":227,"dataGaName":228,"dataGaLocation":473},{"text":230,"config":579},{"href":232,"dataGaName":580,"dataGaLocation":473},"docs",{"text":253,"config":582},{"href":255,"dataGaName":5,"dataGaLocation":473},{"text":248,"config":584},{"href":250,"dataGaName":251,"dataGaLocation":473},{"text":257,"config":586},{"href":259,"dataGaName":260,"dataGaLocation":473},{"text":270,"config":588},{"href":272,"dataGaName":273,"dataGaLocation":473},{"text":262,"config":590},{"href":264,"dataGaName":265,"dataGaLocation":473},{"text":275,"config":592},{"href":277,"dataGaName":278,"dataGaLocation":473},{"text":280,"config":594},{"href":282,"dataGaName":283,"dataGaLocation":473},{"text":285,"config":596},{"href":287,"dataGaName":288,"dataGaLocation":473},{"text":290,"config":598},{"href":292,"dataGaName":293,"dataGaLocation":473},{"title":308,"links":600},[601,603,605,607,609,611,613,617,622,624,626,628],{"text":315,"config":602},{"href":317,"dataGaName":310,"dataGaLocation":473},{"text":320,"config":604},{"href":322,"dataGaName":323,"dataGaLocation":473},{"text":328,"config":606},{"href":330,"dataGaName":331,"dataGaLocation":473},{"text":333,"config":608},{"href":335,"dataGaName":336,"dataGaLocation":473},{"text":338,"config":610},{"href":340,"dataGaName":341,"dataGaLocation":473},{"text":343,"config":612},{"href":345,"dataGaName":346,"dataGaLocation":473},{"text":614,"config":615},"Sustainability",{"href":616,"dataGaName":614,"dataGaLocation":473},"/sustainability/",{"text":618,"config":619},"Diversity, inclusion and belonging (DIB)",{"href":620,"dataGaName":621,"dataGaLocation":473},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":348,"config":623},{"href":350,"dataGaName":351,"dataGaLocation":473},{"text":358,"config":625},{"href":360,"dataGaName":361,"dataGaLocation":473},{"text":363,"config":627},{"href":365,"dataGaName":366,"dataGaLocation":473},{"text":629,"config":630},"Modern Slavery Transparency Statement",{"href":631,"dataGaName":632,"dataGaLocation":473},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":634,"links":635},"Contact Us",[636,639,641,643,648,653,658],{"text":637,"config":638},"Contact an expert",{"href":49,"dataGaName":50,"dataGaLocation":473},{"text":377,"config":640},{"href":379,"dataGaName":380,"dataGaLocation":473},{"text":382,"config":642},{"href":384,"dataGaName":385,"dataGaLocation":473},{"text":644,"config":645},"Status",{"href":646,"dataGaName":647,"dataGaLocation":473},"https://status.gitlab.com/","status",{"text":649,"config":650},"Terms of use",{"href":651,"dataGaName":652,"dataGaLocation":473},"/terms/","terms of use",{"text":654,"config":655},"Privacy statement",{"href":656,"dataGaName":657,"dataGaLocation":473},"/privacy/","privacy statement",{"text":659,"config":660},"Cookie preferences",{"dataGaName":661,"dataGaLocation":473,"id":662,"isOneTrustButton":103},"cookie preferences","ot-sdk-btn",{"items":664},[665,667,669],{"text":649,"config":666},{"href":651,"dataGaName":652,"dataGaLocation":473},{"text":654,"config":668},{"href":656,"dataGaName":657,"dataGaLocation":473},{"text":659,"config":670},{"dataGaName":661,"dataGaLocation":473,"id":662,"isOneTrustButton":103},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[676],{"_path":677,"_dir":678,"_draft":6,"_partial":6,"_locale":7,"content":679,"config":683,"_id":685,"_type":26,"title":18,"_source":28,"_file":686,"_stem":687,"_extension":31},"/en-us/blog/authors/jacob-vosmaer","authors",{"name":18,"config":680},{"headshot":681,"ctfId":682},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","Jacob-Vosmaer",{"template":684},"BlogAuthor","content:en-us:blog:authors:jacob-vosmaer.yml","en-us/blog/authors/jacob-vosmaer.yml","en-us/blog/authors/jacob-vosmaer",{"_path":689,"_dir":34,"_draft":6,"_partial":6,"_locale":7,"header":690,"eyebrow":691,"blurb":692,"button":693,"secondaryButton":697,"_id":699,"_type":26,"title":700,"_source":28,"_file":701,"_stem":702,"_extension":31},"/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":42,"config":694},{"href":695,"dataGaName":45,"dataGaLocation":696},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":47,"config":698},{"href":49,"dataGaName":50,"dataGaLocation":696},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1753981622781]