[{"data":1,"prerenderedAt":1164},["ShallowReactive",2],{"/blog/fast-search-using-postgresql-trigram":3,"navigation-en-us":376,"banner-en-us":804,"footer-en-us":814,"blog-post-authors-en-us-Yorick Peterse":1059,"blog-related-posts-en-us-fast-search-using-postgresql-trigram":1074,"blog-promotions-en-us":1100,"next-steps-en-us":1154},{"id":4,"title":5,"authors":6,"body":8,"category":354,"date":355,"description":356,"extension":357,"externalUrl":358,"faq":358,"featured":359,"heroImage":360,"meta":361,"navigation":366,"path":367,"seo":368,"slug":372,"stem":373,"tags":358,"template":374,"updatedDate":358,"__hash__":375},"blogPosts/en-us/blog/fast-search-using-postgresql-trigram.md","Fast Search Using PostgreSQL Trigram Text Indexes",[7],"Yorick Peterse",{"type":9,"value":10,"toc":347},"minimark",[11,15,20,28,38,41,64,67,84,87,91,94,100,103,109,112,118,129,141,147,158,161,167,170,173,177,187,193,202,208,211,217,220,235,242,248,251,257,260,266,269,275,278,284,287,291,310,332,339],[12,13,14],"p",{},"GitLab 8.6 will ship with improved search performance for PostgreSQL thanks to\nthe use of trigram indexes. In this article we'll look at how these indexes work\nand how they can be used to speed up queries using LIKE conditions.",[16,17,19],"h2",{"id":18},"how-to-use-postgresql-fast-search-in-gitlab","How to Use PostgreSQL Fast Search in GitLab?",[12,21,22,23,27],{},"GitLab allows users to search for issues, comments, commits, code, merge\nrequests, and snippets. The traditional approach to developing a system for\nsearching data like this in an RDBMS is to simply use a LIKE condition. A LIKE\noperates on a string that specifies what to search for and optional percentage\nsigns acting as wildcards. For example, to match all values starting with\n\"Alice\" you'd use the string ",[24,25,26],"code",{},"'Alice%'",". If one wants to search all records in a\ntable (e.g. all users) they might write the following query:",[29,30,35],"pre",{"className":31,"code":33,"language":34},[32],"language-text","SELECT *\nFROM users\nWHERE name LIKE 'Alice%';\n","text",[24,36,33],{"__ignoreMap":37},"",[12,39,40],{},"The wildcards used for a LIKE condition can appear anywhere (and optionally\nmultiple times) in the string, as such all these values are valid:",[42,43,44,49,54,59],"ul",{},[45,46,47],"li",{},[24,48,26],{},[45,50,51],{},[24,52,53],{},"'%Alice'",[45,55,56],{},[24,57,58],{},"'%Alice%'",[45,60,61],{},[24,62,63],{},"'%Al%ice%'",[12,65,66],{},"However, wildcards being allowed poses a problem: index usage. When a wildcard\nappears somewhere at the end of a string both MySQL and PostgreSQL are able to\nuse any existing indexes. However, when a wildcard appears at the start of a\nstring things become problematic. To better understand the problem, imagine you\nhave the following list of names:",[42,68,69,72,75,78,81],{},[45,70,71],{},"Alice",[45,73,74],{},"Bob",[45,76,77],{},"Charlie",[45,79,80],{},"Eve",[45,82,83],{},"Emily",[12,85,86],{},"When searching for any name containing \"li\" the only solution is to iterate over\nall values and check if each value contains the string \"li\". Because the value\ncan appear anywhere in the strings to search an index won't help as we'd still\nhave to compare every value one by one with no way of reducing the set of rows\nto search through. This in turn can lead to very slow queries depending on the\namount of data to search through.",[16,88,90],{"id":89},"postgres-full-text-search","Postgres Full Text Search",[12,92,93],{},"Since both MySQL and PostgreSQL provide full text searching capabilities one\nsolution would be to use this instead of a regular LIKE condition. Sadly both\nimplementations are not without their problems. Up until MySQL 5.6 full text\nsearch only worked on MyISAM tables which in turn meant not being able to use\ntransactions. Both MySQL and PostgreSQL also use different syntax for searching\nand require different steps to set things up. For example, MySQL uses the\nfollowing syntax:",[29,95,98],{"className":96,"code":97,"language":34},[32],"SELECT *\nFROM users\nWHERE MATCH (username) AGAINST ('yorick');\n",[24,99,97],{"__ignoreMap":37},[12,101,102],{},"PostgreSQL uses the following instead:",[29,104,107],{"className":105,"code":106,"language":34},[32],"SELECT *\nFROM users\nWHERE to_tsvector('english', username) @@ to_tsquery('english', 'yorick');\n",[24,108,106],{"__ignoreMap":37},[12,110,111],{},"The differences in syntax make the code more complex. On top of that\nPostgresSQL full text search works best when the text vectors are stored in\nphysical columns with an index. This in turn means having to adjust all your\nqueries to use these columns instead of the regular ones, resulting queries such\nas:",[29,113,116],{"className":114,"code":115,"language":34},[32],"SELECT *\nFROM users\nWHERE username_tsvector @@ to_tsquery('english', 'yorick');\n",[24,117,115],{"__ignoreMap":37},[12,119,120,121,124,125,128],{},"This assumes ",[24,122,123],{},"username_tsvector"," contains a text vector built from the data\nstored in the ",[24,126,127],{},"username"," column. To further complicate matters you'd have to set\nup a stored procedure and database trigger to keep these text vector columns in\nsync with the ones containing the raw data.",[12,130,131,132,136,137,140],{},"Another problem with full text search is that words are broken up according to\nthe rules defined by the language of the text. For example, on PostgreSQL\nconverting \"Yorick Peterse\" to a text vector results in the values \"peters\" and\n\"yorick\". This means that searching for \"yorick\" or \"peterse\" ",[133,134,135],"em",{},"will"," match the\ndata, but searching for \"yor\" ",[133,138,139],{},"will not",". To showcase this we can run the\nfollowing query in PostgreSQL:",[29,142,145],{"className":143,"code":144,"language":34},[32],"SELECT 1\nWHERE to_tsvector('english', 'Yorick Peterse') @@ to_tsquery('english', 'peterse');\n",[24,146,144],{"__ignoreMap":37},[12,148,149,150,153,154,157],{},"Here ",[24,151,152],{},"to_tsvector()"," creates a text vector with English as the language and\n\"Yorick Peterse\" as the input. The ",[24,155,156],{},"to_tsquery()"," function in turn creates a\ntext search query with English as the language and \"peterse\" as the input.",[12,159,160],{},"Running this query will result in a single row being returned. On the other\nhand, this will return no rows:",[29,162,165],{"className":163,"code":164,"language":34},[32],"SELECT 1\nWHERE to_tsvector('english', 'Yorick Peterse') @@ to_tsquery('english', 'yor');\n",[24,166,164],{"__ignoreMap":37},[12,168,169],{},"This is problematic when you don't know exactly what you're looking for, for\nexample when you're looking for a person but only know part of their first name.",[12,171,172],{},"In short, full text search is only really an option if you only support\nPostgreSQL or MySQL as supporting both leads to a lot of unwanted complexity.",[16,174,176],{"id":175},"trigram-indexes","Trigram Indexes",[12,178,179,180,186],{},"While MySQL offers no further solutions (that I know of), PostgreSQL on the\nother hand has some extra tricks up its sleeves: trigram indexes. Trigram\nindexes work by breaking up text in ",[181,182,185],"a",{"href":183,"rel":184},"https://en.wikipedia.org/wiki/Trigram",[],"trigrams",". Trigrams are basically\nwords broken up into sequences of 3 letters. For example, the trigram for\n\"alice\" would be:",[29,188,191],{"className":189,"code":190,"language":34},[32],"{ali, lic, ice}\n",[24,192,190],{"__ignoreMap":37},[12,194,195,196,201],{},"PostgreSQL supports trigram indexes and operations via the ",[181,197,200],{"href":198,"rel":199},"http://www.postgresql.org/docs/current/static/pgtrgm.html",[],"pg_trgm","\nextension. This extension adds a few functions, operators, and support for\ntrigram indexes (Postgres using GIN or GiST indexes to be exact). To see what kind of\ntrigrams PostgreSQL can produce we can run the following query:",[29,203,206],{"className":204,"code":205,"language":34},[32],"select show_trgm('alice');\n",[24,207,205],{"__ignoreMap":37},[12,209,210],{},"This will generate the trigrams for the string \"alice\", producing the following\noutput:",[29,212,215],{"className":213,"code":214,"language":34},[32],"            show_trgm\n---------------------------------\n {\"  a\",\" al\",ali,\"ce \",ice,lic}\n",[24,216,214],{"__ignoreMap":37},[12,218,219],{},"A big benefit of this extension is that these trigram indexes can be used by the\nLIKE and ILIKE conditions without having to change your queries or setting up\ncomplex full text search systems. There are 2 requirements for this to work:",[221,222,223,226],"ol",{},[45,224,225],{},"The index created must be either a GIN or a GiST index, in case of GitLab we\nwent with Postgres using GIN indexes due to them leading to better query timings (at the\ncost of being larger and somewhat slower to build).",[45,227,228,229,234],{},"The index must have the appropriate ",[181,230,233],{"href":231,"rel":232},"http://www.postgresql.org/docs/current/static/indexes-opclass.html",[],"operator class"," set.",[12,236,237,238,241],{},"In case of a GIN index the operator class we have to use is called\n",[24,239,240],{},"gin_trgm_ops",". We can create the appropriate indexes using a query such as the\nfollowing:",[29,243,246],{"className":244,"code":245,"language":34},[32],"CREATE INDEX CONCURRENTLY index_issues_on_title_trigram\nON issues\nUSING gin (title gin_trgm_ops);\n",[24,247,245],{"__ignoreMap":37},[12,249,250],{},"To showcase the impact these indexes have on performance let's use the following\nquery as an example:",[29,252,255],{"className":253,"code":254,"language":34},[32],"SELECT COUNT(*)\nFROM users\nWHERE username ILIKE '%yorick%';\n",[24,256,254],{"__ignoreMap":37},[12,258,259],{},"This query counts the amount of users where the username contains the string\n\"yorick\", regardless of the casing. Running this query on my local PostgreSQL\ndatabase takes around 160 milliseconds and produces the following query plan:",[29,261,264],{"className":262,"code":263,"language":34},[32]," Aggregate  (cost=8143.40..8143.41 rows=1 width=0) (actual time=157.981..157.982 rows=1 loops=1)\n   ->  Index Only Scan using index_users_on_username on users  (cost=0.42..8143.34 rows=26 width=0) (actual time=155.153..157.974 rows=6 loops=1)\n         Filter: (username ~~* '%yorick%'::text)\n         Rows Removed by Filter: 257532\n         Heap Fetches: 0\n Planning time: 0.143 ms\n Execution time: 158.008 ms\n",[24,265,263],{"__ignoreMap":37},[12,267,268],{},"To speed this up we'll run the following to create an index:",[29,270,273],{"className":271,"code":272,"language":34},[32],"CREATE INDEX CONCURRENTLY index_users_on_username_trigram\nON users\nUSING gin (username gin_trgm_ops);\n",[24,274,272],{"__ignoreMap":37},[12,276,277],{},"If we now re-run the query it takes only around 0.2 milliseconds and produces\nthe following query plan:",[29,279,282],{"className":280,"code":281,"language":34},[32]," Aggregate  (cost=152.41..152.42 rows=1 width=0) (actual time=0.128..0.128 rows=1 loops=1)\n   ->  Bitmap Heap Scan on users  (cost=52.20..152.35 rows=26 width=0) (actual time=0.115..0.126 rows=6 loops=1)\n         Recheck Cond: (username ~~* '%yorick%'::text)\n         Heap Blocks: exact=6\n         ->  Bitmap Index Scan on index_users_on_username_trigram  (cost=0.00..52.19 rows=26 width=0) (actual time=0.106..0.106 rows=6 loops=1)\n               Index Cond: (username ~~* '%yorick%'::text)\n Planning time: 0.366 ms\n Execution time: 0.167 ms\n",[24,283,281],{"__ignoreMap":37},[12,285,286],{},"In other words, creating the trigram index results in the query being around\n946 times faster.",[16,288,290],{"id":289},"gitlab-trigram-indexes","GitLab & Trigram Indexes",[12,292,293,294,297,298,303,304,309],{},"GitLab 8.6 will create trigram indexes for PostgreSQL users leading to vastly\nimproved search performance (though there's still some work to be done in the\nfuture). To make this work (while still supporting MySQL) we did have to port\nover some changes from an open Rails pull request to ensure the indexes were\ndumped properly to ",[24,295,296],{},"db/schema.rb",". These changes can be found in\n",[181,299,302],{"href":300,"rel":301},"https://gitlab.com/gitlab-org/gitlab-ce/blob/0602091f0cdebbc3183732dee78c38f89b4b7d01/config/initializers/postgresql_opclasses_support.rb",[],"config/initializers/postgresql_opclasses_support.rb"," and were\ntaken from ",[181,305,308],{"href":306,"rel":307},"https://github.com/rails/rails/pull/19090",[],"Rails pull request #19090",".",[12,311,312,313,315,316,319,320,323,324,326,327,309],{},"We also had to make some changes to ensure MySQL doesn't end up trying to create\nthese indexes when loading the schema definition into a database. For example,\n",[24,314,296],{}," contains lines such as ",[24,317,318],{},"add_index ..., using: :gin"," and the\n",[24,321,322],{},"using"," option is passed straight to the underlying database. Since MySQL\ndoesn't support GIN indexes this would lead to database errors when trying to\nload ",[24,325,296],{},". The code that makes this work can be found in\n",[181,328,331],{"href":329,"rel":330},"https://gitlab.com/gitlab-org/gitlab-ce/blob/0602091f0cdebbc3183732dee78c38f89b4b7d01/config/initializers/mysql_ignore_postgresql_options.rb",[],"config/initializers/mysql_ignore_postgresql_options.rb",[12,333,334,335,338],{},"Finally we made some small changes to the code to ensure queries automatically\nuse ILIKE on PostgreSQL instead of ",[24,336,337],{},"lower(some_column)"," as ILIKE performs quite\na bit better. On MySQL a regular LIKE is used as it's already case-insensitive.",[12,340,341,342,309],{},"All of the other details can be found in GitLab CE merge request ",[181,343,346],{"href":344,"rel":345},"https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2987",[],"\"Refactor\nsearching and use PostgreSQL trigram indexes for significantly improved\nperformance\"",{"title":37,"searchDepth":348,"depth":348,"links":349},2,[350,351,352,353],{"id":18,"depth":348,"text":19},{"id":89,"depth":348,"text":90},{"id":175,"depth":348,"text":176},{"id":289,"depth":348,"text":290},"engineering","2016-03-18","In this article we'll look at how these indexes work and how they can be used to speed up queries using LIKE conditions.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663397/Blog/Hero%20Images/logoforblogpost.jpg",{"excerpt":362},{"type":9,"value":363},[364],[12,365,14],{},true,"/en-us/blog/fast-search-using-postgresql-trigram",{"title":5,"description":356,"ogTitle":5,"ogDescription":356,"noIndex":359,"ogImage":360,"ogUrl":369,"ogSiteName":370,"ogType":371,"canonicalUrls":369},"https://about.gitlab.com/blog/fast-search-using-postgresql-trigram-indexes","https://about.gitlab.com","article","fast-search-using-postgresql-trigram","en-us/blog/fast-search-using-postgresql-trigram","BlogPost","XGQWRmFgGJNq_t6jihVSQoGiPn6e56lXjN_NAKfseVk",{"logo":377,"freeTrial":382,"sales":387,"login":392,"items":397,"search":724,"minimal":755,"duo":774,"switchNav":783,"pricingDeployment":794},{"config":378},{"href":379,"dataGaName":380,"dataGaLocation":381},"/","gitlab logo","header",{"text":383,"config":384},"Get free trial",{"href":385,"dataGaName":386,"dataGaLocation":381},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":388,"config":389},"Request a demo",{"href":390,"dataGaName":391,"dataGaLocation":381},"/sales/?contact-topic=request-demo","sales",{"text":393,"config":394},"Sign in",{"href":395,"dataGaName":396,"dataGaLocation":381},"https://gitlab.com/users/sign_in/","sign in",[398,427,527,532,646,702],{"text":399,"config":400,"menu":402},"Platform",{"dataNavLevelOne":401},"platform",{"type":403,"columns":404},"cards",[405,411,419],{"title":399,"description":406,"link":407},"The intelligent orchestration platform for DevSecOps",{"text":408,"config":409},"Explore our Platform",{"href":410,"dataGaName":401,"dataGaLocation":381},"/platform/",{"title":412,"description":413,"link":414},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":415,"config":416},"Meet GitLab Duo",{"href":417,"dataGaName":418,"dataGaLocation":381},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":420,"description":421,"link":422},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":423,"config":424},"Learn more",{"href":425,"dataGaName":426,"dataGaLocation":381},"/why-gitlab/","why gitlab",{"text":428,"left":366,"config":429,"menu":431},"Product",{"dataNavLevelOne":430},"solutions",{"type":432,"link":433,"columns":437,"feature":506},"lists",{"text":434,"config":435},"View all Solutions",{"href":436,"dataGaName":430,"dataGaLocation":381},"/solutions/",[438,462,485],{"title":439,"description":440,"link":441,"items":446},"Automation","CI/CD and automation to accelerate deployment",{"config":442},{"icon":443,"href":444,"dataGaName":445,"dataGaLocation":381},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[447,451,454,458],{"text":448,"config":449},"CI/CD",{"href":450,"dataGaLocation":381,"dataGaName":448},"/solutions/continuous-integration/",{"text":412,"config":452},{"href":417,"dataGaLocation":381,"dataGaName":453},"gitlab duo agent platform - product menu",{"text":455,"config":456},"Source Code Management",{"href":457,"dataGaLocation":381,"dataGaName":455},"/solutions/source-code-management/",{"text":459,"config":460},"Automated Software Delivery",{"href":444,"dataGaLocation":381,"dataGaName":461},"Automated software delivery",{"title":463,"description":464,"link":465,"items":470},"Security","Deliver code faster without compromising security",{"config":466},{"href":467,"dataGaName":468,"dataGaLocation":381,"icon":469},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[471,475,480],{"text":472,"config":473},"Application Security Testing",{"href":467,"dataGaName":474,"dataGaLocation":381},"Application security testing",{"text":476,"config":477},"Software Supply Chain Security",{"href":478,"dataGaLocation":381,"dataGaName":479},"/solutions/supply-chain/","Software supply chain security",{"text":481,"config":482},"Software Compliance",{"href":483,"dataGaName":484,"dataGaLocation":381},"/solutions/software-compliance/","software compliance",{"title":486,"link":487,"items":492},"Measurement",{"config":488},{"icon":489,"href":490,"dataGaName":491,"dataGaLocation":381},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[493,497,501],{"text":494,"config":495},"Visibility & Measurement",{"href":490,"dataGaLocation":381,"dataGaName":496},"Visibility and Measurement",{"text":498,"config":499},"Value Stream Management",{"href":500,"dataGaLocation":381,"dataGaName":498},"/solutions/value-stream-management/",{"text":502,"config":503},"Analytics & Insights",{"href":504,"dataGaLocation":381,"dataGaName":505},"/solutions/analytics-and-insights/","Analytics and insights",{"title":507,"type":432,"items":508},"GitLab for",[509,515,521],{"text":510,"config":511},"Enterprise",{"icon":512,"href":513,"dataGaLocation":381,"dataGaName":514},"Building","/enterprise/","enterprise",{"text":516,"config":517},"Small Business",{"icon":518,"href":519,"dataGaLocation":381,"dataGaName":520},"Work","/small-business/","small business",{"text":522,"config":523},"Public Sector",{"icon":524,"href":525,"dataGaLocation":381,"dataGaName":526},"Organization","/solutions/public-sector/","public sector",{"text":528,"config":529},"Pricing",{"href":530,"dataGaName":531,"dataGaLocation":381,"dataNavLevelOne":531},"/pricing/","pricing",{"text":533,"config":534,"menu":536},"Resources",{"dataNavLevelOne":535},"resources",{"type":432,"link":537,"columns":541,"feature":635},{"text":538,"config":539},"View all resources",{"href":540,"dataGaName":535,"dataGaLocation":381},"/resources/",[542,575,602],{"title":543,"items":544},"Getting started",[545,550,555,560,565,570],{"text":546,"config":547},"Install",{"href":548,"dataGaName":549,"dataGaLocation":381},"/install/","install",{"text":551,"config":552},"Quick start guides",{"href":553,"dataGaName":554,"dataGaLocation":381},"/get-started/","quick setup checklists",{"text":556,"config":557},"Learn",{"href":558,"dataGaLocation":381,"dataGaName":559},"https://university.gitlab.com/","learn",{"text":561,"config":562},"Product documentation",{"href":563,"dataGaName":564,"dataGaLocation":381},"https://docs.gitlab.com/","product documentation",{"text":566,"config":567},"Best practice videos",{"href":568,"dataGaName":569,"dataGaLocation":381},"/getting-started-videos/","best practice videos",{"text":571,"config":572},"Integrations",{"href":573,"dataGaName":574,"dataGaLocation":381},"/integrations/","integrations",{"title":576,"items":577},"Discover",[578,583,588,593,597],{"text":579,"config":580},"Customer success stories",{"href":581,"dataGaName":582,"dataGaLocation":381},"/customers/","customer success stories",{"text":584,"config":585},"Blog",{"href":586,"dataGaName":587,"dataGaLocation":381},"/blog/","blog",{"text":589,"config":590},"Demo Hub",{"href":591,"dataGaName":592,"dataGaLocation":381},"/demo-hub/","demo hub",{"text":594,"config":595},"The Source",{"href":596,"dataGaName":587,"dataGaLocation":381},"/the-source/",{"text":598,"config":599},"Remote",{"href":600,"dataGaName":601,"dataGaLocation":381},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":603,"items":604},"Connect",[605,610,615,620,625,630],{"text":606,"config":607},"GitLab Services",{"href":608,"dataGaName":609,"dataGaLocation":381},"/services/","services",{"text":611,"config":612},"Contribute",{"href":613,"dataGaName":614,"dataGaLocation":381},"https://contributors.gitlab.com","contribute",{"text":616,"config":617},"Community",{"href":618,"dataGaName":619,"dataGaLocation":381},"/community/","community",{"text":621,"config":622},"Forum",{"href":623,"dataGaName":624,"dataGaLocation":381},"https://forum.gitlab.com/","forum",{"text":626,"config":627},"Events",{"href":628,"dataGaName":629,"dataGaLocation":381},"/events/","events",{"text":631,"config":632},"Partners",{"href":633,"dataGaName":634,"dataGaLocation":381},"/partners/","partners",{"config":636,"title":639,"text":640,"link":641},{"background":637,"textColor":638},"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":642,"config":643},"Read the latest",{"href":644,"dataGaName":645,"dataGaLocation":381},"/whats-new/","whats new",{"text":647,"config":648,"menu":650},"Company",{"dataNavLevelOne":649},"company",{"type":432,"columns":651},[652],{"items":653},[654,659,665,667,672,677,682,687,692,697],{"text":655,"config":656},"About",{"href":657,"dataGaName":658,"dataGaLocation":381},"/company/","about",{"text":660,"config":661,"footerGa":664},"Jobs",{"href":662,"dataGaName":663,"dataGaLocation":381},"/jobs/","jobs",{"dataGaName":663},{"text":626,"config":666},{"href":628,"dataGaName":629,"dataGaLocation":381},{"text":668,"config":669},"Leadership",{"href":670,"dataGaName":671,"dataGaLocation":381},"/company/team/e-group/","leadership",{"text":673,"config":674},"Handbook",{"href":675,"dataGaName":676,"dataGaLocation":381},"https://handbook.gitlab.com/","handbook",{"text":678,"config":679},"Investor relations",{"href":680,"dataGaName":681,"dataGaLocation":381},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":683,"config":684},"Trust Center",{"href":685,"dataGaName":686,"dataGaLocation":381},"/security/","trust center",{"text":688,"config":689},"AI Transparency Center",{"href":690,"dataGaName":691,"dataGaLocation":381},"/ai-transparency-center/","ai transparency center",{"text":693,"config":694},"Newsletter",{"href":695,"dataGaName":696,"dataGaLocation":381},"/company/contact/#contact-forms","newsletter",{"text":698,"config":699},"Press",{"href":700,"dataGaName":701,"dataGaLocation":381},"/press/","press",{"text":703,"config":704,"menu":705},"Contact us",{"dataNavLevelOne":649},{"type":432,"columns":706},[707],{"items":708},[709,714,719],{"text":710,"config":711},"Talk to sales",{"href":712,"dataGaName":713,"dataGaLocation":381},"/sales/","talk to sales",{"text":715,"config":716},"Support portal",{"href":717,"dataGaName":718,"dataGaLocation":381},"https://support.gitlab.com/hc/en-us","support portal",{"text":720,"config":721},"Customer portal",{"href":722,"dataGaName":723,"dataGaLocation":381},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":725,"login":726,"suggestions":733},"Close",{"text":727,"link":728},"To search repositories and projects, login to",{"text":729,"config":730},"gitlab.com",{"href":395,"dataGaName":731,"dataGaLocation":732},"search login","search",{"text":734,"default":735},"Suggestions",[736,738,742,744,748,752],{"text":412,"config":737},{"href":417,"dataGaName":412,"dataGaLocation":732},{"text":739,"config":740},"Code Suggestions (AI)",{"href":741,"dataGaName":739,"dataGaLocation":732},"/solutions/code-suggestions/",{"text":448,"config":743},{"href":450,"dataGaName":448,"dataGaLocation":732},{"text":745,"config":746},"GitLab on AWS",{"href":747,"dataGaName":745,"dataGaLocation":732},"/partners/technology-partners/aws/",{"text":749,"config":750},"GitLab on Google Cloud",{"href":751,"dataGaName":749,"dataGaLocation":732},"/partners/technology-partners/google-cloud-platform/",{"text":753,"config":754},"Why GitLab?",{"href":425,"dataGaName":753,"dataGaLocation":732},{"freeTrial":756,"mobileIcon":761,"desktopIcon":766,"secondaryButton":769},{"text":757,"config":758},"Start free trial",{"href":759,"dataGaName":386,"dataGaLocation":760},"https://gitlab.com/-/trials/new/","nav",{"altText":762,"config":763},"Gitlab Icon",{"src":764,"dataGaName":765,"dataGaLocation":760},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":762,"config":767},{"src":768,"dataGaName":765,"dataGaLocation":760},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":770,"config":771},"Get Started",{"href":772,"dataGaName":773,"dataGaLocation":760},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":775,"mobileIcon":779,"desktopIcon":781},{"text":776,"config":777},"Learn more about GitLab Duo",{"href":417,"dataGaName":778,"dataGaLocation":760},"gitlab duo",{"altText":762,"config":780},{"src":764,"dataGaName":765,"dataGaLocation":760},{"altText":762,"config":782},{"src":768,"dataGaName":765,"dataGaLocation":760},{"button":784,"mobileIcon":789,"desktopIcon":791},{"text":785,"config":786},"/switch",{"href":787,"dataGaName":788,"dataGaLocation":760},"#contact","switch",{"altText":762,"config":790},{"src":764,"dataGaName":765,"dataGaLocation":760},{"altText":762,"config":792},{"src":793,"dataGaName":765,"dataGaLocation":760},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":795,"mobileIcon":800,"desktopIcon":802},{"text":796,"config":797},"Back to pricing",{"href":530,"dataGaName":798,"dataGaLocation":760,"icon":799},"back to pricing","GoBack",{"altText":762,"config":801},{"src":764,"dataGaName":765,"dataGaLocation":760},{"altText":762,"config":803},{"src":768,"dataGaName":765,"dataGaLocation":760},{"title":805,"titleMobile":806,"button":807,"config":812},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":423,"config":808},{"href":809,"dataGaName":810,"dataGaLocation":811},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":813,"disabled":359},"release",{"data":815},{"text":816,"source":817,"edit":823,"contribute":828,"config":833,"items":838,"minimal":1048},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":818,"config":819},"View page source",{"href":820,"dataGaName":821,"dataGaLocation":822},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":824,"config":825},"Edit this page",{"href":826,"dataGaName":827,"dataGaLocation":822},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":829,"config":830},"Please contribute",{"href":831,"dataGaName":832,"dataGaLocation":822},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":834,"facebook":835,"youtube":836,"linkedin":837},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[839,886,940,984,1016],{"title":528,"links":840,"subMenu":855},[841,845,850],{"text":842,"config":843},"View plans",{"href":530,"dataGaName":844,"dataGaLocation":822},"view plans",{"text":846,"config":847},"Why Premium?",{"href":848,"dataGaName":849,"dataGaLocation":822},"/pricing/premium/","why premium",{"text":851,"config":852},"Why Ultimate?",{"href":853,"dataGaName":854,"dataGaLocation":822},"/pricing/ultimate/","why ultimate",[856],{"title":857,"links":858},"Contact Us",[859,862,864,866,871,876,881],{"text":860,"config":861},"Contact sales",{"href":712,"dataGaName":391,"dataGaLocation":822},{"text":715,"config":863},{"href":717,"dataGaName":718,"dataGaLocation":822},{"text":720,"config":865},{"href":722,"dataGaName":723,"dataGaLocation":822},{"text":867,"config":868},"Status",{"href":869,"dataGaName":870,"dataGaLocation":822},"https://status.gitlab.com/","status",{"text":872,"config":873},"Terms of use",{"href":874,"dataGaName":875,"dataGaLocation":822},"/terms/","terms of use",{"text":877,"config":878},"Privacy statement",{"href":879,"dataGaName":880,"dataGaLocation":822},"/privacy/","privacy statement",{"text":882,"config":883},"Cookie preferences",{"dataGaName":884,"dataGaLocation":822,"id":885,"isOneTrustButton":366},"cookie preferences","ot-sdk-btn",{"title":428,"links":887,"subMenu":896},[888,892],{"text":889,"config":890},"DevSecOps platform",{"href":410,"dataGaName":891,"dataGaLocation":822},"devsecops platform",{"text":893,"config":894},"AI-Assisted Development",{"href":417,"dataGaName":895,"dataGaLocation":822},"ai-assisted development",[897],{"title":898,"links":899},"Topics",[900,905,910,915,920,925,930,935],{"text":901,"config":902},"CICD",{"href":903,"dataGaName":904,"dataGaLocation":822},"/topics/ci-cd/","cicd",{"text":906,"config":907},"GitOps",{"href":908,"dataGaName":909,"dataGaLocation":822},"/topics/gitops/","gitops",{"text":911,"config":912},"DevOps",{"href":913,"dataGaName":914,"dataGaLocation":822},"/topics/devops/","devops",{"text":916,"config":917},"Version Control",{"href":918,"dataGaName":919,"dataGaLocation":822},"/topics/version-control/","version control",{"text":921,"config":922},"DevSecOps",{"href":923,"dataGaName":924,"dataGaLocation":822},"/topics/devsecops/","devsecops",{"text":926,"config":927},"Cloud Native",{"href":928,"dataGaName":929,"dataGaLocation":822},"/topics/cloud-native/","cloud native",{"text":931,"config":932},"AI for Coding",{"href":933,"dataGaName":934,"dataGaLocation":822},"/topics/devops/ai-for-coding/","ai for coding",{"text":936,"config":937},"Agentic AI",{"href":938,"dataGaName":939,"dataGaLocation":822},"/topics/agentic-ai/","agentic ai",{"title":941,"links":942},"Solutions",[943,945,947,952,956,959,963,966,968,971,974,979],{"text":472,"config":944},{"href":467,"dataGaName":472,"dataGaLocation":822},{"text":461,"config":946},{"href":444,"dataGaName":445,"dataGaLocation":822},{"text":948,"config":949},"Agile development",{"href":950,"dataGaName":951,"dataGaLocation":822},"/solutions/agile-delivery/","agile delivery",{"text":953,"config":954},"SCM",{"href":457,"dataGaName":955,"dataGaLocation":822},"source code management",{"text":901,"config":957},{"href":450,"dataGaName":958,"dataGaLocation":822},"continuous integration & delivery",{"text":960,"config":961},"Value stream management",{"href":500,"dataGaName":962,"dataGaLocation":822},"value stream management",{"text":906,"config":964},{"href":965,"dataGaName":909,"dataGaLocation":822},"/solutions/gitops/",{"text":510,"config":967},{"href":513,"dataGaName":514,"dataGaLocation":822},{"text":969,"config":970},"Small business",{"href":519,"dataGaName":520,"dataGaLocation":822},{"text":972,"config":973},"Public sector",{"href":525,"dataGaName":526,"dataGaLocation":822},{"text":975,"config":976},"Education",{"href":977,"dataGaName":978,"dataGaLocation":822},"/solutions/education/","education",{"text":980,"config":981},"Financial services",{"href":982,"dataGaName":983,"dataGaLocation":822},"/solutions/finance/","financial services",{"title":533,"links":985},[986,988,990,992,995,997,1000,1002,1004,1006,1008,1010,1012,1014],{"text":546,"config":987},{"href":548,"dataGaName":549,"dataGaLocation":822},{"text":551,"config":989},{"href":553,"dataGaName":554,"dataGaLocation":822},{"text":556,"config":991},{"href":558,"dataGaName":559,"dataGaLocation":822},{"text":561,"config":993},{"href":563,"dataGaName":994,"dataGaLocation":822},"docs",{"text":584,"config":996},{"href":586,"dataGaName":587,"dataGaLocation":822},{"text":998,"config":999},"What's new",{"href":644,"dataGaName":645,"dataGaLocation":822},{"text":579,"config":1001},{"href":581,"dataGaName":582,"dataGaLocation":822},{"text":598,"config":1003},{"href":600,"dataGaName":601,"dataGaLocation":822},{"text":606,"config":1005},{"href":608,"dataGaName":609,"dataGaLocation":822},{"text":611,"config":1007},{"href":613,"dataGaName":614,"dataGaLocation":822},{"text":616,"config":1009},{"href":618,"dataGaName":619,"dataGaLocation":822},{"text":621,"config":1011},{"href":623,"dataGaName":624,"dataGaLocation":822},{"text":626,"config":1013},{"href":628,"dataGaName":629,"dataGaLocation":822},{"text":631,"config":1015},{"href":633,"dataGaName":634,"dataGaLocation":822},{"title":647,"links":1017},[1018,1020,1022,1024,1026,1028,1032,1037,1039,1041,1043],{"text":655,"config":1019},{"href":657,"dataGaName":649,"dataGaLocation":822},{"text":660,"config":1021},{"href":662,"dataGaName":663,"dataGaLocation":822},{"text":668,"config":1023},{"href":670,"dataGaName":671,"dataGaLocation":822},{"text":673,"config":1025},{"href":675,"dataGaName":676,"dataGaLocation":822},{"text":678,"config":1027},{"href":680,"dataGaName":681,"dataGaLocation":822},{"text":1029,"config":1030},"Sustainability",{"href":1031,"dataGaName":1029,"dataGaLocation":822},"/sustainability/",{"text":1033,"config":1034},"Diversity, inclusion and belonging (DIB)",{"href":1035,"dataGaName":1036,"dataGaLocation":822},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":683,"config":1038},{"href":685,"dataGaName":686,"dataGaLocation":822},{"text":693,"config":1040},{"href":695,"dataGaName":696,"dataGaLocation":822},{"text":698,"config":1042},{"href":700,"dataGaName":701,"dataGaLocation":822},{"text":1044,"config":1045},"Modern Slavery Transparency Statement",{"href":1046,"dataGaName":1047,"dataGaLocation":822},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1049},[1050,1053,1056],{"text":1051,"config":1052},"Terms",{"href":874,"dataGaName":875,"dataGaLocation":822},{"text":1054,"config":1055},"Cookies",{"dataGaName":884,"dataGaLocation":822,"id":885,"isOneTrustButton":366},{"text":1057,"config":1058},"Privacy",{"href":879,"dataGaName":880,"dataGaLocation":822},[1060],{"id":1061,"title":7,"body":358,"config":1062,"content":1064,"description":358,"extension":1068,"meta":1069,"navigation":366,"path":1070,"seo":1071,"stem":1072,"__hash__":1073},"blogAuthors/en-us/blog/authors/yorick-peterse.yml",{"template":1063},"BlogAuthor",{"name":7,"config":1065},{"headshot":1066,"ctfId":1067},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","Yorick-Peterse","yml",{},"/en-us/blog/authors/yorick-peterse",{},"en-us/blog/authors/yorick-peterse","VGbmFb88hdwYhr9lyGHjbwPUUHPQ6npgsw4txxlgB14",[1075,1084,1092],{"title":1076,"description":1077,"heroImage":1078,"category":354,"date":1079,"authors":1080,"slug":1083,"externalUrl":358},"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",[1081,1082],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1085,"description":1086,"heroImage":1087,"category":354,"date":1088,"authors":1089,"slug":1091,"externalUrl":358},"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",[1090],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1093,"description":1094,"heroImage":1095,"category":354,"date":1096,"authors":1097,"slug":1099,"externalUrl":358},"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",[1098],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1101},[1102,1116,1128,1140],{"id":1103,"categories":1104,"header":1106,"text":1107,"button":1108,"image":1113},"ai-modernization",[1105],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1109,"config":1110},"Get your AI maturity score",{"href":1111,"dataGaName":1112,"dataGaLocation":587},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1114},{"src":1115},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1117,"categories":1118,"header":1120,"text":1107,"button":1121,"image":1125},"devops-modernization",[1119,924],"product","Are you just managing tools or shipping innovation?",{"text":1122,"config":1123},"Get your DevOps maturity score",{"href":1124,"dataGaName":1112,"dataGaLocation":587},"/assessments/devops-modernization-assessment/",{"config":1126},{"src":1127},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1129,"categories":1130,"header":1132,"text":1107,"button":1133,"image":1137},"security-modernization",[1131],"security","Are you trading speed for security?",{"text":1134,"config":1135},"Get your security maturity score",{"href":1136,"dataGaName":1112,"dataGaLocation":587},"/assessments/security-modernization-assessment/",{"config":1138},{"src":1139},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1141,"paths":1142,"header":1145,"text":1146,"button":1147,"image":1152},"github-azure-migration",[1143,1144],"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":1148,"config":1149},"See how GitLab compares to GitHub",{"href":1150,"dataGaName":1151,"dataGaLocation":587},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1153},{"src":1127},{"header":1155,"blurb":1156,"button":1157,"secondaryButton":1162},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1158,"config":1159},"Get your free trial",{"href":1160,"dataGaName":386,"dataGaLocation":1161},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":860,"config":1163},{"href":712,"dataGaName":391,"dataGaLocation":1161},1786803759305]