[{"data":1,"prerenderedAt":1305},["ShallowReactive",2],{"/blog/puma-nakayoshi-fork-and-compaction":3,"navigation-en-us":516,"banner-en-us":944,"footer-en-us":954,"blog-post-authors-en-us-Matthias Käppler":1199,"blog-related-posts-en-us-puma-nakayoshi-fork-and-compaction":1215,"blog-promotions-en-us":1241,"next-steps-en-us":1295},{"id":4,"title":5,"authors":6,"body":8,"category":494,"date":495,"description":496,"extension":497,"externalUrl":498,"faq":498,"featured":499,"heroImage":500,"meta":501,"navigation":502,"path":503,"seo":504,"slug":508,"stem":509,"tags":510,"template":514,"updatedDate":498,"__hash__":515},"blogPosts/en-us/blog/puma-nakayoshi-fork-and-compaction.md","Ruby 2.7: Understand and debug problems with heap compaction",[7],"Matthias Käppler",{"type":9,"value":10,"toc":485},"minimark",[11,33,36,45,61,97,100,121,125,128,131,143,149,152,156,165,194,198,201,302,333,351,357,365,387,393,397,404,411,414,439,443,446,449,478,481],[12,13,14,15,21,22,27,28,32],"p",{},"The GitLab Rails application runs on ",[16,17,20],"a",{"href":18,"rel":19},"https://puma.io/",[],"Puma",", a multi-threaded Rack application server written in the new Ruby.\nWe recently updated Puma to major version 5, which introduced ",[16,23,26],{"href":24,"rel":25},"https://github.com/puma/puma/blob/master/History.md#500--2020-09-17",[],"a number of important\nchanges",",\nincluding support for ",[29,30,31],"em",{},"compaction",", a technique to reduce memory fragmentation in the\nRuby heap.",[12,34,35],{},"In this post we will describe what Puma's \"nakayoshi fork\" does, what compaction is,\nand some of the challenges we faced when first deploying it.",[37,38,40,41],"h2",{"id":39},"nakayoshi-a-friendlier-fork","Nakayoshi: A friendlier ",[42,43,44],"code",{},"fork",[12,46,47,48,51,52,57,58,60],{},"Puma 5 added a new configuration switch: ",[42,49,50],{},"nakayoshi_fork",". This switch affects Puma's behavior when\nforking new workers from the primary process. It is largely based on a ",[16,53,56],{"href":54,"rel":55},"https://github.com/ko1/nakayoshi_fork",[],"Ruby gem of the same name","\nbut adds new functionality. More specifically, enabling ",[42,59,50],{}," in Puma will result in two additional\nsteps prior to forking into new workers:",[62,63,64,81],"ol",{},[65,66,67,71,72,74,75,80],"li",{},[68,69,70],"strong",{},"Tenuring objects."," By running several minor garbage collection cycles ahead of a ",[42,73,44],{},", Ruby can promote survivors\nfrom the young to the old generation (referred to as \"tenuring\"). These objects are often classes, modules, or long-lived\nconstants that are unlikely to change.\nThis process makes forking copy-on-write friendly because tagging an object as \"old\" implies a write\nto the underlying heap page. Doing this prior to forking means the OS won't have\nto copy this page from the parent to the worker process later. We won't be discussing copy-on-write in detail but\n",[16,76,79],{"href":77,"rel":78},"https://brandur.org/ruby-memory",[],"this blog post offers a good introduction to the topic and how it relates to Ruby and pre-fork servers",".",[65,82,83,86,87,90,91,93,94,96],{},[68,84,85],{},"Heap compaction."," Ruby 2.7 added a new method ",[42,88,89],{},"GC.compact",", which\nwill reorganize the Ruby heap to pack objects closer together when invoked. ",[42,92,89],{}," reduces Ruby heap fragmentation and\npotentially frees up Ruby heap pages so that the physical memory consumed can be reclaimed by the OS.\nThis step only happens when ",[42,95,89],{}," is available in the version of Ruby that is in use (for MRI, 2.7 or newer).",[12,98,99],{},"In the remainder of this post, we will look at:",[101,102,103,109,112,115],"ul",{},[65,104,105,106,108],{},"How ",[42,107,89],{}," works and its potential benefits.",[65,110,111],{},"Why using C-extensions can be problematic when using compaction.",[65,113,114],{},"How we resolved a production incident that crashed GitLab.",[65,116,117,118,120],{},"What to look out for before enabling compaction in your app, via ",[42,119,50],{}," or otherwise.",[37,122,124],{"id":123},"how-compacting-garbage-collection-works","How compacting garbage collection works",[12,126,127],{},"The primary goal of a compacting garbage collector (GC) is to use allocated memory more\neffectively, which increases the likelihood of the application using less memory over time.\nCompaction is especially important when processes can share memory, as is the case with Ruby pre-fork\nservers such as Puma or Unicorn. But how does Ruby accomplish this?",[12,129,130],{},"Ruby manages its own object heap by allocating chunks of memory from the operating system called pages\n(a confusing term since Ruby heap pages are distinct from the smaller memory pages managed by the OS itself).\nWhen an application asks to create a new object, Ruby will try to find a free object slot in one of these\npages and fill it. As objects are allocated and deallocated over the lifetime of the application,\nthis can lead to fragmentation, with pages being neither entirely full nor entirely empty. This is the\nprimary cause for Ruby's infamous runaway memory problem: Since the available space isn't optimally used,\npages will rarely be entirely empty and become \"tomb pages\" which means it is necessary for the pages to be empty for them to be deallocated.",[12,132,133,134,136,137,142],{},"Ruby 2.7 added a new method, ",[42,135,89],{},", which aims to address this problem by walking the entire\nRuby heap space and moving objects around to obtain tightly packed pages. This process will ideally make\nsome pages unused, and unused memory can be reclaimed by the OS. ",[16,138,141],{"href":139,"rel":140},"https://www.youtube.com/watch?v=H8iWLoarTZc",[],"Watch this video from RubyConf 2019"," where Aaron Patterson, the author of this feature, gave a good introduction to compacting GC.",[12,144,145,146,148],{},"Compaction is a fairly expensive task since Ruby needs to stop-the-world for a complete heap reorganization so\nits best to perform this task before forking a new worker process, which is why Puma 5 included this step when performing ",[42,147,50],{},". Moreover, running compaction before forking\ninto worker processes increases the chance of workers being able to share memory.",[12,150,151],{},"We were eager to enable this feature on GitLab to see if it would reduce memory consumption, but things didn't entirely go as planned.",[37,153,155],{"id":154},"inside-the-incident","Inside the incident",[12,157,158,159,164],{},"After extensive testing via our automated performance test suite and in preproduction\nenvironments, we felt ready to explore compaction on production nodes. We kept a\n",[16,160,163],{"href":161,"rel":162},"https://gitlab.com/gitlab-com/gl-infra/production/-/issues/3370",[],"detailed, public record of what happened\nduring this production incident",", but the key details are summarized below:",[101,166,167,170,173,180,191],{},[65,168,169],{},"The deployment passed the canary stage, meaning workers who had their heaps compacted were serving traffic\nsuccessfully at this point.",[65,171,172],{},"Sometime during the full fleet rollout, problems emerged: Error rates started spiking but not\nacross the entire fleet. This phenomenon is odd because errors tend to spread across all workers due to load balancing.",[65,174,175,176,179],{},"The error messages surfacing in Sentry were mysterious at best:\n",[42,177,178],{},"ActionView::Template::Error uninitialized constant #\u003CClass:#GrapePathHelpers::DecoratedRoute:0x00007f95f10ea5b8>::UNDERSCORE",". Remember this error message for later.",[65,181,182,183,190],{},"We discovered the affected workers were segfaulting in ",[16,184,187],{"href":185,"rel":186},"https://github.com/k0kubun/hamlit",[],[42,188,189],{},"hamlit",",\na high-performance HAML compiler. Hamlit uses a C-extension to achieve better performance. The segfaulting and the fact\nthat we were rolling out an optimization that touches GC-internal structures was a tell-tale sign that\ncompaction was likely to be the cause.",[65,192,193],{},"We rolled back the change to quickly recover from the outage.",[37,195,197],{"id":196},"how-we-diagnosed-the-problem","How we diagnosed the problem",[12,199,200],{},"We were disappointed by this setback and wanted to understand why the outage occurred. Fortunately,\nRuby provides detailed stack traces when crashing in C-extensions. The most effective way\nto quickly analyze these is to look for transitions where a C-extension calls into the Ruby VM\nor vice versa. These lines therefore caught our attention:",[202,203,208],"pre",{"className":204,"code":205,"language":206,"meta":207,"style":207},"language-shell shiki shiki-themes github-light","...\n/opt/gitlab/embedded/lib/libruby.so.2.7(sigsegv+0x52) [0x7f9601adb932] signal.c:946\n/lib/x86_64-linux-gnu/libc.so.6(0x7f960154c4c0) [0x7f960154c4c0]\n/opt/gitlab/embedded/lib/libruby.so.2.7(rb_id_table_lookup+0x1) [0x7f9601b15e11] id_table.c:227\n/opt/gitlab/embedded/lib/libruby.so.2.7(rb_const_lookup+0x1e) [0x7f9601b4861e] variable.c:3357\n/opt/gitlab/embedded/lib/libruby.so.2.7(rb_const_get+0x39) [0x7f9601b4a049] variable.c:2339\n# ^--- Ruby VM functions\n/opt/gitlab/embedded/lib/ruby/gems/2.7.0/gems/hamlit-2.11.0/lib/hamlit/hamlit.so(str_underscore+0x16) [0x7f95ee3518f8] hamlit.c:17\n/opt/gitlab/embedded/lib/ruby/gems/2.7.0/gems/hamlit-2.11.0/lib/hamlit/hamlit.so(rb_hamlit_build_id) hamlit.c:100\n# ^-- hamlit C-extension\n...\n","shell","",[42,209,210,219,230,239,248,257,266,273,282,291,297],{"__ignoreMap":207},[211,212,215],"span",{"class":213,"line":214},"line",1,[211,216,218],{"class":217},"sYu0t","...\n",[211,220,222,226],{"class":213,"line":221},2,[211,223,225],{"class":224},"s7eDp","/opt/gitlab/embedded/lib/libruby.so.2.7(sigsegv+0x52",[211,227,229],{"class":228},"sgsFI",") [0x7f9601adb932] signal.c:946\n",[211,231,233,236],{"class":213,"line":232},3,[211,234,235],{"class":224},"/lib/x86_64-linux-gnu/libc.so.6(0x7f960154c4c0",[211,237,238],{"class":228},") [0x7f960154c4c0]\n",[211,240,242,245],{"class":213,"line":241},4,[211,243,244],{"class":224},"/opt/gitlab/embedded/lib/libruby.so.2.7(rb_id_table_lookup+0x1",[211,246,247],{"class":228},") [0x7f9601b15e11] id_table.c:227\n",[211,249,251,254],{"class":213,"line":250},5,[211,252,253],{"class":224},"/opt/gitlab/embedded/lib/libruby.so.2.7(rb_const_lookup+0x1e",[211,255,256],{"class":228},") [0x7f9601b4861e] variable.c:3357\n",[211,258,260,263],{"class":213,"line":259},6,[211,261,262],{"class":224},"/opt/gitlab/embedded/lib/libruby.so.2.7(rb_const_get+0x39",[211,264,265],{"class":228},") [0x7f9601b4a049] variable.c:2339\n",[211,267,269],{"class":213,"line":268},7,[211,270,272],{"class":271},"sAwPA","# ^--- Ruby VM functions\n",[211,274,276,279],{"class":213,"line":275},8,[211,277,278],{"class":224},"/opt/gitlab/embedded/lib/ruby/gems/2.7.0/gems/hamlit-2.11.0/lib/hamlit/hamlit.so(str_underscore+0x16",[211,280,281],{"class":228},") [0x7f95ee3518f8] hamlit.c:17\n",[211,283,285,288],{"class":213,"line":284},9,[211,286,287],{"class":224},"/opt/gitlab/embedded/lib/ruby/gems/2.7.0/gems/hamlit-2.11.0/lib/hamlit/hamlit.so(rb_hamlit_build_id",[211,289,290],{"class":228},") hamlit.c:100\n",[211,292,294],{"class":213,"line":293},10,[211,295,296],{"class":271},"# ^-- hamlit C-extension\n",[211,298,300],{"class":213,"line":299},11,[211,301,218],{"class":217},[12,303,304,305,308,309,312,313,316,317,320,321,328,329,332],{},"The topmost stack frame reveals the preceeding calls led to a segmentation fault (",[42,306,307],{},"SIGSEGV",").\nWe highlighted the lines where Hamlit calls back into Ruby: In a function called ",[42,310,311],{},"str_underscore"," which\nwas called by ",[42,314,315],{},"rb_hamlit_build_id",". The ",[42,318,319],{},"rb_*"," prefix tells us that this is a C-function we can call from Ruby,\nand indeed it is used by ",[16,322,325],{"href":323,"rel":324},"https://github.com/k0kubun/hamlit/blob/master/lib/hamlit/attribute_builder.rb",[],[42,326,327],{},"Hamlit::AttributeBuilder"," to construct DOM ",[42,330,331],{},"id","s.",[12,334,335,336,338,339,342,343,346,347,350],{},"But we still don't know why it is crashing. Next, we need to inspect what happens in ",[42,337,311],{},".\nWe can see that this function performs a constant lookup on ",[42,340,341],{},"mAttributeBuilder"," – searching\nfor a constant called ",[42,344,345],{},"UNDERSCORE",". When following the breadcrumbs it turns out to simply be the string ",[42,348,349],{},"\"_\"",".\nIt is this lookup that failed.",[12,352,353,354,356],{},"Wait -- ",[42,355,345],{},"? That sounds familiar. Recall the top-level error messages:",[202,358,363],{"className":359,"code":361,"language":362,"meta":207},[360],"language-text","ActionView::Template::Error\nuninitialized constant #\u003CClass:#GrapePathHelpers::DecoratedRoute:0x00007f95f10ea5b8>::UNDERSCORE\n","text",[42,364,361],{"__ignoreMap":207},[12,366,367,368,371,372,374,375,382,383,386],{},"But ",[42,369,370],{},"GrapePathHelpers"," is clearly not a Hamlit class. Hamlit is trying to look up its own ",[42,373,345],{},"\nconstant on a class in the ",[16,376,379],{"href":377,"rel":378},"https://github.com/ruby-grape/grape",[],[42,380,381],{},"grape"," gem, an entirely different library\nthat is not involved in HTML rendering at all and there is no such constant defined on Grape's\n",[42,384,385],{},"DecoratedRoute"," class either.",[12,388,389,390,392],{},"Now the penny dropped – remember how compaction moves around objects in Ruby's heap space? Classes in\nRuby are objects too, so ",[42,391,89],{}," must have moved a Grape class into an object slot that was previously\noccupied by a Hamlit class object, but Hamlit's C-extension never saw it coming!",[37,394,396],{"id":395},"how-we-solved-the-problem","How we solved the problem",[12,398,399,400,403],{},"To be clear, what happened above should ",[29,401,402],{},"not"," happen with a well-behaved C-extension. Compaction\nwas developed carefully with support for C-extensions that predate Ruby 2.7, so all\nexisting Ruby gems would continue to operate normally.",[12,405,406,407,410],{},"So what went wrong? When a C-extension allocates Ruby objects, it must ",[29,408,409],{},"mark"," them for as long as\nthey are alive. A marked object will not be garbage collected and because the Ruby GC cannot reason about objects\noutside of its own purview (i.e., objects created from Ruby code), it needs to rely on C-extensions\nto correctly mark and unmark objects themselves.",[12,412,413],{},"Now comes the twist: Marked objects can be moved during compaction and existing C-extensions\ncan't cope with an object they hold pointers to suddenly move into a different slot.\nTherefore, Ruby 2.7 does something clever: It \"pins\" objects allocated with the mark function that existed prior\nto Ruby 2.7, meaning the pinned objects are not allowed to move during compaction. For new code, it introduces\na special mark-but-don't-pin function that will also allow an object to move, giving gem authors the\nopportunity to make their libraries compaction-aware.",[12,415,416,417,422,423,428,429,432,433,438],{},"Hamlit does not implement compaction support, so this could only mean one thing:\nHamlit wasn't even properly marking those objects, otherwise Ruby 2.7\nwould have automatically pinned them so they wouldn't move during compaction.\nAfter ",[16,418,421],{"href":419,"rel":420},"https://github.com/k0kubun/hamlit/pull/171",[],"discussing an attempted fix we submitted"," but without\na reliable way to reproduce the issue for everyone, the Hamlit author decided to sidestep the\nproblem by ",[16,424,427],{"href":425,"rel":426},"https://github.com/k0kubun/hamlit/pull/172",[],"resolving those constants statically instead","\nand marking each via ",[42,430,431],{},"rb_gc_register_mark_object",".\nThis change landed in ",[16,434,437],{"href":435,"rel":436},"https://github.com/k0kubun/hamlit/blob/master/CHANGELOG.md#2142---2021-01-21",[],"Hamlit 2.14.2","\nwhich we confirmed resolves the issue.",[37,440,442],{"id":441},"the-next-steps","The next steps",[12,444,445],{},"It is exciting to see that the Ruby community is making progress on making Ruby a more memory-efficient\nlanguage but we learned that we need to step carefully when introducing such wide-reaching changes to a large\napplication like GitLab. It is difficult to investigate and fix problems that crash the Ruby VM, which is more likely for\nany library that uses C-extensions.",[12,447,448],{},"Two particular action items we took away from this were:",[62,450,451,461],{},[65,452,453,456,457,460],{},[68,454,455],{},"More reliable detection of compaction-related issues in CI."," We're not going to sugar-coat this:\nWe detected the problem late. Our comprehensive test suite was passing, our QA and performance tests\non staging environments passed, and the problem didn't even show up in canary deployments. Ideally, we\nwould have caught this issue with our automated test suite. One way to test whether compaction causes problems\nis by using ",[42,458,459],{},"GC.verify_compaction_references"," – this is a rather crude tool because it requires\nkeeping two copies of the Ruby heap, which can be prohibitively expensive in terms of memory use. We\nhave therefore not yet decided how to approach this.",[65,462,463,466,467,472,473,80],{},[68,464,465],{},"Improve our ability to roll out system configuration gradually."," Puma is part of our core infrastructure,\nsince it sits in the path of every web request, which makes it especially risky to experiment with Puma\nconfiguration. GitLab already supports ",[16,468,471],{"href":469,"rel":470},"https://docs.gitlab.com/development/feature_flags/",[],"feature flags","\nto allow developers to roll out product changes gradually, but it presents us with a catch-22 when\nmaking changes at the infrastructure level, because to query the state of a feature flag, the infrastructure\nneeds to already be up and running. It would be ideal to have a similar mechanism for system configuration, ",[16,474,477],{"href":475,"rel":476},"https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/154",[],"which we are currently exploring",[12,479,480],{},"While performance is a major focus for us at the moment it must not compromise availability.\nWe will continue to monitor developments in the Ruby community around compaction support, but decided to\nnot use it in production at this point in time since the gains don't appear to outweigh the risks.",[482,483,484],"style",{},"html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":207,"searchDepth":221,"depth":221,"links":486},[487,489,490,491,492,493],{"id":39,"depth":221,"text":488},"Nakayoshi: A friendlier fork",{"id":123,"depth":221,"text":124},{"id":154,"depth":221,"text":155},{"id":196,"depth":221,"text":197},{"id":395,"depth":221,"text":396},{"id":441,"depth":221,"text":442},"engineering","2021-04-28","An overview of Ruby 2.7 heap compaction and the risks it adds to production Rails applications.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749669673/Blog/Hero%20Images/engineering.png",{},true,"/en-us/blog/puma-nakayoshi-fork-and-compaction",{"title":5,"description":496,"ogTitle":5,"ogDescription":496,"noIndex":499,"ogImage":500,"ogUrl":505,"ogSiteName":506,"ogType":507,"canonicalUrls":505},"https://about.gitlab.com/blog/puma-nakayoshi-fork-and-compaction","https://about.gitlab.com","article","puma-nakayoshi-fork-and-compaction","en-us/blog/puma-nakayoshi-fork-and-compaction",[511,512,513],"production","performance","inside GitLab","BlogPost","Y1L14bJ-2woZw3eQr0eivkYfIUhKM3yfujoA_P9ieIQ",{"logo":517,"freeTrial":522,"sales":527,"login":532,"items":537,"search":864,"minimal":895,"duo":914,"switchNav":923,"pricingDeployment":934},{"config":518},{"href":519,"dataGaName":520,"dataGaLocation":521},"/","gitlab logo","header",{"text":523,"config":524},"Get free trial",{"href":525,"dataGaName":526,"dataGaLocation":521},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":528,"config":529},"Request a demo",{"href":530,"dataGaName":531,"dataGaLocation":521},"/sales/?contact-topic=request-demo","sales",{"text":533,"config":534},"Sign in",{"href":535,"dataGaName":536,"dataGaLocation":521},"https://gitlab.com/users/sign_in/","sign in",[538,567,667,672,786,842],{"text":539,"config":540,"menu":542},"Platform",{"dataNavLevelOne":541},"platform",{"type":543,"columns":544},"cards",[545,551,559],{"title":539,"description":546,"link":547},"The intelligent orchestration platform for DevSecOps",{"text":548,"config":549},"Explore our Platform",{"href":550,"dataGaName":541,"dataGaLocation":521},"/platform/",{"title":552,"description":553,"link":554},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":555,"config":556},"Meet GitLab Duo",{"href":557,"dataGaName":558,"dataGaLocation":521},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":560,"description":561,"link":562},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":563,"config":564},"Learn more",{"href":565,"dataGaName":566,"dataGaLocation":521},"/why-gitlab/","why gitlab",{"text":568,"left":502,"config":569,"menu":571},"Product",{"dataNavLevelOne":570},"solutions",{"type":572,"link":573,"columns":577,"feature":646},"lists",{"text":574,"config":575},"View all Solutions",{"href":576,"dataGaName":570,"dataGaLocation":521},"/solutions/",[578,602,625],{"title":579,"description":580,"link":581,"items":586},"Automation","CI/CD and automation to accelerate deployment",{"config":582},{"icon":583,"href":584,"dataGaName":585,"dataGaLocation":521},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[587,591,594,598],{"text":588,"config":589},"CI/CD",{"href":590,"dataGaLocation":521,"dataGaName":588},"/solutions/continuous-integration/",{"text":552,"config":592},{"href":557,"dataGaLocation":521,"dataGaName":593},"gitlab duo agent platform - product menu",{"text":595,"config":596},"Source Code Management",{"href":597,"dataGaLocation":521,"dataGaName":595},"/solutions/source-code-management/",{"text":599,"config":600},"Automated Software Delivery",{"href":584,"dataGaLocation":521,"dataGaName":601},"Automated software delivery",{"title":603,"description":604,"link":605,"items":610},"Security","Deliver code faster without compromising security",{"config":606},{"href":607,"dataGaName":608,"dataGaLocation":521,"icon":609},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[611,615,620],{"text":612,"config":613},"Application Security Testing",{"href":607,"dataGaName":614,"dataGaLocation":521},"Application security testing",{"text":616,"config":617},"Software Supply Chain Security",{"href":618,"dataGaLocation":521,"dataGaName":619},"/solutions/supply-chain/","Software supply chain security",{"text":621,"config":622},"Software Compliance",{"href":623,"dataGaName":624,"dataGaLocation":521},"/solutions/software-compliance/","software compliance",{"title":626,"link":627,"items":632},"Measurement",{"config":628},{"icon":629,"href":630,"dataGaName":631,"dataGaLocation":521},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[633,637,641],{"text":634,"config":635},"Visibility & Measurement",{"href":630,"dataGaLocation":521,"dataGaName":636},"Visibility and Measurement",{"text":638,"config":639},"Value Stream Management",{"href":640,"dataGaLocation":521,"dataGaName":638},"/solutions/value-stream-management/",{"text":642,"config":643},"Analytics & Insights",{"href":644,"dataGaLocation":521,"dataGaName":645},"/solutions/analytics-and-insights/","Analytics and insights",{"title":647,"type":572,"items":648},"GitLab for",[649,655,661],{"text":650,"config":651},"Enterprise",{"icon":652,"href":653,"dataGaLocation":521,"dataGaName":654},"Building","/enterprise/","enterprise",{"text":656,"config":657},"Small Business",{"icon":658,"href":659,"dataGaLocation":521,"dataGaName":660},"Work","/small-business/","small business",{"text":662,"config":663},"Public Sector",{"icon":664,"href":665,"dataGaLocation":521,"dataGaName":666},"Organization","/solutions/public-sector/","public sector",{"text":668,"config":669},"Pricing",{"href":670,"dataGaName":671,"dataGaLocation":521,"dataNavLevelOne":671},"/pricing/","pricing",{"text":673,"config":674,"menu":676},"Resources",{"dataNavLevelOne":675},"resources",{"type":572,"link":677,"columns":681,"feature":775},{"text":678,"config":679},"View all resources",{"href":680,"dataGaName":675,"dataGaLocation":521},"/resources/",[682,715,742],{"title":683,"items":684},"Getting started",[685,690,695,700,705,710],{"text":686,"config":687},"Install",{"href":688,"dataGaName":689,"dataGaLocation":521},"/install/","install",{"text":691,"config":692},"Quick start guides",{"href":693,"dataGaName":694,"dataGaLocation":521},"/get-started/","quick setup checklists",{"text":696,"config":697},"Learn",{"href":698,"dataGaLocation":521,"dataGaName":699},"https://university.gitlab.com/","learn",{"text":701,"config":702},"Product documentation",{"href":703,"dataGaName":704,"dataGaLocation":521},"https://docs.gitlab.com/","product documentation",{"text":706,"config":707},"Best practice videos",{"href":708,"dataGaName":709,"dataGaLocation":521},"/getting-started-videos/","best practice videos",{"text":711,"config":712},"Integrations",{"href":713,"dataGaName":714,"dataGaLocation":521},"/integrations/","integrations",{"title":716,"items":717},"Discover",[718,723,728,733,737],{"text":719,"config":720},"Customer success stories",{"href":721,"dataGaName":722,"dataGaLocation":521},"/customers/","customer success stories",{"text":724,"config":725},"Blog",{"href":726,"dataGaName":727,"dataGaLocation":521},"/blog/","blog",{"text":729,"config":730},"Demo Hub",{"href":731,"dataGaName":732,"dataGaLocation":521},"/demo-hub/","demo hub",{"text":734,"config":735},"The Source",{"href":736,"dataGaName":727,"dataGaLocation":521},"/the-source/",{"text":738,"config":739},"Remote",{"href":740,"dataGaName":741,"dataGaLocation":521},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":743,"items":744},"Connect",[745,750,755,760,765,770],{"text":746,"config":747},"GitLab Services",{"href":748,"dataGaName":749,"dataGaLocation":521},"/services/","services",{"text":751,"config":752},"Contribute",{"href":753,"dataGaName":754,"dataGaLocation":521},"https://contributors.gitlab.com","contribute",{"text":756,"config":757},"Community",{"href":758,"dataGaName":759,"dataGaLocation":521},"/community/","community",{"text":761,"config":762},"Forum",{"href":763,"dataGaName":764,"dataGaLocation":521},"https://forum.gitlab.com/","forum",{"text":766,"config":767},"Events",{"href":768,"dataGaName":769,"dataGaLocation":521},"/events/","events",{"text":771,"config":772},"Partners",{"href":773,"dataGaName":774,"dataGaLocation":521},"/partners/","partners",{"config":776,"title":779,"text":780,"link":781},{"background":777,"textColor":778},"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":782,"config":783},"Read the latest",{"href":784,"dataGaName":785,"dataGaLocation":521},"/whats-new/","whats new",{"text":787,"config":788,"menu":790},"Company",{"dataNavLevelOne":789},"company",{"type":572,"columns":791},[792],{"items":793},[794,799,805,807,812,817,822,827,832,837],{"text":795,"config":796},"About",{"href":797,"dataGaName":798,"dataGaLocation":521},"/company/","about",{"text":800,"config":801,"footerGa":804},"Jobs",{"href":802,"dataGaName":803,"dataGaLocation":521},"/jobs/","jobs",{"dataGaName":803},{"text":766,"config":806},{"href":768,"dataGaName":769,"dataGaLocation":521},{"text":808,"config":809},"Leadership",{"href":810,"dataGaName":811,"dataGaLocation":521},"/company/team/e-group/","leadership",{"text":813,"config":814},"Handbook",{"href":815,"dataGaName":816,"dataGaLocation":521},"https://handbook.gitlab.com/","handbook",{"text":818,"config":819},"Investor relations",{"href":820,"dataGaName":821,"dataGaLocation":521},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":823,"config":824},"Trust Center",{"href":825,"dataGaName":826,"dataGaLocation":521},"/security/","trust center",{"text":828,"config":829},"AI Transparency Center",{"href":830,"dataGaName":831,"dataGaLocation":521},"/ai-transparency-center/","ai transparency center",{"text":833,"config":834},"Newsletter",{"href":835,"dataGaName":836,"dataGaLocation":521},"/company/contact/#contact-forms","newsletter",{"text":838,"config":839},"Press",{"href":840,"dataGaName":841,"dataGaLocation":521},"/press/","press",{"text":843,"config":844,"menu":845},"Contact us",{"dataNavLevelOne":789},{"type":572,"columns":846},[847],{"items":848},[849,854,859],{"text":850,"config":851},"Talk to sales",{"href":852,"dataGaName":853,"dataGaLocation":521},"/sales/","talk to sales",{"text":855,"config":856},"Support portal",{"href":857,"dataGaName":858,"dataGaLocation":521},"https://support.gitlab.com/hc/en-us","support portal",{"text":860,"config":861},"Customer portal",{"href":862,"dataGaName":863,"dataGaLocation":521},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":865,"login":866,"suggestions":873},"Close",{"text":867,"link":868},"To search repositories and projects, login to",{"text":869,"config":870},"gitlab.com",{"href":535,"dataGaName":871,"dataGaLocation":872},"search login","search",{"text":874,"default":875},"Suggestions",[876,878,882,884,888,892],{"text":552,"config":877},{"href":557,"dataGaName":552,"dataGaLocation":872},{"text":879,"config":880},"Code Suggestions (AI)",{"href":881,"dataGaName":879,"dataGaLocation":872},"/solutions/code-suggestions/",{"text":588,"config":883},{"href":590,"dataGaName":588,"dataGaLocation":872},{"text":885,"config":886},"GitLab on AWS",{"href":887,"dataGaName":885,"dataGaLocation":872},"/partners/technology-partners/aws/",{"text":889,"config":890},"GitLab on Google Cloud",{"href":891,"dataGaName":889,"dataGaLocation":872},"/partners/technology-partners/google-cloud-platform/",{"text":893,"config":894},"Why GitLab?",{"href":565,"dataGaName":893,"dataGaLocation":872},{"freeTrial":896,"mobileIcon":901,"desktopIcon":906,"secondaryButton":909},{"text":897,"config":898},"Start free trial",{"href":899,"dataGaName":526,"dataGaLocation":900},"https://gitlab.com/-/trials/new/","nav",{"altText":902,"config":903},"Gitlab Icon",{"src":904,"dataGaName":905,"dataGaLocation":900},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":902,"config":907},{"src":908,"dataGaName":905,"dataGaLocation":900},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":910,"config":911},"Get Started",{"href":912,"dataGaName":913,"dataGaLocation":900},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":915,"mobileIcon":919,"desktopIcon":921},{"text":916,"config":917},"Learn more about GitLab Duo",{"href":557,"dataGaName":918,"dataGaLocation":900},"gitlab duo",{"altText":902,"config":920},{"src":904,"dataGaName":905,"dataGaLocation":900},{"altText":902,"config":922},{"src":908,"dataGaName":905,"dataGaLocation":900},{"button":924,"mobileIcon":929,"desktopIcon":931},{"text":925,"config":926},"/switch",{"href":927,"dataGaName":928,"dataGaLocation":900},"#contact","switch",{"altText":902,"config":930},{"src":904,"dataGaName":905,"dataGaLocation":900},{"altText":902,"config":932},{"src":933,"dataGaName":905,"dataGaLocation":900},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":935,"mobileIcon":940,"desktopIcon":942},{"text":936,"config":937},"Back to pricing",{"href":670,"dataGaName":938,"dataGaLocation":900,"icon":939},"back to pricing","GoBack",{"altText":902,"config":941},{"src":904,"dataGaName":905,"dataGaLocation":900},{"altText":902,"config":943},{"src":908,"dataGaName":905,"dataGaLocation":900},{"title":945,"titleMobile":946,"button":947,"config":952},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":563,"config":948},{"href":949,"dataGaName":950,"dataGaLocation":951},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":953,"disabled":499},"release",{"data":955},{"text":956,"source":957,"edit":963,"contribute":968,"config":973,"items":978,"minimal":1188},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":958,"config":959},"View page source",{"href":960,"dataGaName":961,"dataGaLocation":962},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":964,"config":965},"Edit this page",{"href":966,"dataGaName":967,"dataGaLocation":962},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":969,"config":970},"Please contribute",{"href":971,"dataGaName":972,"dataGaLocation":962},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":974,"facebook":975,"youtube":976,"linkedin":977},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[979,1026,1080,1124,1156],{"title":668,"links":980,"subMenu":995},[981,985,990],{"text":982,"config":983},"View plans",{"href":670,"dataGaName":984,"dataGaLocation":962},"view plans",{"text":986,"config":987},"Why Premium?",{"href":988,"dataGaName":989,"dataGaLocation":962},"/pricing/premium/","why premium",{"text":991,"config":992},"Why Ultimate?",{"href":993,"dataGaName":994,"dataGaLocation":962},"/pricing/ultimate/","why ultimate",[996],{"title":997,"links":998},"Contact Us",[999,1002,1004,1006,1011,1016,1021],{"text":1000,"config":1001},"Contact sales",{"href":852,"dataGaName":531,"dataGaLocation":962},{"text":855,"config":1003},{"href":857,"dataGaName":858,"dataGaLocation":962},{"text":860,"config":1005},{"href":862,"dataGaName":863,"dataGaLocation":962},{"text":1007,"config":1008},"Status",{"href":1009,"dataGaName":1010,"dataGaLocation":962},"https://status.gitlab.com/","status",{"text":1012,"config":1013},"Terms of use",{"href":1014,"dataGaName":1015,"dataGaLocation":962},"/terms/","terms of use",{"text":1017,"config":1018},"Privacy statement",{"href":1019,"dataGaName":1020,"dataGaLocation":962},"/privacy/","privacy statement",{"text":1022,"config":1023},"Cookie preferences",{"dataGaName":1024,"dataGaLocation":962,"id":1025,"isOneTrustButton":502},"cookie preferences","ot-sdk-btn",{"title":568,"links":1027,"subMenu":1036},[1028,1032],{"text":1029,"config":1030},"DevSecOps platform",{"href":550,"dataGaName":1031,"dataGaLocation":962},"devsecops platform",{"text":1033,"config":1034},"AI-Assisted Development",{"href":557,"dataGaName":1035,"dataGaLocation":962},"ai-assisted development",[1037],{"title":1038,"links":1039},"Topics",[1040,1045,1050,1055,1060,1065,1070,1075],{"text":1041,"config":1042},"CICD",{"href":1043,"dataGaName":1044,"dataGaLocation":962},"/topics/ci-cd/","cicd",{"text":1046,"config":1047},"GitOps",{"href":1048,"dataGaName":1049,"dataGaLocation":962},"/topics/gitops/","gitops",{"text":1051,"config":1052},"DevOps",{"href":1053,"dataGaName":1054,"dataGaLocation":962},"/topics/devops/","devops",{"text":1056,"config":1057},"Version Control",{"href":1058,"dataGaName":1059,"dataGaLocation":962},"/topics/version-control/","version control",{"text":1061,"config":1062},"DevSecOps",{"href":1063,"dataGaName":1064,"dataGaLocation":962},"/topics/devsecops/","devsecops",{"text":1066,"config":1067},"Cloud Native",{"href":1068,"dataGaName":1069,"dataGaLocation":962},"/topics/cloud-native/","cloud native",{"text":1071,"config":1072},"AI for Coding",{"href":1073,"dataGaName":1074,"dataGaLocation":962},"/topics/devops/ai-for-coding/","ai for coding",{"text":1076,"config":1077},"Agentic AI",{"href":1078,"dataGaName":1079,"dataGaLocation":962},"/topics/agentic-ai/","agentic ai",{"title":1081,"links":1082},"Solutions",[1083,1085,1087,1092,1096,1099,1103,1106,1108,1111,1114,1119],{"text":612,"config":1084},{"href":607,"dataGaName":612,"dataGaLocation":962},{"text":601,"config":1086},{"href":584,"dataGaName":585,"dataGaLocation":962},{"text":1088,"config":1089},"Agile development",{"href":1090,"dataGaName":1091,"dataGaLocation":962},"/solutions/agile-delivery/","agile delivery",{"text":1093,"config":1094},"SCM",{"href":597,"dataGaName":1095,"dataGaLocation":962},"source code management",{"text":1041,"config":1097},{"href":590,"dataGaName":1098,"dataGaLocation":962},"continuous integration & delivery",{"text":1100,"config":1101},"Value stream management",{"href":640,"dataGaName":1102,"dataGaLocation":962},"value stream management",{"text":1046,"config":1104},{"href":1105,"dataGaName":1049,"dataGaLocation":962},"/solutions/gitops/",{"text":650,"config":1107},{"href":653,"dataGaName":654,"dataGaLocation":962},{"text":1109,"config":1110},"Small business",{"href":659,"dataGaName":660,"dataGaLocation":962},{"text":1112,"config":1113},"Public sector",{"href":665,"dataGaName":666,"dataGaLocation":962},{"text":1115,"config":1116},"Education",{"href":1117,"dataGaName":1118,"dataGaLocation":962},"/solutions/education/","education",{"text":1120,"config":1121},"Financial services",{"href":1122,"dataGaName":1123,"dataGaLocation":962},"/solutions/finance/","financial services",{"title":673,"links":1125},[1126,1128,1130,1132,1135,1137,1140,1142,1144,1146,1148,1150,1152,1154],{"text":686,"config":1127},{"href":688,"dataGaName":689,"dataGaLocation":962},{"text":691,"config":1129},{"href":693,"dataGaName":694,"dataGaLocation":962},{"text":696,"config":1131},{"href":698,"dataGaName":699,"dataGaLocation":962},{"text":701,"config":1133},{"href":703,"dataGaName":1134,"dataGaLocation":962},"docs",{"text":724,"config":1136},{"href":726,"dataGaName":727,"dataGaLocation":962},{"text":1138,"config":1139},"What's new",{"href":784,"dataGaName":785,"dataGaLocation":962},{"text":719,"config":1141},{"href":721,"dataGaName":722,"dataGaLocation":962},{"text":738,"config":1143},{"href":740,"dataGaName":741,"dataGaLocation":962},{"text":746,"config":1145},{"href":748,"dataGaName":749,"dataGaLocation":962},{"text":751,"config":1147},{"href":753,"dataGaName":754,"dataGaLocation":962},{"text":756,"config":1149},{"href":758,"dataGaName":759,"dataGaLocation":962},{"text":761,"config":1151},{"href":763,"dataGaName":764,"dataGaLocation":962},{"text":766,"config":1153},{"href":768,"dataGaName":769,"dataGaLocation":962},{"text":771,"config":1155},{"href":773,"dataGaName":774,"dataGaLocation":962},{"title":787,"links":1157},[1158,1160,1162,1164,1166,1168,1172,1177,1179,1181,1183],{"text":795,"config":1159},{"href":797,"dataGaName":789,"dataGaLocation":962},{"text":800,"config":1161},{"href":802,"dataGaName":803,"dataGaLocation":962},{"text":808,"config":1163},{"href":810,"dataGaName":811,"dataGaLocation":962},{"text":813,"config":1165},{"href":815,"dataGaName":816,"dataGaLocation":962},{"text":818,"config":1167},{"href":820,"dataGaName":821,"dataGaLocation":962},{"text":1169,"config":1170},"Sustainability",{"href":1171,"dataGaName":1169,"dataGaLocation":962},"/sustainability/",{"text":1173,"config":1174},"Diversity, inclusion and belonging (DIB)",{"href":1175,"dataGaName":1176,"dataGaLocation":962},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":823,"config":1178},{"href":825,"dataGaName":826,"dataGaLocation":962},{"text":833,"config":1180},{"href":835,"dataGaName":836,"dataGaLocation":962},{"text":838,"config":1182},{"href":840,"dataGaName":841,"dataGaLocation":962},{"text":1184,"config":1185},"Modern Slavery Transparency Statement",{"href":1186,"dataGaName":1187,"dataGaLocation":962},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1189},[1190,1193,1196],{"text":1191,"config":1192},"Terms",{"href":1014,"dataGaName":1015,"dataGaLocation":962},{"text":1194,"config":1195},"Cookies",{"dataGaName":1024,"dataGaLocation":962,"id":1025,"isOneTrustButton":502},{"text":1197,"config":1198},"Privacy",{"href":1019,"dataGaName":1020,"dataGaLocation":962},[1200],{"id":1201,"title":1202,"body":498,"config":1203,"content":1205,"description":498,"extension":1209,"meta":1210,"navigation":502,"path":1211,"seo":1212,"stem":1213,"__hash__":1214},"blogAuthors/en-us/blog/authors/matthias-kppler.yml","Matthias Kppler",{"template":1204},"BlogAuthor",{"name":7,"config":1206},{"headshot":1207,"ctfId":1208},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749670351/Blog/Author%20Headshots/mkaeppler-headshot.jpg","mkaeppler","yml",{},"/en-us/blog/authors/matthias-kppler",{},"en-us/blog/authors/matthias-kppler","ifYoCrN_DXBz-4NxWtEPrkhCLzL27iwXIaXEy_ogYFc",[1216,1225,1233],{"title":1217,"description":1218,"heroImage":1219,"category":494,"date":1220,"authors":1221,"slug":1224,"externalUrl":498},"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",[1222,1223],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1226,"description":1227,"heroImage":1228,"category":494,"date":1229,"authors":1230,"slug":1232,"externalUrl":498},"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",[1231],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1234,"description":1235,"heroImage":1236,"category":494,"date":1237,"authors":1238,"slug":1240,"externalUrl":498},"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",[1239],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1242},[1243,1257,1269,1281],{"id":1244,"categories":1245,"header":1247,"text":1248,"button":1249,"image":1254},"ai-modernization",[1246],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1250,"config":1251},"Get your AI maturity score",{"href":1252,"dataGaName":1253,"dataGaLocation":727},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1255},{"src":1256},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1258,"categories":1259,"header":1261,"text":1248,"button":1262,"image":1266},"devops-modernization",[1260,1064],"product","Are you just managing tools or shipping innovation?",{"text":1263,"config":1264},"Get your DevOps maturity score",{"href":1265,"dataGaName":1253,"dataGaLocation":727},"/assessments/devops-modernization-assessment/",{"config":1267},{"src":1268},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1270,"categories":1271,"header":1273,"text":1248,"button":1274,"image":1278},"security-modernization",[1272],"security","Are you trading speed for security?",{"text":1275,"config":1276},"Get your security maturity score",{"href":1277,"dataGaName":1253,"dataGaLocation":727},"/assessments/security-modernization-assessment/",{"config":1279},{"src":1280},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1282,"paths":1283,"header":1286,"text":1287,"button":1288,"image":1293},"github-azure-migration",[1284,1285],"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":1289,"config":1290},"See how GitLab compares to GitHub",{"href":1291,"dataGaName":1292,"dataGaLocation":727},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1294},{"src":1268},{"header":1296,"blurb":1297,"button":1298,"secondaryButton":1303},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1299,"config":1300},"Get your free trial",{"href":1301,"dataGaName":526,"dataGaLocation":1302},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1000,"config":1304},{"href":852,"dataGaName":531,"dataGaLocation":1302},1786803764700]