[{"data":1,"prerenderedAt":1309},["ShallowReactive",2],{"/blog/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x":3,"navigation-en-us":521,"banner-en-us":949,"footer-en-us":959,"blog-post-authors-en-us-Andrew Newdigate":1204,"blog-related-posts-en-us-how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x":1219,"blog-promotions-en-us":1245,"next-steps-en-us":1299},{"id":4,"title":5,"authors":6,"body":8,"category":488,"date":489,"description":490,"extension":491,"externalUrl":492,"faq":492,"featured":493,"heroImage":494,"meta":495,"navigation":508,"path":509,"seo":510,"slug":514,"stem":515,"tags":516,"template":519,"updatedDate":492,"__hash__":520},"blogPosts/en-us/blog/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x.md","How a fix in Go 1.9 sped up our Gitaly service by 30x",[7],"Andrew Newdigate",{"type":9,"value":10,"toc":474},"minimark",[11,21,30,35,38,41,49,52,60,69,72,79,82,85,92,107,111,120,128,131,134,146,149,156,159,167,170,177,181,193,196,203,206,209,213,234,245,251,258,262,294,313,324,337,357,375,387,392,395,402,405,412,416,422,461],[12,13,14,20],"p",{},[15,16,19],"a",{"href":17,"rel":18},"https://gitlab.com/gitlab-org/gitaly",[],"Gitaly"," is a Git RPC service that we are currently rolling out\nacross GitLab.com, to replace our legacy NFS-based file-sharing solution. We expect it to be faster, more stable\nand the basis for amazing new features in the future.",[12,22,23,24,29],{},"We're still in the process of porting Git operations to Gitaly, but the service has been\nrunning in production on GitLab.com for about nine months, and currently peaks at about 1,000\n",[15,25,28],{"href":26,"rel":27},"https://grpc.io/",[],"gRPC"," requests per second. We expect the migration effort to be completed\nby the beginning of April at which point all Git operations in the GitLab application will\nuse the service and we'll be able to decommission NFS infrastructure.",[31,32,34],"h2",{"id":33},"worrying-performance-improvements","Worrying performance improvements",[12,36,37],{},"The first time we realized that something might be wrong was shortly after we'd finished deploying a new release.",[12,39,40],{},"We were monitoring the performance of one of the gRPC endpoints for the Gitaly service and noticed that the\n99th percentile performance of the endpoint had dropped from 400ms down to 100ms.",[12,42,43,48],{},[44,45],"img",{"alt":46,"src":47},"400ms to 100ms latency drop","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782398484/blog/Content%20Images/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x/graph-01.png","\nLatencies drop from 400ms to 100ms after a deploy, for no good reason",[12,50,51],{},"This should have been fantastic news, but it wasn't. There were no changes that should have led to faster\nresponse times. We hadn't optimized anything in that release; we hadn't changed the runtime and the new\nrelease was using the same version of Git.",[12,53,54,55,59],{},"Everything ",[56,57,58],"em",{},"should have"," been exactly the same.",[12,61,62,63,68],{},"We started digging into the data a little more and quickly realised that 400ms is a very high latency for\nan operation that simply confirms the existence of a ",[15,64,67],{"href":65,"rel":66},"https://git-scm.com/book/en/v2/Git-Internals-Git-References",[],"Git reference",".",[12,70,71],{},"How long had it been this way? Well it started about 24 hours after the previous deployment.",[12,73,74,78],{},[44,75],{"alt":76,"src":77},"100ms to 400ms latency hike","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782398484/blog/Content%20Images/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x/graph-02.png","\nLatencies rising over a 24 hour period following a deployment, for no good reason",[12,80,81],{},"When browsing our Prometheus performance data, it quickly became apparent that this pattern was being repeated with each\ndeployment: things would start fast and gradually slow down. This was occurring across all endpoints. It had been this way for a while.",[12,83,84],{},"The first assumption was that there was some sort of resource leak in the application, causing the host to slow\ndown over time. Unfortunately the data didn't back this up. CPU usage of the Gitaly service did increase, but the\nhosts still had lots of capacity.",[12,86,87,91],{},[44,88],{"alt":89,"src":90},"Gitaly CPU charts","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782398484/blog/Content%20Images/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x/graph-03.png","\nGitaly CPU increasing with process age, but not enough to explain the problem",[12,93,94,95,100,101,106],{},"At this point, we still didn't have any good leads as to the cause of the problem, so we decided to further\nimprove the observability of the application by adding ",[15,96,99],{"href":97,"rel":98},"https://golang.org/pkg/net/http/pprof/",[],"pprof profiling support","\nand ",[15,102,105],{"href":103,"rel":104},"https://github.com/google/cadvisor",[],"cAdvisor"," metrics.",[31,108,110],{"id":109},"profiling","Profiling",[12,112,113,114,119],{},"Adding pprof support to a Go process is ",[15,115,118],{"href":116,"rel":117},"https://gitlab.com/gitlab-org/gitaly/merge_requests/442",[],"very easy",".\nThe process already has a Prometheus listener and we added a pprof handler on the same listener.",[12,121,122,123,68],{},"Since production teams would need to be able to perform the profiling without our assistance, we\nalso ",[15,124,127],{"href":125,"rel":126},"https://gitlab.com/gitlab-com/runbooks/blob/master/howto/gitaly-profiling.md",[],"added a runbook",[12,129,130],{},"Go's pprof support is easy to use and in our testing, we found that the overhead it\nadded to production workloads was negligible, meaning we could use it in production without concern\nabout the impact it would have on site performance.",[31,132,105],{"id":133},"cadvisor",[12,135,136,137,145],{},"The Gitaly service spawns Git child processes for many of its endpoints. Unfortunately these Git\nchild processes don't have the same instrumentation as the parent process so it was\ndifficult to tell if they were contributing to the problem. (Note: we record ",[15,138,141],{"href":139,"rel":140},"http://man7.org/linux/man-pages/man2/getrlimit.2.html",[],[142,143,144],"code",{},"getrlimit(2)"," metrics for Git processes but cannot observe grandchild processes spawned by Git, which often do much of the heavy lifting)",[12,147,148],{},"On GitLab.com, Gitaly is managed through systemd, which will automatically create a cgroup for\neach service it manages.",[12,150,151,152,155],{},"This means that Gitaly and its child processes are contained within a single cgroup, which we\ncould monitor with ",[15,153,105],{"href":103,"rel":154},[],", a Google monitoring tool\nwhich supports cgroups and is compatible with Prometheus.",[12,157,158],{},"Although we didn't have direct metrics to determine the behavior of the Git processes, we could\ninfer it using the cgroup metrics and the Gitaly process metrics: the difference between the\ntwo would tell us the resources (CPU, memory, etc) being consumed by the Git child processes.",[12,160,161,162,68],{},"At our request, the production team ",[15,163,166],{"href":164,"rel":165},"https://gitlab.com/gitlab-com/infrastructure/issues/3307",[],"added cAdvisor to the Gitaly servers",[12,168,169],{},"Having cAdvisor gives us the ability to know what the Gitaly service, including all its child\nprocesses, is doing.",[12,171,172,176],{},[44,173],{"alt":174,"src":175},"cAdvisor graphs for the Gitaly cgroup","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782398484/blog/Content%20Images/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x/graph-04.png","\ncAdvisor graphs of the Gitaly cgroup",[31,178,180],{"id":179},"from-bad-to-worse-much-much-worse","From bad to worse. Much, much worse...",[12,182,183,184,192],{},"In the meantime, ",[185,186,187],"strong",{},[15,188,191],{"href":189,"rel":190},"https://gitlab.com/gitlab-org/gitaly/issues/823",[],"the situation had got far worse",".\nInstead of only seeing gradual latency increases over time, we were now seeing far more serious lockups.",[12,194,195],{},"Individual Gitaly server instances would grind to a halt, to the point where all new incoming TCP connections\nwere not being accepted. This proved to be a problem to using pprof: during the lockup the connection\nwould time out when attempting to profile the process. Since the reason we added pprof was to observe the\nprocess under duress, that approach was a bust.",[12,197,198,199,202],{},"Interestingly, during a lock-up, CPU would actually decrease – the system was not overloaded, but actually\n",[56,200,201],{},"idled",". Iops, iowait and CPU would all drop way down.",[12,204,205],{},"Eventually, after a few minutes the service would recover and there would be a surge in backlogged\nrequests. Usually though, as soon as the state was detected, the production team would restart the\nservice manually.",[12,207,208],{},"The team spent a significant amount of time trying to recreate the problem locally, with little success.",[31,210,212],{"id":211},"forking-locks","Forking locks",[12,214,215,216,221,222,229,230,233],{},"Without pprof, we fell back to ",[15,217,220],{"href":218,"rel":219},"http://pro-tips-dot-com.tumblr.com/post/47677612115/kill-a-hung-go-process-and-print-stack-traces",[],"SIGABRT thread dumps","\nof hung processes. Using these, we determined that the process had a large amount of contention around ",[15,223,226],{"href":224,"rel":225},"https://gitlab.com/gitlab-org/gitaly/issues/823#note_50951140",[],[142,227,228],{},"syscall.ForkLock","\nduring the lockups. In one dump, 1,400 goroutines were blocked waiting on ",[142,231,232],{},"ForkLock"," – most for several minutes.",[12,235,236,238,239,244],{},[142,237,228],{}," has ",[15,240,243],{"href":241,"rel":242},"https://github.com/golang/go/blob/release-branch.go1.8/src/syscall/exec_unix.go#L17",[],"the following documentation",":",[246,247,248],"blockquote",{},[12,249,250],{},"Lock synchronizing creation of new file descriptors with fork.",[12,252,253,254,257],{},"Each Gitaly server instance was ",[142,255,256],{},"fork/exec","'ing Git processes about 20 times per second so we seemed to finally have a very promising lead.",[31,259,261],{"id":260},"serendipity","Serendipity",[12,263,264,269,270,272,273,280,281,284,285,288,289],{},[15,265,268],{"href":266,"rel":267},"https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/9365#note_54342481",[],"Researching ForkLock"," led us to an issue on the Go repository,\nopened in 2013, about switching from ",[142,271,256],{}," to ",[15,274,277],{"href":275,"rel":276},"https://man7.org/linux/man-pages/man2/clone.2.html",[],[142,278,279],{},"clone(2)"," with ",[142,282,283],{},"CLONE_VFORK"," and ",[142,286,287],{},"CLONE_VM","\non systems that support it: ",[15,290,293],{"href":291,"rel":292},"https://github.com/golang/go/issues/5838",[],"golang/go#5838",[12,295,296,297,299,300,284,302,304,305,312],{},"The ",[142,298,279],{}," syscall with ",[142,301,283],{},[142,303,287],{}," is the same as\nthe ",[15,306,309],{"href":307,"rel":308},"http://man7.org/linux/man-pages/man3/posix_spawn.3.html",[],[142,310,311],{},"posix_spawn(3)"," c function, but the latter is easier to\nrefer to, so let's use that.",[12,314,315,316,319,320,323],{},"When using ",[142,317,318],{},"fork",", the child process will start with a copy of the parent processes' memory.\nUnfortunately this process takes longer the larger the virtual memory footprint the process has.\nEven with copy-on-write, it can take several hundred milliseconds in a memory-intensive process.\n",[142,321,322],{},"posix_spawn"," doesn't copy the parent processes' memory space and has a roughly constant time.",[12,325,326,327,329,330,332,333],{},"Some good benchmarks of ",[142,328,256],{}," vs. ",[142,331,322],{}," can be found here: ",[15,334,335],{"href":335,"rel":336},"https://github.com/rtomayko/posix-spawn#benchmarks",[],[12,338,339,340,347,348,350,351,353,354,356],{},"This seemed like a possible explanation. Over time, the virtual memory size (VMM) of the Gitaly process would increase. As VMM\nincreased, each ",[15,341,344],{"href":342,"rel":343},"http://man7.org/linux/man-pages/man2/fork.2.html",[],[142,345,346],{},"fork(2)"," syscall would take longer. As fork latency increased, ",[142,349,228],{}," contention would increase.\nIf ",[142,352,318],{}," time exceeded the frequency of ",[142,355,318],{}," requests, the system could temporarily lock up entirely.",[12,358,359,360,367,280,372,374],{},"(Interestingly, ",[15,361,364],{"href":362,"rel":363},"https://golang.org/pkg/net/#TCPListener.Accept",[],[142,365,366],{},"TCPListener.Accept",[15,368,371],{"href":369,"rel":370},"https://github.com/golang/go/blob/2ea7d3461bb41d0ae12b56ee52d43314bcdb97f9/src/net/sock_cloexec.go#L20",[],"also interacts",[142,373,228],{},",\nalthough only on older versions of Linux. Could this be the cause of our failure to connect to the pprof listener during a lockup?)",[12,376,377,378,381,382,272,384,386],{},"By some incredibly good luck, ",[15,379,293],{"href":291,"rel":380},[],", the switch from ",[142,383,318],{},[142,385,322],{},", had,\nafter several years' delay, recently landed in Go 1.9, just in time for us. Gitaly had been compiled with Go 1.8.\nWe quickly built and tested a new binary with Go 1.9 and manually deployed this\non one of our production servers.",[388,389,391],"h3",{"id":390},"spectacular-results","Spectacular results",[12,393,394],{},"Here's the CPU usage of Gitaly processes across the fleet:",[12,396,397,401],{},[44,398],{"alt":399,"src":400},"CPU after Go 1.9","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782398484/blog/Content%20Images/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x/graph-05.png","\nCPU after recompiling with Go 1.9",[12,403,404],{},"Here's the 99th percentile latency figures. This chart is using a logarithmic scale, so we're talking about two orders of\nmagnitude faster!",[12,406,407,411],{},[44,408],{"alt":409,"src":410},"30x latency drops with Go 1.9","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782398484/blog/Content%20Images/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x/graph-06.png","\nEndpoint latency after recompiling with Go 1.9 (log scale)",[31,413,415],{"id":414},"conclusion","Conclusion",[12,417,418,419,421],{},"Recompiling with Go 1.9 solved the problem, thanks to the switch to ",[142,420,322],{},". We learned several other lessons\nin the process too:",[423,424,425,429,441,450],"ol",{},[426,427,428],"li",{},"Having solid application monitoring in place allowed us to detect this issue, and start investigating it, far\nearlier than we otherwise would have been able to.",[426,430,431,436,437,440],{},[15,432,435],{"href":433,"rel":434},"https://blog.golang.org/profiling-go-programs",[],"pprof"," can be really helpful, but may not help when a process\nhas locked up and won't accept new connections. pprof is lightweight enough that you should consider adding it to your application ",[56,438,439],{},"before"," you need it.",[426,442,443,444,449],{},"When all else fails, ",[15,445,447],{"href":218,"rel":446},[],[142,448,220],{}," might help.",[426,451,452,457,458,460],{},[15,453,455],{"href":103,"rel":454},[],[142,456,105],{}," is great for monitoring cgroups. Systemd services each run in\ntheir own cgroup, so ",[142,459,105],{}," is an easy way of monitoring a service and all its child processes, together.",[12,462,463,468,469],{},[15,464,467],{"href":465,"rel":466},"https://unsplash.com/photos/jJbQBP_yh68?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText",[],"Photo"," by Javier García on ",[15,470,473],{"href":471,"rel":472},"https://unsplash.com/search/photos/slow?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText",[],"Unsplash",{"title":475,"searchDepth":476,"depth":476,"links":477},"",2,[478,479,480,481,482,483,487],{"id":33,"depth":476,"text":34},{"id":109,"depth":476,"text":110},{"id":133,"depth":476,"text":105},{"id":179,"depth":476,"text":180},{"id":211,"depth":476,"text":212},{"id":260,"depth":476,"text":261,"children":484},[485],{"id":390,"depth":486,"text":391},3,{"id":414,"depth":476,"text":415},"engineering","2018-01-23","After noticing a worrying pattern in Gitaly's performance, we uncovered an issue with fork locking affecting virtual memory size. Here's how we figured out the problem and how to fix it.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749666775/Blog/Hero%20Images/cover.jpg",{"excerpt":496},{"type":9,"value":497},[498,503],[12,499,500,20],{},[15,501,19],{"href":17,"rel":502},[],[12,504,23,505,29],{},[15,506,28],{"href":26,"rel":507},[],true,"/en-us/blog/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x",{"title":5,"description":490,"ogTitle":5,"ogDescription":490,"noIndex":493,"ogImage":494,"ogUrl":511,"ogSiteName":512,"ogType":513,"canonicalUrls":511},"https://about.gitlab.com/blog/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x","https://about.gitlab.com","article","how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x","en-us/blog/how-a-fix-in-go-19-sped-up-our-gitaly-service-by-30x",[517,518],"inside GitLab","performance","BlogPost","aPHa3udFzf1wcCRDIIvy7dDejFGLgVMltBaRsNoyKUI",{"logo":522,"freeTrial":527,"sales":532,"login":537,"items":542,"search":869,"minimal":900,"duo":919,"switchNav":928,"pricingDeployment":939},{"config":523},{"href":524,"dataGaName":525,"dataGaLocation":526},"/","gitlab logo","header",{"text":528,"config":529},"Get free trial",{"href":530,"dataGaName":531,"dataGaLocation":526},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":533,"config":534},"Request a demo",{"href":535,"dataGaName":536,"dataGaLocation":526},"/sales/?contact-topic=request-demo","sales",{"text":538,"config":539},"Sign in",{"href":540,"dataGaName":541,"dataGaLocation":526},"https://gitlab.com/users/sign_in/","sign in",[543,572,672,677,791,847],{"text":544,"config":545,"menu":547},"Platform",{"dataNavLevelOne":546},"platform",{"type":548,"columns":549},"cards",[550,556,564],{"title":544,"description":551,"link":552},"The intelligent orchestration platform for DevSecOps",{"text":553,"config":554},"Explore our Platform",{"href":555,"dataGaName":546,"dataGaLocation":526},"/platform/",{"title":557,"description":558,"link":559},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":560,"config":561},"Meet GitLab Duo",{"href":562,"dataGaName":563,"dataGaLocation":526},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":565,"description":566,"link":567},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":568,"config":569},"Learn more",{"href":570,"dataGaName":571,"dataGaLocation":526},"/why-gitlab/","why gitlab",{"text":573,"left":508,"config":574,"menu":576},"Product",{"dataNavLevelOne":575},"solutions",{"type":577,"link":578,"columns":582,"feature":651},"lists",{"text":579,"config":580},"View all Solutions",{"href":581,"dataGaName":575,"dataGaLocation":526},"/solutions/",[583,607,630],{"title":584,"description":585,"link":586,"items":591},"Automation","CI/CD and automation to accelerate deployment",{"config":587},{"icon":588,"href":589,"dataGaName":590,"dataGaLocation":526},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[592,596,599,603],{"text":593,"config":594},"CI/CD",{"href":595,"dataGaLocation":526,"dataGaName":593},"/solutions/continuous-integration/",{"text":557,"config":597},{"href":562,"dataGaLocation":526,"dataGaName":598},"gitlab duo agent platform - product menu",{"text":600,"config":601},"Source Code Management",{"href":602,"dataGaLocation":526,"dataGaName":600},"/solutions/source-code-management/",{"text":604,"config":605},"Automated Software Delivery",{"href":589,"dataGaLocation":526,"dataGaName":606},"Automated software delivery",{"title":608,"description":609,"link":610,"items":615},"Security","Deliver code faster without compromising security",{"config":611},{"href":612,"dataGaName":613,"dataGaLocation":526,"icon":614},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[616,620,625],{"text":617,"config":618},"Application Security Testing",{"href":612,"dataGaName":619,"dataGaLocation":526},"Application security testing",{"text":621,"config":622},"Software Supply Chain Security",{"href":623,"dataGaLocation":526,"dataGaName":624},"/solutions/supply-chain/","Software supply chain security",{"text":626,"config":627},"Software Compliance",{"href":628,"dataGaName":629,"dataGaLocation":526},"/solutions/software-compliance/","software compliance",{"title":631,"link":632,"items":637},"Measurement",{"config":633},{"icon":634,"href":635,"dataGaName":636,"dataGaLocation":526},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[638,642,646],{"text":639,"config":640},"Visibility & Measurement",{"href":635,"dataGaLocation":526,"dataGaName":641},"Visibility and Measurement",{"text":643,"config":644},"Value Stream Management",{"href":645,"dataGaLocation":526,"dataGaName":643},"/solutions/value-stream-management/",{"text":647,"config":648},"Analytics & Insights",{"href":649,"dataGaLocation":526,"dataGaName":650},"/solutions/analytics-and-insights/","Analytics and insights",{"title":652,"type":577,"items":653},"GitLab for",[654,660,666],{"text":655,"config":656},"Enterprise",{"icon":657,"href":658,"dataGaLocation":526,"dataGaName":659},"Building","/enterprise/","enterprise",{"text":661,"config":662},"Small Business",{"icon":663,"href":664,"dataGaLocation":526,"dataGaName":665},"Work","/small-business/","small business",{"text":667,"config":668},"Public Sector",{"icon":669,"href":670,"dataGaLocation":526,"dataGaName":671},"Organization","/solutions/public-sector/","public sector",{"text":673,"config":674},"Pricing",{"href":675,"dataGaName":676,"dataGaLocation":526,"dataNavLevelOne":676},"/pricing/","pricing",{"text":678,"config":679,"menu":681},"Resources",{"dataNavLevelOne":680},"resources",{"type":577,"link":682,"columns":686,"feature":780},{"text":683,"config":684},"View all resources",{"href":685,"dataGaName":680,"dataGaLocation":526},"/resources/",[687,720,747],{"title":688,"items":689},"Getting started",[690,695,700,705,710,715],{"text":691,"config":692},"Install",{"href":693,"dataGaName":694,"dataGaLocation":526},"/install/","install",{"text":696,"config":697},"Quick start guides",{"href":698,"dataGaName":699,"dataGaLocation":526},"/get-started/","quick setup checklists",{"text":701,"config":702},"Learn",{"href":703,"dataGaLocation":526,"dataGaName":704},"https://university.gitlab.com/","learn",{"text":706,"config":707},"Product documentation",{"href":708,"dataGaName":709,"dataGaLocation":526},"https://docs.gitlab.com/","product documentation",{"text":711,"config":712},"Best practice videos",{"href":713,"dataGaName":714,"dataGaLocation":526},"/getting-started-videos/","best practice videos",{"text":716,"config":717},"Integrations",{"href":718,"dataGaName":719,"dataGaLocation":526},"/integrations/","integrations",{"title":721,"items":722},"Discover",[723,728,733,738,742],{"text":724,"config":725},"Customer success stories",{"href":726,"dataGaName":727,"dataGaLocation":526},"/customers/","customer success stories",{"text":729,"config":730},"Blog",{"href":731,"dataGaName":732,"dataGaLocation":526},"/blog/","blog",{"text":734,"config":735},"Demo Hub",{"href":736,"dataGaName":737,"dataGaLocation":526},"/demo-hub/","demo hub",{"text":739,"config":740},"The Source",{"href":741,"dataGaName":732,"dataGaLocation":526},"/the-source/",{"text":743,"config":744},"Remote",{"href":745,"dataGaName":746,"dataGaLocation":526},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":748,"items":749},"Connect",[750,755,760,765,770,775],{"text":751,"config":752},"GitLab Services",{"href":753,"dataGaName":754,"dataGaLocation":526},"/services/","services",{"text":756,"config":757},"Contribute",{"href":758,"dataGaName":759,"dataGaLocation":526},"https://contributors.gitlab.com","contribute",{"text":761,"config":762},"Community",{"href":763,"dataGaName":764,"dataGaLocation":526},"/community/","community",{"text":766,"config":767},"Forum",{"href":768,"dataGaName":769,"dataGaLocation":526},"https://forum.gitlab.com/","forum",{"text":771,"config":772},"Events",{"href":773,"dataGaName":774,"dataGaLocation":526},"/events/","events",{"text":776,"config":777},"Partners",{"href":778,"dataGaName":779,"dataGaLocation":526},"/partners/","partners",{"config":781,"title":784,"text":785,"link":786},{"background":782,"textColor":783},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":787,"config":788},"Read the latest",{"href":789,"dataGaName":790,"dataGaLocation":526},"/whats-new/","whats new",{"text":792,"config":793,"menu":795},"Company",{"dataNavLevelOne":794},"company",{"type":577,"columns":796},[797],{"items":798},[799,804,810,812,817,822,827,832,837,842],{"text":800,"config":801},"About",{"href":802,"dataGaName":803,"dataGaLocation":526},"/company/","about",{"text":805,"config":806,"footerGa":809},"Jobs",{"href":807,"dataGaName":808,"dataGaLocation":526},"/jobs/","jobs",{"dataGaName":808},{"text":771,"config":811},{"href":773,"dataGaName":774,"dataGaLocation":526},{"text":813,"config":814},"Leadership",{"href":815,"dataGaName":816,"dataGaLocation":526},"/company/team/e-group/","leadership",{"text":818,"config":819},"Handbook",{"href":820,"dataGaName":821,"dataGaLocation":526},"https://handbook.gitlab.com/","handbook",{"text":823,"config":824},"Investor relations",{"href":825,"dataGaName":826,"dataGaLocation":526},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":828,"config":829},"Trust Center",{"href":830,"dataGaName":831,"dataGaLocation":526},"/security/","trust center",{"text":833,"config":834},"AI Transparency Center",{"href":835,"dataGaName":836,"dataGaLocation":526},"/ai-transparency-center/","ai transparency center",{"text":838,"config":839},"Newsletter",{"href":840,"dataGaName":841,"dataGaLocation":526},"/company/contact/#contact-forms","newsletter",{"text":843,"config":844},"Press",{"href":845,"dataGaName":846,"dataGaLocation":526},"/press/","press",{"text":848,"config":849,"menu":850},"Contact us",{"dataNavLevelOne":794},{"type":577,"columns":851},[852],{"items":853},[854,859,864],{"text":855,"config":856},"Talk to sales",{"href":857,"dataGaName":858,"dataGaLocation":526},"/sales/","talk to sales",{"text":860,"config":861},"Support portal",{"href":862,"dataGaName":863,"dataGaLocation":526},"https://support.gitlab.com/hc/en-us","support portal",{"text":865,"config":866},"Customer portal",{"href":867,"dataGaName":868,"dataGaLocation":526},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":870,"login":871,"suggestions":878},"Close",{"text":872,"link":873},"To search repositories and projects, login to",{"text":874,"config":875},"gitlab.com",{"href":540,"dataGaName":876,"dataGaLocation":877},"search login","search",{"text":879,"default":880},"Suggestions",[881,883,887,889,893,897],{"text":557,"config":882},{"href":562,"dataGaName":557,"dataGaLocation":877},{"text":884,"config":885},"Code Suggestions (AI)",{"href":886,"dataGaName":884,"dataGaLocation":877},"/solutions/code-suggestions/",{"text":593,"config":888},{"href":595,"dataGaName":593,"dataGaLocation":877},{"text":890,"config":891},"GitLab on AWS",{"href":892,"dataGaName":890,"dataGaLocation":877},"/partners/technology-partners/aws/",{"text":894,"config":895},"GitLab on Google Cloud",{"href":896,"dataGaName":894,"dataGaLocation":877},"/partners/technology-partners/google-cloud-platform/",{"text":898,"config":899},"Why GitLab?",{"href":570,"dataGaName":898,"dataGaLocation":877},{"freeTrial":901,"mobileIcon":906,"desktopIcon":911,"secondaryButton":914},{"text":902,"config":903},"Start free trial",{"href":904,"dataGaName":531,"dataGaLocation":905},"https://gitlab.com/-/trials/new/","nav",{"altText":907,"config":908},"Gitlab Icon",{"src":909,"dataGaName":910,"dataGaLocation":905},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":907,"config":912},{"src":913,"dataGaName":910,"dataGaLocation":905},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":915,"config":916},"Get Started",{"href":917,"dataGaName":918,"dataGaLocation":905},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":920,"mobileIcon":924,"desktopIcon":926},{"text":921,"config":922},"Learn more about GitLab Duo",{"href":562,"dataGaName":923,"dataGaLocation":905},"gitlab duo",{"altText":907,"config":925},{"src":909,"dataGaName":910,"dataGaLocation":905},{"altText":907,"config":927},{"src":913,"dataGaName":910,"dataGaLocation":905},{"button":929,"mobileIcon":934,"desktopIcon":936},{"text":930,"config":931},"/switch",{"href":932,"dataGaName":933,"dataGaLocation":905},"#contact","switch",{"altText":907,"config":935},{"src":909,"dataGaName":910,"dataGaLocation":905},{"altText":907,"config":937},{"src":938,"dataGaName":910,"dataGaLocation":905},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":940,"mobileIcon":945,"desktopIcon":947},{"text":941,"config":942},"Back to pricing",{"href":675,"dataGaName":943,"dataGaLocation":905,"icon":944},"back to pricing","GoBack",{"altText":907,"config":946},{"src":909,"dataGaName":910,"dataGaLocation":905},{"altText":907,"config":948},{"src":913,"dataGaName":910,"dataGaLocation":905},{"title":950,"titleMobile":951,"button":952,"config":957},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":568,"config":953},{"href":954,"dataGaName":955,"dataGaLocation":956},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":958,"disabled":493},"release",{"data":960},{"text":961,"source":962,"edit":968,"contribute":973,"config":978,"items":983,"minimal":1193},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":963,"config":964},"View page source",{"href":965,"dataGaName":966,"dataGaLocation":967},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":969,"config":970},"Edit this page",{"href":971,"dataGaName":972,"dataGaLocation":967},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":974,"config":975},"Please contribute",{"href":976,"dataGaName":977,"dataGaLocation":967},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":979,"facebook":980,"youtube":981,"linkedin":982},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[984,1031,1085,1129,1161],{"title":673,"links":985,"subMenu":1000},[986,990,995],{"text":987,"config":988},"View plans",{"href":675,"dataGaName":989,"dataGaLocation":967},"view plans",{"text":991,"config":992},"Why Premium?",{"href":993,"dataGaName":994,"dataGaLocation":967},"/pricing/premium/","why premium",{"text":996,"config":997},"Why Ultimate?",{"href":998,"dataGaName":999,"dataGaLocation":967},"/pricing/ultimate/","why ultimate",[1001],{"title":1002,"links":1003},"Contact Us",[1004,1007,1009,1011,1016,1021,1026],{"text":1005,"config":1006},"Contact sales",{"href":857,"dataGaName":536,"dataGaLocation":967},{"text":860,"config":1008},{"href":862,"dataGaName":863,"dataGaLocation":967},{"text":865,"config":1010},{"href":867,"dataGaName":868,"dataGaLocation":967},{"text":1012,"config":1013},"Status",{"href":1014,"dataGaName":1015,"dataGaLocation":967},"https://status.gitlab.com/","status",{"text":1017,"config":1018},"Terms of use",{"href":1019,"dataGaName":1020,"dataGaLocation":967},"/terms/","terms of use",{"text":1022,"config":1023},"Privacy statement",{"href":1024,"dataGaName":1025,"dataGaLocation":967},"/privacy/","privacy statement",{"text":1027,"config":1028},"Cookie preferences",{"dataGaName":1029,"dataGaLocation":967,"id":1030,"isOneTrustButton":508},"cookie preferences","ot-sdk-btn",{"title":573,"links":1032,"subMenu":1041},[1033,1037],{"text":1034,"config":1035},"DevSecOps platform",{"href":555,"dataGaName":1036,"dataGaLocation":967},"devsecops platform",{"text":1038,"config":1039},"AI-Assisted Development",{"href":562,"dataGaName":1040,"dataGaLocation":967},"ai-assisted development",[1042],{"title":1043,"links":1044},"Topics",[1045,1050,1055,1060,1065,1070,1075,1080],{"text":1046,"config":1047},"CICD",{"href":1048,"dataGaName":1049,"dataGaLocation":967},"/topics/ci-cd/","cicd",{"text":1051,"config":1052},"GitOps",{"href":1053,"dataGaName":1054,"dataGaLocation":967},"/topics/gitops/","gitops",{"text":1056,"config":1057},"DevOps",{"href":1058,"dataGaName":1059,"dataGaLocation":967},"/topics/devops/","devops",{"text":1061,"config":1062},"Version Control",{"href":1063,"dataGaName":1064,"dataGaLocation":967},"/topics/version-control/","version control",{"text":1066,"config":1067},"DevSecOps",{"href":1068,"dataGaName":1069,"dataGaLocation":967},"/topics/devsecops/","devsecops",{"text":1071,"config":1072},"Cloud Native",{"href":1073,"dataGaName":1074,"dataGaLocation":967},"/topics/cloud-native/","cloud native",{"text":1076,"config":1077},"AI for Coding",{"href":1078,"dataGaName":1079,"dataGaLocation":967},"/topics/devops/ai-for-coding/","ai for coding",{"text":1081,"config":1082},"Agentic AI",{"href":1083,"dataGaName":1084,"dataGaLocation":967},"/topics/agentic-ai/","agentic ai",{"title":1086,"links":1087},"Solutions",[1088,1090,1092,1097,1101,1104,1108,1111,1113,1116,1119,1124],{"text":617,"config":1089},{"href":612,"dataGaName":617,"dataGaLocation":967},{"text":606,"config":1091},{"href":589,"dataGaName":590,"dataGaLocation":967},{"text":1093,"config":1094},"Agile development",{"href":1095,"dataGaName":1096,"dataGaLocation":967},"/solutions/agile-delivery/","agile delivery",{"text":1098,"config":1099},"SCM",{"href":602,"dataGaName":1100,"dataGaLocation":967},"source code management",{"text":1046,"config":1102},{"href":595,"dataGaName":1103,"dataGaLocation":967},"continuous integration & delivery",{"text":1105,"config":1106},"Value stream management",{"href":645,"dataGaName":1107,"dataGaLocation":967},"value stream management",{"text":1051,"config":1109},{"href":1110,"dataGaName":1054,"dataGaLocation":967},"/solutions/gitops/",{"text":655,"config":1112},{"href":658,"dataGaName":659,"dataGaLocation":967},{"text":1114,"config":1115},"Small business",{"href":664,"dataGaName":665,"dataGaLocation":967},{"text":1117,"config":1118},"Public sector",{"href":670,"dataGaName":671,"dataGaLocation":967},{"text":1120,"config":1121},"Education",{"href":1122,"dataGaName":1123,"dataGaLocation":967},"/solutions/education/","education",{"text":1125,"config":1126},"Financial services",{"href":1127,"dataGaName":1128,"dataGaLocation":967},"/solutions/finance/","financial services",{"title":678,"links":1130},[1131,1133,1135,1137,1140,1142,1145,1147,1149,1151,1153,1155,1157,1159],{"text":691,"config":1132},{"href":693,"dataGaName":694,"dataGaLocation":967},{"text":696,"config":1134},{"href":698,"dataGaName":699,"dataGaLocation":967},{"text":701,"config":1136},{"href":703,"dataGaName":704,"dataGaLocation":967},{"text":706,"config":1138},{"href":708,"dataGaName":1139,"dataGaLocation":967},"docs",{"text":729,"config":1141},{"href":731,"dataGaName":732,"dataGaLocation":967},{"text":1143,"config":1144},"What's new",{"href":789,"dataGaName":790,"dataGaLocation":967},{"text":724,"config":1146},{"href":726,"dataGaName":727,"dataGaLocation":967},{"text":743,"config":1148},{"href":745,"dataGaName":746,"dataGaLocation":967},{"text":751,"config":1150},{"href":753,"dataGaName":754,"dataGaLocation":967},{"text":756,"config":1152},{"href":758,"dataGaName":759,"dataGaLocation":967},{"text":761,"config":1154},{"href":763,"dataGaName":764,"dataGaLocation":967},{"text":766,"config":1156},{"href":768,"dataGaName":769,"dataGaLocation":967},{"text":771,"config":1158},{"href":773,"dataGaName":774,"dataGaLocation":967},{"text":776,"config":1160},{"href":778,"dataGaName":779,"dataGaLocation":967},{"title":792,"links":1162},[1163,1165,1167,1169,1171,1173,1177,1182,1184,1186,1188],{"text":800,"config":1164},{"href":802,"dataGaName":794,"dataGaLocation":967},{"text":805,"config":1166},{"href":807,"dataGaName":808,"dataGaLocation":967},{"text":813,"config":1168},{"href":815,"dataGaName":816,"dataGaLocation":967},{"text":818,"config":1170},{"href":820,"dataGaName":821,"dataGaLocation":967},{"text":823,"config":1172},{"href":825,"dataGaName":826,"dataGaLocation":967},{"text":1174,"config":1175},"Sustainability",{"href":1176,"dataGaName":1174,"dataGaLocation":967},"/sustainability/",{"text":1178,"config":1179},"Diversity, inclusion and belonging (DIB)",{"href":1180,"dataGaName":1181,"dataGaLocation":967},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":828,"config":1183},{"href":830,"dataGaName":831,"dataGaLocation":967},{"text":838,"config":1185},{"href":840,"dataGaName":841,"dataGaLocation":967},{"text":843,"config":1187},{"href":845,"dataGaName":846,"dataGaLocation":967},{"text":1189,"config":1190},"Modern Slavery Transparency Statement",{"href":1191,"dataGaName":1192,"dataGaLocation":967},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1194},[1195,1198,1201],{"text":1196,"config":1197},"Terms",{"href":1019,"dataGaName":1020,"dataGaLocation":967},{"text":1199,"config":1200},"Cookies",{"dataGaName":1029,"dataGaLocation":967,"id":1030,"isOneTrustButton":508},{"text":1202,"config":1203},"Privacy",{"href":1024,"dataGaName":1025,"dataGaLocation":967},[1205],{"id":1206,"title":7,"body":492,"config":1207,"content":1209,"description":492,"extension":1213,"meta":1214,"navigation":508,"path":1215,"seo":1216,"stem":1217,"__hash__":1218},"blogAuthors/en-us/blog/authors/andrew-newdigate.yml",{"template":1208},"BlogAuthor",{"name":7,"config":1210},{"headshot":1211,"ctfId":1212},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749670199/Blog/Author%20Headshots/andrewn-headshot.jpg","andrewn","yml",{},"/en-us/blog/authors/andrew-newdigate",{},"en-us/blog/authors/andrew-newdigate","T_TbXElfi2h-0oHIT5B0iOQQVcjp2x-eIuiKLfhbDBQ",[1220,1229,1237],{"title":1221,"description":1222,"heroImage":1223,"category":488,"date":1224,"authors":1225,"slug":1228,"externalUrl":492},"Confidential AI for GitLab Self-Hosted","Give developers AI coding agents in GitLab Duo without source code leaving a hardware-encrypted boundary — no GPUs needed.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773866173/vte9qh8rriznvyclhkes.png","2026-08-06",[1226,1227],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1230,"description":1231,"heroImage":1232,"category":488,"date":1233,"authors":1234,"slug":1236,"externalUrl":492},"Green DevOps: Why carbon measurement belongs in your CI/CD pipeline","CI/CD pipelines have a hidden carbon cost. Here's why measuring it matters, and how you can get started with Eco CI and Carmen in GitLab.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1765809212/noh0mdfn9o94ry9ykura.png","2026-07-09",[1235],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1238,"description":1239,"heroImage":1240,"category":488,"date":1241,"authors":1242,"slug":1244,"externalUrl":492},"How to build CI/CD observability at scale","This practical guide to GitLab pipeline analytics helps self-managed users gain operational insights using Prometheus and Grafana.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-28",[1243],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1246},[1247,1261,1273,1285],{"id":1248,"categories":1249,"header":1251,"text":1252,"button":1253,"image":1258},"ai-modernization",[1250],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1254,"config":1255},"Get your AI maturity score",{"href":1256,"dataGaName":1257,"dataGaLocation":732},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1259},{"src":1260},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1262,"categories":1263,"header":1265,"text":1252,"button":1266,"image":1270},"devops-modernization",[1264,1069],"product","Are you just managing tools or shipping innovation?",{"text":1267,"config":1268},"Get your DevOps maturity score",{"href":1269,"dataGaName":1257,"dataGaLocation":732},"/assessments/devops-modernization-assessment/",{"config":1271},{"src":1272},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1274,"categories":1275,"header":1277,"text":1252,"button":1278,"image":1282},"security-modernization",[1276],"security","Are you trading speed for security?",{"text":1279,"config":1280},"Get your security maturity score",{"href":1281,"dataGaName":1257,"dataGaLocation":732},"/assessments/security-modernization-assessment/",{"config":1283},{"src":1284},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1286,"paths":1287,"header":1290,"text":1291,"button":1292,"image":1297},"github-azure-migration",[1288,1289],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":1293,"config":1294},"See how GitLab compares to GitHub",{"href":1295,"dataGaName":1296,"dataGaLocation":732},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1298},{"src":1272},{"header":1300,"blurb":1301,"button":1302,"secondaryButton":1307},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1303,"config":1304},"Get your free trial",{"href":1305,"dataGaName":531,"dataGaLocation":1306},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1005,"config":1308},{"href":857,"dataGaName":536,"dataGaLocation":1306},1786803734477]