[{"data":1,"prerenderedAt":1168},["ShallowReactive",2],{"/blog/how-to-create-a-ci-cd-pipeline-with-auto-deploy-to-kubernetes-using-gitlab":3,"navigation-en-us":382,"banner-en-us":809,"footer-en-us":819,"blog-post-authors-en-us-Sergey Nuzhdin":1063,"blog-related-posts-en-us-how-to-create-a-ci-cd-pipeline-with-auto-deploy-to-kubernetes-using-gitlab":1078,"blog-promotions-en-us":1104,"next-steps-en-us":1158},{"id":4,"title":5,"authors":6,"body":8,"category":353,"date":354,"description":355,"extension":356,"externalUrl":357,"faq":357,"featured":358,"heroImage":359,"meta":360,"navigation":161,"path":367,"seo":368,"slug":373,"stem":374,"tags":375,"template":380,"updatedDate":357,"__hash__":381},"blogPosts/en-us/blog/how-to-create-a-ci-cd-pipeline-with-auto-deploy-to-kubernetes-using-gitlab.md","How to create a CI/CD pipeline with Auto Deploy to Kubernetes using GitLab and Helm",[7],"Sergey Nuzhdin",{"type":9,"value":10,"toc":347},"minimark",[11,21,41,49,65,70,81,91,94,233,236,242,246,260,268,272,275,282,287,290,293,299,302,309,315,319,325,335,343],[12,13,14,15,20],"p",{},"Recently, I started working on a few Golang ",[16,17,19],"a",{"href":18},"/topics/microservices/","microservices",". I decided to try GitLab’s caching and split the job into multiple steps for better feedback in the UI.",[12,22,23,24,29,30,35,36,40],{},"Since my previous posts[",[16,25,28],{"href":26,"rel":27},"http://blog.lwolf.org/post/how-to-build-tiny-golang-docker-images-with-gitlab-ci/",[],"1","][",[16,31,34],{"href":32,"rel":33},"http://blog.lwolf.org/post/continuous-deployment-to-kubernetes-from-gitlab-ci/",[],"2","] about ",[16,37,39],{"href":38},"/topics/ci-cd/","CI/CD",", a lot has changed. I started using Helm charts for packaging applications, and stopped using docker-in-docker in gitlab-runner.",[12,42,43,44,48],{},"Here are a few of the main changes to my ",[45,46,47],"code",{},".gitlab-ci.yml"," file since my previous post:",[50,51,52,56,59,62],"ul",{},[53,54,55],"li",{},"no docker-in-docker",[53,57,58],{},"using cache for packages instead of a prebuilt image with dependencies",[53,60,61],{},"splitting everything into multiple steps",[53,63,64],{},"autodeploy to staging environment using Helm, a package manager for Kubernetes",[66,67,69],"h3",{"id":68},"building-golang-image","Building Golang image",[12,71,72,73,76,77,80],{},"Since Golang is very strict about the location of the project, we need to make some adjustments to the CI job. This is done in the ",[45,74,75],{},"before_script"," block. Simply create needed directories and link source code in there. Assuming that the official repository of the project is ",[45,78,79],{},"gitlab.example.com/librerio/libr_files"," it should look like this.",[82,83,89],"pre",{"className":84,"code":86,"language":87,"meta":88},[85],"language-text","variables:\n  APP_PATH: /go/src/gitlab.example.com/librerio/libr_files\n\nbefore_script:\n  - mkdir -p /go/src/gitlab.example.com/librerio/\n  - ln -s $PWD ${APP_PATH}\n  - mkdir -p ${APP_PATH}/vendor\n  - cd ${APP_PATH}\n\n","text","",[45,90,86],{"__ignoreMap":88},[12,92,93],{},"With this in place, we can install dependencies and build our binaries. To avoid the download of all packages on each build we need to configure caching. Due to the strange caching rules of GitLab, we need to add vendor directory to both cache and artifacts. Cache will give us an ability to use it between build jobs and artifacts will allow us to use it inside the same job.",[82,95,99],{"className":96,"code":97,"language":98,"meta":88,"style":88},"language-yaml shiki shiki-themes github-light","cache:\n  untracked: true\n  key: \"$CI_BUILD_REF_NAME\"\n  paths:\n    - vendor/\n\nsetup:\n  stage: setup\n  image: lwolf/golang-glide:0.12.3\n  script:\n    - glide install -v\n  artifacts:\n    paths:\n     - vendor/\n\n","yaml",[45,100,101,114,127,139,147,156,163,171,182,193,201,209,217,225],{"__ignoreMap":88},[102,103,106,110],"span",{"class":104,"line":105},"line",1,[102,107,109],{"class":108},"shJU0","cache",[102,111,113],{"class":112},"sgsFI",":\n",[102,115,117,120,123],{"class":104,"line":116},2,[102,118,119],{"class":108},"  untracked",[102,121,122],{"class":112},": ",[102,124,126],{"class":125},"sYu0t","true\n",[102,128,130,133,135],{"class":104,"line":129},3,[102,131,132],{"class":108},"  key",[102,134,122],{"class":112},[102,136,138],{"class":137},"sYBdl","\"$CI_BUILD_REF_NAME\"\n",[102,140,142,145],{"class":104,"line":141},4,[102,143,144],{"class":108},"  paths",[102,146,113],{"class":112},[102,148,150,153],{"class":104,"line":149},5,[102,151,152],{"class":112},"    - ",[102,154,155],{"class":137},"vendor/\n",[102,157,159],{"class":104,"line":158},6,[102,160,162],{"emptyLinePlaceholder":161},true,"\n",[102,164,166,169],{"class":104,"line":165},7,[102,167,168],{"class":108},"setup",[102,170,113],{"class":112},[102,172,174,177,179],{"class":104,"line":173},8,[102,175,176],{"class":108},"  stage",[102,178,122],{"class":112},[102,180,181],{"class":137},"setup\n",[102,183,185,188,190],{"class":104,"line":184},9,[102,186,187],{"class":108},"  image",[102,189,122],{"class":112},[102,191,192],{"class":137},"lwolf/golang-glide:0.12.3\n",[102,194,196,199],{"class":104,"line":195},10,[102,197,198],{"class":108},"  script",[102,200,113],{"class":112},[102,202,204,206],{"class":104,"line":203},11,[102,205,152],{"class":112},[102,207,208],{"class":137},"glide install -v\n",[102,210,212,215],{"class":104,"line":211},12,[102,213,214],{"class":108},"  artifacts",[102,216,113],{"class":112},[102,218,220,223],{"class":104,"line":219},13,[102,221,222],{"class":108},"    paths",[102,224,113],{"class":112},[102,226,228,231],{"class":104,"line":227},14,[102,229,230],{"class":112},"     - ",[102,232,155],{"class":137},[12,234,235],{},"Build step didn’t change, it’s still about building the binary. I add binary to artifacts.",[82,237,240],{"className":238,"code":239,"language":87,"meta":88},[85],"build:\n  stage: build\n  image: lwolf/golang-glide:0.12.3\n  script:\n    - cd ${APP_PATH}\n    - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o release/app -ldflags '-w -s'\n    - cd release\n  artifacts:\n    paths:\n     - release/\n\n",[45,241,239],{"__ignoreMap":88},[66,243,245],{"id":244},"test-stage","Test stage",[12,247,248,249,254,255,259],{},"To run golang tests with coverage reports I’m using the variation of ",[16,250,253],{"href":251,"rel":252},"https://github.com/mlafeldt/chef-runner/blob/v0.7.0/script/coverage",[],"this shell script",". It runs all tests in project subdirectories and creates a ",[16,256,258],{"href":257},"/blog/publish-code-coverage-report-with-gitlab-pages/","coverage report",". I changed it a bit before putting into a gist. I exclude vendor directory from tests.",[50,261,262],{},[53,263,264,265],{},"coverage regexp for gitlab-ci: ",[45,266,267],{},"^total:\\s*\\(statements\\)\\s*(\\d+.\\d+\\%)",[66,269,271],{"id":270},"deploy-stage","Deploy stage",[12,273,274],{},"I don’t use native GitLab’s integration with Kubernetes.",[12,276,277,278,281],{},"First I thought about creating Kubernetes secrets and mounting it to the gitlab-runner pod. But it’s very complicated. You need to upgrade deployment every time you want to add new Kubernetes cluster configurations. So I’m using GitLab’s CI/CD variables with base64 encoded Kubernetes config. Each project can have any number of configurations. The process is easy – create base64 string from the configuration file and copy it to the clipboard. After this, put it into ",[45,279,280],{},"kube_config"," variable (name it whatever you like).",[12,283,284],{},[45,285,286],{},"cat ~/.kube/config | base64 | pbcopy",[12,288,289],{},"If you do not own a full GitLab installation, consider creating a Kubernetes user with restricted permissions.",[12,291,292],{},"Then on the deploy stage, we can decode this variable back into the file and use it with kubectl.",[82,294,297],{"className":295,"code":296,"language":87,"meta":88},[85],"variables:\n  KUBECONFIG: /etc/deploy/config\n\ndeploy:\n  ...\n  before_script:\n    - mkdir -p /etc/deploy\n    - echo ${kube_config} | base64 -d > ${KUBECONFIG}\n    - kubectl config use-context homekube\n    - helm init --client-only\n    - helm repo add stable https://kubernetes-charts.storage.googleapis.com/\n    - helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com/\n    - helm repo update\n\n",[45,298,296],{"__ignoreMap":88},[12,300,301],{},"Deploy stage also covers the case when you have several versions of the same application.",[12,303,304,305,308],{},"For example, you have two versions of API: v1.0 and v1.1. All you need to do is set ",[45,306,307],{},"appVersion"," in Chart.yaml file. Build system will check API version and either deploy or upgrade needed release.",[82,310,313],{"className":311,"code":312,"language":87,"meta":88},[85],"- export API_VERSION=\"$(grep \"appVersion\" Chart.yaml | cut -d\" \" -f2)\"\n- export RELEASE_NAME=\"libr-files-v${API_VERSION/./-}\"\n- export DEPLOYS=$(helm ls | grep $RELEASE_NAME | wc -l)\n- if [ ${DEPLOYS}  -eq 0 ]; then helm install --name=${RELEASE_NAME} . --namespace=${STAGING_NAMESPACE}; else helm upgrade ${RELEASE_NAME} . --namespace=${STAGING_NAMESPACE}; fi\n",[45,314,312],{"__ignoreMap":88},[66,316,318],{"id":317},"tldr","tl;dr",[82,320,323],{"className":321,"code":322,"language":87,"meta":88},[85],"Here is complete `.gitlab-ci.yaml` file for reference.\n\ncache:\n  untracked: true\n  key: \"$CI_BUILD_REF_NAME\"\n  paths:\n    - vendor/\n\nbefore_script:\n  - mkdir -p /go/src/gitlab.example.com/librerio/\n  - ln -s $PWD ${APP_PATH}\n  - mkdir -p ${APP_PATH}/vendor\n  - cd ${APP_PATH}\n\nstages:\n  - setup\n  - test\n  - build\n  - release\n  - deploy\n\nvariables:\n  CONTAINER_IMAGE: ${CI_REGISTRY}/${CI_PROJECT_PATH}:${CI_BUILD_REF_NAME}_${CI_BUILD_REF}\n  CONTAINER_IMAGE_LATEST: ${CI_REGISTRY}/${CI_PROJECT_PATH}:latest\n  DOCKER_DRIVER: overlay2\n\n  KUBECONFIG: /etc/deploy/config\n  STAGING_NAMESPACE: app-stage\n  PRODUCTION_NAMESPACE: app-prod\n\n  APP_PATH: /go/src/gitlab.example.com/librerio/libr_files\n  POSTGRES_USER: gorma\n  POSTGRES_DB: test-${CI_BUILD_REF}\n  POSTGRES_PASSWORD: gorma\n\nsetup:\n  stage: setup\n  image: lwolf/golang-glide:0.12.3\n  script:\n    - glide install -v\n  artifacts:\n    paths:\n     - vendor/\n\nbuild:\n  stage: build\n  image: lwolf/golang-glide:0.12.3\n  script:\n    - cd ${APP_PATH}\n    - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o release/app -ldflags '-w -s'\n    - cd release\n  artifacts:\n    paths:\n     - release/\n\nrelease:\n  stage: release\n  image: docker:latest\n  script:\n    - cd ${APP_PATH}/release\n    - docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}\n    - docker build -t ${CONTAINER_IMAGE} .\n    - docker tag ${CONTAINER_IMAGE} ${CONTAINER_IMAGE_LATEST}\n    - docker push ${CONTAINER_IMAGE}\n    - docker push ${CONTAINER_IMAGE_LATEST}\n\ntest:\n  stage: test\n  image: lwolf/golang-glide:0.12.3\n  services:\n    - postgres:9.6\n  script:\n    - cd ${APP_PATH}\n    - curl -o coverage.sh https://gist.githubusercontent.com/lwolf/3764a3b6cd08387e80aa6ca3b9534b8a/raw\n    - sh coverage.sh\n\ndeploy_staging:\n  stage: deploy\n  image: lwolf/helm-kubectl-docker:v152_213\n  before_script:\n    - mkdir -p /etc/deploy\n    - echo ${kube_config} | base64 -d > ${KUBECONFIG}\n    - kubectl config use-context homekube\n    - helm init --client-only\n    - helm repo add stable https://kubernetes-charts.storage.googleapis.com/\n    - helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com/\n    - helm repo update\n  script:\n    - cd deploy/libr-files\n    - helm dep build\n    - export API_VERSION=\"$(grep \"appVersion\" Chart.yaml | cut -d\" \" -f2)\"\n    - export RELEASE_NAME=\"libr-files-v${API_VERSION/./-}\"\n    - export DEPLOYS=$(helm ls | grep $RELEASE_NAME | wc -l)\n    - if [ ${DEPLOYS}  -eq 0 ]; then helm install --name=${RELEASE_NAME} . --namespace=${STAGING_NAMESPACE}; else helm upgrade ${RELEASE_NAME} . --namespace=${STAGING_NAMESPACE}; fi\n  environment:\n    name: staging\n    url: https://librerio.example.com\n  only:\n  - master\n\n",[45,324,322],{"__ignoreMap":88},[12,326,327],{},[328,329,330,334],"em",{},[16,331,5],{"href":332,"rel":333},"http://blog.lwolf.org/post/how-to-create-ci-cd-pipeline-with-autodeploy-k8s-gitlab-helm/",[]," was originally published on Lwolfs Blog.",[12,336,337,338],{},"Photo by C Chapman on ",[16,339,342],{"href":340,"rel":341},"https://unsplash.com/",[],"Unsplash",[344,345,346],"style",{},"html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}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":88,"searchDepth":116,"depth":116,"links":348},[349,350,351,352],{"id":68,"depth":129,"text":69},{"id":244,"depth":129,"text":245},{"id":270,"depth":129,"text":271},{"id":317,"depth":129,"text":318},"engineering","2017-09-21","One user walks through how he tried GitLab caching and split the job into multiple steps to get better feedback.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664472/Blog/Hero%20Images/gitlabflatlogomap.png",{"excerpt":361},{"type":9,"value":362},[363],[12,364,14,365,20],{},[16,366,19],{"href":18},"/en-us/blog/how-to-create-a-ci-cd-pipeline-with-auto-deploy-to-kubernetes-using-gitlab",{"title":369,"description":355,"ogTitle":369,"ogDescription":355,"noIndex":358,"ogImage":359,"ogUrl":370,"ogSiteName":371,"ogType":372,"canonicalUrls":370},"CI/CD pipeline: GitLab & Helm for Kubernetes Auto Deploy","https://about.gitlab.com/blog/how-to-create-a-ci-cd-pipeline-with-auto-deploy-to-kubernetes-using-gitlab","https://about.gitlab.com","article","how-to-create-a-ci-cd-pipeline-with-auto-deploy-to-kubernetes-using-gitlab","en-us/blog/how-to-create-a-ci-cd-pipeline-with-auto-deploy-to-kubernetes-using-gitlab",[376,377,378,379],"CI","CD","tutorial","user stories","BlogPost","ioRofzTjWn8lTBCuOfHC0tjLbjN1J2oXOZfwP5fgiXg",{"logo":383,"freeTrial":388,"sales":393,"login":398,"items":403,"search":729,"minimal":760,"duo":779,"switchNav":788,"pricingDeployment":799},{"config":384},{"href":385,"dataGaName":386,"dataGaLocation":387},"/","gitlab logo","header",{"text":389,"config":390},"Get free trial",{"href":391,"dataGaName":392,"dataGaLocation":387},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":394,"config":395},"Request a demo",{"href":396,"dataGaName":397,"dataGaLocation":387},"/sales/?contact-topic=request-demo","sales",{"text":399,"config":400},"Sign in",{"href":401,"dataGaName":402,"dataGaLocation":387},"https://gitlab.com/users/sign_in/","sign in",[404,433,532,537,651,707],{"text":405,"config":406,"menu":408},"Platform",{"dataNavLevelOne":407},"platform",{"type":409,"columns":410},"cards",[411,417,425],{"title":405,"description":412,"link":413},"The intelligent orchestration platform for DevSecOps",{"text":414,"config":415},"Explore our Platform",{"href":416,"dataGaName":407,"dataGaLocation":387},"/platform/",{"title":418,"description":419,"link":420},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":421,"config":422},"Meet GitLab Duo",{"href":423,"dataGaName":424,"dataGaLocation":387},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":426,"description":427,"link":428},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":429,"config":430},"Learn more",{"href":431,"dataGaName":432,"dataGaLocation":387},"/why-gitlab/","why gitlab",{"text":434,"left":161,"config":435,"menu":437},"Product",{"dataNavLevelOne":436},"solutions",{"type":438,"link":439,"columns":443,"feature":511},"lists",{"text":440,"config":441},"View all Solutions",{"href":442,"dataGaName":436,"dataGaLocation":387},"/solutions/",[444,467,490],{"title":445,"description":446,"link":447,"items":452},"Automation","CI/CD and automation to accelerate deployment",{"config":448},{"icon":449,"href":450,"dataGaName":451,"dataGaLocation":387},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[453,456,459,463],{"text":39,"config":454},{"href":455,"dataGaLocation":387,"dataGaName":39},"/solutions/continuous-integration/",{"text":418,"config":457},{"href":423,"dataGaLocation":387,"dataGaName":458},"gitlab duo agent platform - product menu",{"text":460,"config":461},"Source Code Management",{"href":462,"dataGaLocation":387,"dataGaName":460},"/solutions/source-code-management/",{"text":464,"config":465},"Automated Software Delivery",{"href":450,"dataGaLocation":387,"dataGaName":466},"Automated software delivery",{"title":468,"description":469,"link":470,"items":475},"Security","Deliver code faster without compromising security",{"config":471},{"href":472,"dataGaName":473,"dataGaLocation":387,"icon":474},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[476,480,485],{"text":477,"config":478},"Application Security Testing",{"href":472,"dataGaName":479,"dataGaLocation":387},"Application security testing",{"text":481,"config":482},"Software Supply Chain Security",{"href":483,"dataGaLocation":387,"dataGaName":484},"/solutions/supply-chain/","Software supply chain security",{"text":486,"config":487},"Software Compliance",{"href":488,"dataGaName":489,"dataGaLocation":387},"/solutions/software-compliance/","software compliance",{"title":491,"link":492,"items":497},"Measurement",{"config":493},{"icon":494,"href":495,"dataGaName":496,"dataGaLocation":387},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[498,502,506],{"text":499,"config":500},"Visibility & Measurement",{"href":495,"dataGaLocation":387,"dataGaName":501},"Visibility and Measurement",{"text":503,"config":504},"Value Stream Management",{"href":505,"dataGaLocation":387,"dataGaName":503},"/solutions/value-stream-management/",{"text":507,"config":508},"Analytics & Insights",{"href":509,"dataGaLocation":387,"dataGaName":510},"/solutions/analytics-and-insights/","Analytics and insights",{"title":512,"type":438,"items":513},"GitLab for",[514,520,526],{"text":515,"config":516},"Enterprise",{"icon":517,"href":518,"dataGaLocation":387,"dataGaName":519},"Building","/enterprise/","enterprise",{"text":521,"config":522},"Small Business",{"icon":523,"href":524,"dataGaLocation":387,"dataGaName":525},"Work","/small-business/","small business",{"text":527,"config":528},"Public Sector",{"icon":529,"href":530,"dataGaLocation":387,"dataGaName":531},"Organization","/solutions/public-sector/","public sector",{"text":533,"config":534},"Pricing",{"href":535,"dataGaName":536,"dataGaLocation":387,"dataNavLevelOne":536},"/pricing/","pricing",{"text":538,"config":539,"menu":541},"Resources",{"dataNavLevelOne":540},"resources",{"type":438,"link":542,"columns":546,"feature":640},{"text":543,"config":544},"View all resources",{"href":545,"dataGaName":540,"dataGaLocation":387},"/resources/",[547,580,607],{"title":548,"items":549},"Getting started",[550,555,560,565,570,575],{"text":551,"config":552},"Install",{"href":553,"dataGaName":554,"dataGaLocation":387},"/install/","install",{"text":556,"config":557},"Quick start guides",{"href":558,"dataGaName":559,"dataGaLocation":387},"/get-started/","quick setup checklists",{"text":561,"config":562},"Learn",{"href":563,"dataGaLocation":387,"dataGaName":564},"https://university.gitlab.com/","learn",{"text":566,"config":567},"Product documentation",{"href":568,"dataGaName":569,"dataGaLocation":387},"https://docs.gitlab.com/","product documentation",{"text":571,"config":572},"Best practice videos",{"href":573,"dataGaName":574,"dataGaLocation":387},"/getting-started-videos/","best practice videos",{"text":576,"config":577},"Integrations",{"href":578,"dataGaName":579,"dataGaLocation":387},"/integrations/","integrations",{"title":581,"items":582},"Discover",[583,588,593,598,602],{"text":584,"config":585},"Customer success stories",{"href":586,"dataGaName":587,"dataGaLocation":387},"/customers/","customer success stories",{"text":589,"config":590},"Blog",{"href":591,"dataGaName":592,"dataGaLocation":387},"/blog/","blog",{"text":594,"config":595},"Demo Hub",{"href":596,"dataGaName":597,"dataGaLocation":387},"/demo-hub/","demo hub",{"text":599,"config":600},"The Source",{"href":601,"dataGaName":592,"dataGaLocation":387},"/the-source/",{"text":603,"config":604},"Remote",{"href":605,"dataGaName":606,"dataGaLocation":387},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":608,"items":609},"Connect",[610,615,620,625,630,635],{"text":611,"config":612},"GitLab Services",{"href":613,"dataGaName":614,"dataGaLocation":387},"/services/","services",{"text":616,"config":617},"Contribute",{"href":618,"dataGaName":619,"dataGaLocation":387},"https://contributors.gitlab.com","contribute",{"text":621,"config":622},"Community",{"href":623,"dataGaName":624,"dataGaLocation":387},"/community/","community",{"text":626,"config":627},"Forum",{"href":628,"dataGaName":629,"dataGaLocation":387},"https://forum.gitlab.com/","forum",{"text":631,"config":632},"Events",{"href":633,"dataGaName":634,"dataGaLocation":387},"/events/","events",{"text":636,"config":637},"Partners",{"href":638,"dataGaName":639,"dataGaLocation":387},"/partners/","partners",{"config":641,"title":644,"text":645,"link":646},{"background":642,"textColor":643},"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":647,"config":648},"Read the latest",{"href":649,"dataGaName":650,"dataGaLocation":387},"/whats-new/","whats new",{"text":652,"config":653,"menu":655},"Company",{"dataNavLevelOne":654},"company",{"type":438,"columns":656},[657],{"items":658},[659,664,670,672,677,682,687,692,697,702],{"text":660,"config":661},"About",{"href":662,"dataGaName":663,"dataGaLocation":387},"/company/","about",{"text":665,"config":666,"footerGa":669},"Jobs",{"href":667,"dataGaName":668,"dataGaLocation":387},"/jobs/","jobs",{"dataGaName":668},{"text":631,"config":671},{"href":633,"dataGaName":634,"dataGaLocation":387},{"text":673,"config":674},"Leadership",{"href":675,"dataGaName":676,"dataGaLocation":387},"/company/team/e-group/","leadership",{"text":678,"config":679},"Handbook",{"href":680,"dataGaName":681,"dataGaLocation":387},"https://handbook.gitlab.com/","handbook",{"text":683,"config":684},"Investor relations",{"href":685,"dataGaName":686,"dataGaLocation":387},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":688,"config":689},"Trust Center",{"href":690,"dataGaName":691,"dataGaLocation":387},"/security/","trust center",{"text":693,"config":694},"AI Transparency Center",{"href":695,"dataGaName":696,"dataGaLocation":387},"/ai-transparency-center/","ai transparency center",{"text":698,"config":699},"Newsletter",{"href":700,"dataGaName":701,"dataGaLocation":387},"/company/contact/#contact-forms","newsletter",{"text":703,"config":704},"Press",{"href":705,"dataGaName":706,"dataGaLocation":387},"/press/","press",{"text":708,"config":709,"menu":710},"Contact us",{"dataNavLevelOne":654},{"type":438,"columns":711},[712],{"items":713},[714,719,724],{"text":715,"config":716},"Talk to sales",{"href":717,"dataGaName":718,"dataGaLocation":387},"/sales/","talk to sales",{"text":720,"config":721},"Support portal",{"href":722,"dataGaName":723,"dataGaLocation":387},"https://support.gitlab.com/hc/en-us","support portal",{"text":725,"config":726},"Customer portal",{"href":727,"dataGaName":728,"dataGaLocation":387},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":730,"login":731,"suggestions":738},"Close",{"text":732,"link":733},"To search repositories and projects, login to",{"text":734,"config":735},"gitlab.com",{"href":401,"dataGaName":736,"dataGaLocation":737},"search login","search",{"text":739,"default":740},"Suggestions",[741,743,747,749,753,757],{"text":418,"config":742},{"href":423,"dataGaName":418,"dataGaLocation":737},{"text":744,"config":745},"Code Suggestions (AI)",{"href":746,"dataGaName":744,"dataGaLocation":737},"/solutions/code-suggestions/",{"text":39,"config":748},{"href":455,"dataGaName":39,"dataGaLocation":737},{"text":750,"config":751},"GitLab on AWS",{"href":752,"dataGaName":750,"dataGaLocation":737},"/partners/technology-partners/aws/",{"text":754,"config":755},"GitLab on Google Cloud",{"href":756,"dataGaName":754,"dataGaLocation":737},"/partners/technology-partners/google-cloud-platform/",{"text":758,"config":759},"Why GitLab?",{"href":431,"dataGaName":758,"dataGaLocation":737},{"freeTrial":761,"mobileIcon":766,"desktopIcon":771,"secondaryButton":774},{"text":762,"config":763},"Start free trial",{"href":764,"dataGaName":392,"dataGaLocation":765},"https://gitlab.com/-/trials/new/","nav",{"altText":767,"config":768},"Gitlab Icon",{"src":769,"dataGaName":770,"dataGaLocation":765},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":767,"config":772},{"src":773,"dataGaName":770,"dataGaLocation":765},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":775,"config":776},"Get Started",{"href":777,"dataGaName":778,"dataGaLocation":765},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":780,"mobileIcon":784,"desktopIcon":786},{"text":781,"config":782},"Learn more about GitLab Duo",{"href":423,"dataGaName":783,"dataGaLocation":765},"gitlab duo",{"altText":767,"config":785},{"src":769,"dataGaName":770,"dataGaLocation":765},{"altText":767,"config":787},{"src":773,"dataGaName":770,"dataGaLocation":765},{"button":789,"mobileIcon":794,"desktopIcon":796},{"text":790,"config":791},"/switch",{"href":792,"dataGaName":793,"dataGaLocation":765},"#contact","switch",{"altText":767,"config":795},{"src":769,"dataGaName":770,"dataGaLocation":765},{"altText":767,"config":797},{"src":798,"dataGaName":770,"dataGaLocation":765},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":800,"mobileIcon":805,"desktopIcon":807},{"text":801,"config":802},"Back to pricing",{"href":535,"dataGaName":803,"dataGaLocation":765,"icon":804},"back to pricing","GoBack",{"altText":767,"config":806},{"src":769,"dataGaName":770,"dataGaLocation":765},{"altText":767,"config":808},{"src":773,"dataGaName":770,"dataGaLocation":765},{"title":810,"titleMobile":811,"button":812,"config":817},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":429,"config":813},{"href":814,"dataGaName":815,"dataGaLocation":816},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":818,"disabled":358},"release",{"data":820},{"text":821,"source":822,"edit":828,"contribute":833,"config":838,"items":843,"minimal":1052},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":823,"config":824},"View page source",{"href":825,"dataGaName":826,"dataGaLocation":827},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":829,"config":830},"Edit this page",{"href":831,"dataGaName":832,"dataGaLocation":827},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":834,"config":835},"Please contribute",{"href":836,"dataGaName":837,"dataGaLocation":827},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":839,"facebook":840,"youtube":841,"linkedin":842},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[844,891,944,988,1020],{"title":533,"links":845,"subMenu":860},[846,850,855],{"text":847,"config":848},"View plans",{"href":535,"dataGaName":849,"dataGaLocation":827},"view plans",{"text":851,"config":852},"Why Premium?",{"href":853,"dataGaName":854,"dataGaLocation":827},"/pricing/premium/","why premium",{"text":856,"config":857},"Why Ultimate?",{"href":858,"dataGaName":859,"dataGaLocation":827},"/pricing/ultimate/","why ultimate",[861],{"title":862,"links":863},"Contact Us",[864,867,869,871,876,881,886],{"text":865,"config":866},"Contact sales",{"href":717,"dataGaName":397,"dataGaLocation":827},{"text":720,"config":868},{"href":722,"dataGaName":723,"dataGaLocation":827},{"text":725,"config":870},{"href":727,"dataGaName":728,"dataGaLocation":827},{"text":872,"config":873},"Status",{"href":874,"dataGaName":875,"dataGaLocation":827},"https://status.gitlab.com/","status",{"text":877,"config":878},"Terms of use",{"href":879,"dataGaName":880,"dataGaLocation":827},"/terms/","terms of use",{"text":882,"config":883},"Privacy statement",{"href":884,"dataGaName":885,"dataGaLocation":827},"/privacy/","privacy statement",{"text":887,"config":888},"Cookie preferences",{"dataGaName":889,"dataGaLocation":827,"id":890,"isOneTrustButton":161},"cookie preferences","ot-sdk-btn",{"title":434,"links":892,"subMenu":901},[893,897],{"text":894,"config":895},"DevSecOps platform",{"href":416,"dataGaName":896,"dataGaLocation":827},"devsecops platform",{"text":898,"config":899},"AI-Assisted Development",{"href":423,"dataGaName":900,"dataGaLocation":827},"ai-assisted development",[902],{"title":903,"links":904},"Topics",[905,909,914,919,924,929,934,939],{"text":906,"config":907},"CICD",{"href":38,"dataGaName":908,"dataGaLocation":827},"cicd",{"text":910,"config":911},"GitOps",{"href":912,"dataGaName":913,"dataGaLocation":827},"/topics/gitops/","gitops",{"text":915,"config":916},"DevOps",{"href":917,"dataGaName":918,"dataGaLocation":827},"/topics/devops/","devops",{"text":920,"config":921},"Version Control",{"href":922,"dataGaName":923,"dataGaLocation":827},"/topics/version-control/","version control",{"text":925,"config":926},"DevSecOps",{"href":927,"dataGaName":928,"dataGaLocation":827},"/topics/devsecops/","devsecops",{"text":930,"config":931},"Cloud Native",{"href":932,"dataGaName":933,"dataGaLocation":827},"/topics/cloud-native/","cloud native",{"text":935,"config":936},"AI for Coding",{"href":937,"dataGaName":938,"dataGaLocation":827},"/topics/devops/ai-for-coding/","ai for coding",{"text":940,"config":941},"Agentic AI",{"href":942,"dataGaName":943,"dataGaLocation":827},"/topics/agentic-ai/","agentic ai",{"title":945,"links":946},"Solutions",[947,949,951,956,960,963,967,970,972,975,978,983],{"text":477,"config":948},{"href":472,"dataGaName":477,"dataGaLocation":827},{"text":466,"config":950},{"href":450,"dataGaName":451,"dataGaLocation":827},{"text":952,"config":953},"Agile development",{"href":954,"dataGaName":955,"dataGaLocation":827},"/solutions/agile-delivery/","agile delivery",{"text":957,"config":958},"SCM",{"href":462,"dataGaName":959,"dataGaLocation":827},"source code management",{"text":906,"config":961},{"href":455,"dataGaName":962,"dataGaLocation":827},"continuous integration & delivery",{"text":964,"config":965},"Value stream management",{"href":505,"dataGaName":966,"dataGaLocation":827},"value stream management",{"text":910,"config":968},{"href":969,"dataGaName":913,"dataGaLocation":827},"/solutions/gitops/",{"text":515,"config":971},{"href":518,"dataGaName":519,"dataGaLocation":827},{"text":973,"config":974},"Small business",{"href":524,"dataGaName":525,"dataGaLocation":827},{"text":976,"config":977},"Public sector",{"href":530,"dataGaName":531,"dataGaLocation":827},{"text":979,"config":980},"Education",{"href":981,"dataGaName":982,"dataGaLocation":827},"/solutions/education/","education",{"text":984,"config":985},"Financial services",{"href":986,"dataGaName":987,"dataGaLocation":827},"/solutions/finance/","financial services",{"title":538,"links":989},[990,992,994,996,999,1001,1004,1006,1008,1010,1012,1014,1016,1018],{"text":551,"config":991},{"href":553,"dataGaName":554,"dataGaLocation":827},{"text":556,"config":993},{"href":558,"dataGaName":559,"dataGaLocation":827},{"text":561,"config":995},{"href":563,"dataGaName":564,"dataGaLocation":827},{"text":566,"config":997},{"href":568,"dataGaName":998,"dataGaLocation":827},"docs",{"text":589,"config":1000},{"href":591,"dataGaName":592,"dataGaLocation":827},{"text":1002,"config":1003},"What's new",{"href":649,"dataGaName":650,"dataGaLocation":827},{"text":584,"config":1005},{"href":586,"dataGaName":587,"dataGaLocation":827},{"text":603,"config":1007},{"href":605,"dataGaName":606,"dataGaLocation":827},{"text":611,"config":1009},{"href":613,"dataGaName":614,"dataGaLocation":827},{"text":616,"config":1011},{"href":618,"dataGaName":619,"dataGaLocation":827},{"text":621,"config":1013},{"href":623,"dataGaName":624,"dataGaLocation":827},{"text":626,"config":1015},{"href":628,"dataGaName":629,"dataGaLocation":827},{"text":631,"config":1017},{"href":633,"dataGaName":634,"dataGaLocation":827},{"text":636,"config":1019},{"href":638,"dataGaName":639,"dataGaLocation":827},{"title":652,"links":1021},[1022,1024,1026,1028,1030,1032,1036,1041,1043,1045,1047],{"text":660,"config":1023},{"href":662,"dataGaName":654,"dataGaLocation":827},{"text":665,"config":1025},{"href":667,"dataGaName":668,"dataGaLocation":827},{"text":673,"config":1027},{"href":675,"dataGaName":676,"dataGaLocation":827},{"text":678,"config":1029},{"href":680,"dataGaName":681,"dataGaLocation":827},{"text":683,"config":1031},{"href":685,"dataGaName":686,"dataGaLocation":827},{"text":1033,"config":1034},"Sustainability",{"href":1035,"dataGaName":1033,"dataGaLocation":827},"/sustainability/",{"text":1037,"config":1038},"Diversity, inclusion and belonging (DIB)",{"href":1039,"dataGaName":1040,"dataGaLocation":827},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":688,"config":1042},{"href":690,"dataGaName":691,"dataGaLocation":827},{"text":698,"config":1044},{"href":700,"dataGaName":701,"dataGaLocation":827},{"text":703,"config":1046},{"href":705,"dataGaName":706,"dataGaLocation":827},{"text":1048,"config":1049},"Modern Slavery Transparency Statement",{"href":1050,"dataGaName":1051,"dataGaLocation":827},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1053},[1054,1057,1060],{"text":1055,"config":1056},"Terms",{"href":879,"dataGaName":880,"dataGaLocation":827},{"text":1058,"config":1059},"Cookies",{"dataGaName":889,"dataGaLocation":827,"id":890,"isOneTrustButton":161},{"text":1061,"config":1062},"Privacy",{"href":884,"dataGaName":885,"dataGaLocation":827},[1064],{"id":1065,"title":7,"body":357,"config":1066,"content":1068,"description":357,"extension":1072,"meta":1073,"navigation":161,"path":1074,"seo":1075,"stem":1076,"__hash__":1077},"blogAuthors/en-us/blog/authors/sergey-nuzhdin.yml",{"template":1067},"BlogAuthor",{"name":7,"config":1069},{"headshot":1070,"ctfId":1071},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","Sergey-Nuzhdin","yml",{},"/en-us/blog/authors/sergey-nuzhdin",{},"en-us/blog/authors/sergey-nuzhdin","Rw-eqM54SPd8wD7c8qyy9F-ldhwUAOjD9xSLRx5pzZE",[1079,1088,1096],{"title":1080,"description":1081,"heroImage":1082,"category":353,"date":1083,"authors":1084,"slug":1087,"externalUrl":357},"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",[1085,1086],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1089,"description":1090,"heroImage":1091,"category":353,"date":1092,"authors":1093,"slug":1095,"externalUrl":357},"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",[1094],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1097,"description":1098,"heroImage":1099,"category":353,"date":1100,"authors":1101,"slug":1103,"externalUrl":357},"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",[1102],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1105},[1106,1120,1132,1144],{"id":1107,"categories":1108,"header":1110,"text":1111,"button":1112,"image":1117},"ai-modernization",[1109],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1113,"config":1114},"Get your AI maturity score",{"href":1115,"dataGaName":1116,"dataGaLocation":592},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1118},{"src":1119},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1121,"categories":1122,"header":1124,"text":1111,"button":1125,"image":1129},"devops-modernization",[1123,928],"product","Are you just managing tools or shipping innovation?",{"text":1126,"config":1127},"Get your DevOps maturity score",{"href":1128,"dataGaName":1116,"dataGaLocation":592},"/assessments/devops-modernization-assessment/",{"config":1130},{"src":1131},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1133,"categories":1134,"header":1136,"text":1111,"button":1137,"image":1141},"security-modernization",[1135],"security","Are you trading speed for security?",{"text":1138,"config":1139},"Get your security maturity score",{"href":1140,"dataGaName":1116,"dataGaLocation":592},"/assessments/security-modernization-assessment/",{"config":1142},{"src":1143},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1145,"paths":1146,"header":1149,"text":1150,"button":1151,"image":1156},"github-azure-migration",[1147,1148],"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":1152,"config":1153},"See how GitLab compares to GitHub",{"href":1154,"dataGaName":1155,"dataGaLocation":592},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1157},{"src":1131},{"header":1159,"blurb":1160,"button":1161,"secondaryButton":1166},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1162,"config":1163},"Get your free trial",{"href":1164,"dataGaName":392,"dataGaLocation":1165},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":865,"config":1167},{"href":717,"dataGaName":397,"dataGaLocation":1165},1786803749728]