[{"data":1,"prerenderedAt":732},["ShallowReactive",2],{"/en-us/blog/ci-deployment-and-environments/":3,"navigation-en-us":38,"banner-en-us":467,"footer-en-us":484,"Ivan Nemytchenko-Cesar Saavedra":694,"next-steps-en-us":717},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":28,"_id":31,"_type":32,"title":33,"_source":34,"_file":35,"_stem":36,"_extension":37},"/en-us/blog/ci-deployment-and-environments","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"How to use GitLab CI to deploy to multiple environments","We walk you through different scenarios to demonstrate the versatility and power of GitLab CI.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749662033/Blog/Hero%20Images/intro.jpg","https://about.gitlab.com/blog/ci-deployment-and-environments","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to use GitLab CI to deploy to multiple environments\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Ivan Nemytchenko\"},{\"@type\":\"Person\",\"name\":\"Cesar Saavedra\"}],\n        \"datePublished\": \"2021-02-05\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":20,"body":21,"category":22,"tags":23,"updatedDate":27},[18,19],"Ivan Nemytchenko","Cesar Saavedra","2021-02-05","This post is a success story of one imaginary news portal, and you're the\nhappy\n\nowner, the editor, and the only developer. Luckily, you already host your\nproject\n\ncode on GitLab.com and know that you can\n\n[run tests with GitLab CI/CD](https://docs.gitlab.com/ee/ci/testing/).\n\nNow you’re curious if it can be [used for\ndeployment](/blog/how-to-keep-up-with-ci-cd-best-practices/), and how far\ncan you go with it.\n\n\nTo keep our story technology stack-agnostic, let's assume that the app is\njust a\n\nset of HTML files. No server-side code, no fancy JS assets compilation.\n\n\nDestination platform is also simplistic – we will use [Amazon\nS3](https://aws.amazon.com/s3/).\n\n\nThe goal of the article is not to give you a bunch of copy-pasteable\nsnippets.\n\nThe goal is to show the principles and features of [GitLab\nCI](/solutions/continuous-integration/) so that you can easily apply them to\nyour technology stack.\n\n{: .alert .alert-warning}\n\n\nLet’s start from the beginning. There's no continuous integration (CI) in\nour story yet.\n\n\n## At the starting line\n\n\n**Deployment**: In your case, it means that a bunch of HTML files should\nappear on your\n\nS3 bucket (which is already configured for\n\n[static website\nhosting](http://docs.aws.amazon.com/AmazonS3/latest/dev/HowDoIWebsiteConfiguration.html?shortFooter=true)).\n\n\nThere are a million ways to do it. We’ll use the\n\n[awscli](http://docs.aws.amazon.com/cli/latest/reference/s3/cp.html#examples)\nlibrary,\n\nprovided by Amazon.\n\n\nThe full command looks like this:\n\n\n```shell\n\naws s3 cp ./ s3://yourbucket/ --recursive --exclude \"*\" --include \"*.html\"\n\n```\n\n\n![Manual\ndeployment](https://about.gitlab.com/images/blogimages/ci-deployment-and-environments/13.jpg){:\n.center}\n\nPushing code to repository and deploying are separate processes.\n\n{: .note .text-center}\n\n\nImportant detail: The command\n\n[expects\nyou](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#config-settings-and-precedence)\n\nto provide `AWS_ACCESS_KEY_ID` and  `AWS_SECRET_ACCESS_KEY` environment\n\nvariables. Also you might need to specify `AWS_DEFAULT_REGION`.\n\n{: .alert .alert-info}\n\n\nLet’s try to automate it using [GitLab\nCI](/solutions/continuous-integration/).\n\n\n## The first automated deployment\n\n\nWith GitLab, there's no difference on what commands to run.\n\nYou can set up GitLab CI in a way that tailors to your specific needs, as if\nit was your local terminal on your computer. As long as you execute commands\nthere, you can tell CI to do the same for you in GitLab.\n\nPut your script to `.gitlab-ci.yml` and push your code – that’s it: CI\ntriggers\n\na _job_ and your commands are executed.\n\n\nNow, let's add some context to our story: Our website is small, there is\n20-30 daily\n\nvisitors and the code repository has only one default branch: `main`.\n\n\nLet's start by specifying a _job_ with the command from above in the\n`.gitlab-ci.yml` file:\n\n\n```yaml\n\ndeploy:\n  script: aws s3 cp ./ s3://yourbucket/ --recursive --exclude \"*\" --include \"*.html\"\n```\n\n\nNo luck:\n\n![Failed\ncommand](https://about.gitlab.com/images/blogimages/ci-deployment-and-environments/fail1.png){:\n.shadow}\n\n\nIt is our _job_ to ensure that there is an `aws` executable.\n\nTo install `awscli` we need `pip`, which is a tool for Python packages\ninstallation.\n\nLet's specify Docker image with preinstalled Python, which should contain\n`pip` as well:\n\n\n```yaml\n\ndeploy:\n  image: python:latest\n  script:\n  - pip install awscli\n  - aws s3 cp ./ s3://yourbucket/ --recursive --exclude \"*\" --include \"*.html\"\n```\n\n\n![Automated\ndeployment](https://about.gitlab.com/images/blogimages/ci-deployment-and-environments/14.jpg){:\n.center}\n\nYou push your code to GitLab, and it is automatically deployed by CI.\n  {: .note .text-center}\n\nThe installation of `awscli` extends the job execution time, but that is not\na big\n\ndeal for now. If you need to speed up the process, you can always [look for\n\na Docker image](https://hub.docker.com/explore/) with preinstalled `awscli`,\n\nor create an image by yourself.\n\n{: .alert .alert-warning}\n\n\nAlso, let’s not forget about these environment variables, which you've just\ngrabbed\n\nfrom [AWS Console](https://console.aws.amazon.com/):\n\n\n```yaml\n\nvariables:\n  AWS_ACCESS_KEY_ID: \"AKIAIOSFODNN7EXAMPLE\"\n  AWS_SECRET_ACCESS_KEY: \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\ndeploy:\n  image: python:latest\n  script:\n  - pip install awscli\n  - aws s3 cp ./ s3://yourbucket/ --recursive --exclude \"*\" --include \"*.html\"\n```\n\nIt should work, but keeping secret keys open, even in a private repository,\n\nis not a good idea. Let's see how to deal with this situation.\n\n\n### Keeping secret things secret\n\n\nGitLab has a special place for secret variables: **Settings > CI/CD >\nVariables**\n\n\n![Picture of Variables\npage](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674076/Blog/Content%20Images/add-variable-updated.png)\n\n\nWhatever you put there will be turned into **environment variables**.\n\nChecking the \"Mask variable\" checkbox will obfuscate the variable in job\nlogs. Also, checking the \"Protect variable\" checkbox will export the\nvariable to only pipelines running on protected branches and tags. Users\nwith Owner or Maintainer permissions to a project will have access to this\nsection.\n\n\nWe could remove `variables` section from our CI configuration. However,\nlet’s use it for another purpose.\n\n\n### How to specify and use variables that are not secret\n\n\nWhen your configuration gets bigger, it is convenient to keep some of the\n\nparameters as variables at the beginning of your configuration. Especially\nif you\n\nuse them in more than one place. Although it is not the case in our\nsituation yet,\n\nlet's set the S3 bucket name as a\n[**variable**](https://docs.gitlab.com/ee/ci/variables/) for the purpose of\nthis demonstration:\n\n\n```yaml\n\nvariables:\n  S3_BUCKET_NAME: \"yourbucket\"\ndeploy:\n  image: python:latest\n  script:\n  - pip install awscli\n  - aws s3 cp ./ s3://$S3_BUCKET_NAME/ --recursive --exclude \"*\" --include \"*.html\"\n```\n\n\nSo far so good:\n\n\n![Successful\nbuild](https://about.gitlab.com/images/blogimages/ci-deployment-and-environments/build.png){:\n.shadow.medium.center}\n\n\nIn our hypothetical scenario, the audience of your website has grown, so\nyou've hired a developer to help you.\n\nNow you have a team. Let's see how teamwork changes the GitLab CI workflow.\n\n\n## How to use GitLab CI with a team\n\n\nNow, that there are two users working in the same repository, it is no\nlonger convenient\n\nto use the `main` branch for development. You decide to use separate\nbranches\n\nfor both new features and new articles and merge them into `main` when they\nare ready.\n\n\nThe problem is that your current CI config doesn’t care about branches at\nall.\n\nWhenever you push anything to GitLab, it will be deployed to S3.\n\n\nPreventing this problem is straightforward. Just add `only: main` to your\n`deploy` job.\n\n\n![Automated deployment of main\nbranch](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674076/Blog/Content%20Images/15-updated.png){:\n.center}\n\nYou don't want to deploy every branch to the production website but it would\nalso be nice to preview your changes from feature-branches somehow.\n\n{: .note .text-center}\n\n\n### How to set up a separate place for testing code\n\n\nThe person you recently hired, let's call him Patrick, reminds you that\nthere is a featured called\n\n[GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/). It looks like a perfect\ncandidate for\n\na place to preview your work in progress.\n\n\nTo [host websites on GitLab Pages](/blog/gitlab-pages-setup/) your CI\nconfiguration file should satisfy three simple rules:\n\n\n- The _job_ should be named `pages`\n\n- There should be an `artifacts` section with folder `public` in it\n\n- Everything you want to host should be in this `public` folder\n\n\nThe contents of the public folder will be hosted at\n`http://\u003Cusername>.gitlab.io/\u003Cprojectname>/`\n\n{: .alert .alert-info}\n\n\nAfter applying the [example config for plain-html\nwebsites](https://gitlab.com/pages/plain-html/blob/master/.gitlab-ci.yml),\n\nthe full CI configuration looks like this:\n\n\n```yaml\n\nvariables:\n  S3_BUCKET_NAME: \"yourbucket\"\n\ndeploy:\n  image: python:latest\n  script:\n  - pip install awscli\n  - aws s3 cp ./ s3://$S3_BUCKET_NAME/ --recursive --exclude \"*\" --include \"*.html\"\n  only:\n  - main\n\npages:\n  image: alpine:latest\n  script:\n  - mkdir -p ./public\n  - cp ./*.html ./public/\n  artifacts:\n    paths:\n    - public\n  except:\n  - main\n```\n\n\nWe specified two jobs. One job deploys the website for your customers to S3\n(`deploy`).\n\nThe other one (`pages`) deploys the website to GitLab Pages.\n\nWe can name them \"Production environment\" and \"Staging environment\",\nrespectively.\n\n\n![Deployment to two\nplaces](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674076/Blog/Content%20Images/16-updated.png){:\n.center}\n\nAll branches, except main, will be deployed to GitLab Pages.\n\n{: .note .text-center}\n\n\n## Introducing environments\n\n\nGitLab offers\n [support for environments](https://docs.gitlab.com/ee/ci/environments/) (including dynamic environments and static environments),\n and all you need to do it to specify the corresponding environment for each deployment *job*:\n\n```yaml\n\nvariables:\n  S3_BUCKET_NAME: \"yourbucket\"\n\ndeploy to production:\n  environment: production\n  image: python:latest\n  script:\n  - pip install awscli\n  - aws s3 cp ./ s3://$S3_BUCKET_NAME/ --recursive --exclude \"*\" --include \"*.html\"\n  only:\n  - main\n\npages:\n  image: alpine:latest\n  environment: staging\n  script:\n  - mkdir -p ./public\n  - cp ./*.html ./public/\n  artifacts:\n    paths:\n    - public\n  except:\n  - main\n```\n\n\nGitLab keeps track of your deployments, so you always know what is currently\nbeing deployed on your servers:\n\n\n![List of\nenvironments](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674076/Blog/Content%20Images/envs-updated.png){:\n.shadow.center}\n\n\nGitLab provides full history of your deployments for each of your current\nenvironments:\n\n\n![List of deployments to staging\nenvironment](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674077/Blog/Content%20Images/staging-env-detail-updated.png){:\n.shadow.center}\n\n\n![Environments](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674077/Blog/Content%20Images/17-updated.png){:\n.center}\n\n\nNow, with everything automated and set up, we’re ready for the new\nchallenges that are just around the corner.\n\n\n## How to troubleshoot deployments\n\n\nIt has just happened again.\n\nYou've pushed your feature-branch to preview it on staging and a minute\nlater Patrick pushed\n\nhis branch, so the staging environment was rewritten with his work. Aargh!!\nIt was the third time today!\n\n\nIdea! \u003Ci class=\"far fa-lightbulb\" style=\"color:#FFD900; font-size:.85em\"\naria-hidden=\"true\">\u003C/i> Let's use Slack to notify us of deployments, so that\npeople will not push their stuff if another one has been just deployed!\n\n\n> Learn how to [integrate GitLab with\nSlack](https://docs.gitlab.com/ee/user/project/integrations/gitlab_slack_application.html).\n\n\n## Teamwork at scale\n\n\nAs the time passed, your website became really popular, and your team has\ngrown from two people to eight people.\n\nPeople develop in parallel, so the situation when people wait for each other\nto\n\npreview something on Staging has become pretty common. \"Deploy every branch\nto staging\" stopped working.\n\n\n![Queue of branches for review on\nStaging](https://about.gitlab.com/images/blogimages/ci-deployment-and-environments/queue.jpg){:\n.center}\n\n\nIt's time to modify the process one more time. You and your team agreed that\nif\n\nsomeone wants to see their changes on the staging\n\nserver, they should first merge the changes to the \"staging\" branch.\n\n\nThe change of `.gitlab-ci.yml` is minimal:\n\n\n```yaml\n\nexcept:\n\n- main\n\n```\n\n\nis now changed to\n\n\n```yaml\n\nonly:\n\n- staging\n\n```\n\n\n![Staging\nbranch](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674077/Blog/Content%20Images/18-updated.png){:\n.center}\n\nPeople have to merge their feature branches before preview on the staging\nserver.\n\n{: .note .text-center}\n\n\nOf course, it requires additional time and effort for merging, but everybody\nagreed that it is better than waiting.\n\n\n### How to handle emergencies\n\n\nYou can't control everything, so sometimes things go wrong. Someone merged\nbranches incorrectly and\n\npushed the result straight to production exactly when your site was on top\nof HackerNews.\n\nThousands of people saw your completely broken layout instead of your shiny\nmain page.\n\n\nLuckily, someone found the **Rollback** button, so the\n\nwebsite was fixed a minute after the problem was discovered.\n\n\n![List of\nenvironments](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674077/Blog/Content%20Images/prod-env-rollback-arrow-updated.png){:\n.shadow.center}\n\nRollback relaunches the previous job with the previous commit\n\n{: .note .text-center}\n\n\nAnyway, you felt that you needed to react to the problem and decided to turn\noff\n\nauto-deployment to Production and switch to manual deployment.\n\nTo do that, you needed to add `when: manual` to your _job_.\n\n\nAs you expected, there will be no automatic deployment to Production after\nthat.\n\nTo deploy manually go to **CI/CD > Pipelines**, and click the button:\n\n\n![Skipped job is available for manual\nlaunch](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674076/Blog/Content%20Images/manual-pipeline-arrow-updated.png){:\n.shadow.center}\n\n\nFast forward in time. Finally, your company has turned into a corporation.\nNow, you have hundreds of people working on the website,\n\nso all the previous compromises no longer work.\n\n\n### Time to start using Review Apps\n\n\nThe next logical step is to boot up a temporary instance of the application\nper feature branch for review.\n\n\nIn our case, we set up another bucket on S3 for that. The only difference is\nthat\n\nwe copy the contents of our website to a \"folder\" with the name of the\n\nthe development branch, so that the URL looks like this:\n\n\n`http://\u003CREVIEW_S3_BUCKET_NAME>.s3-website-us-east-1.amazonaws.com/\u003Cbranchname>/`\n\n\nHere's the replacement for the `pages` _job_ we used before:\n\n\n```yaml\n\nreview apps:\n  variables:\n    S3_BUCKET_NAME: \"reviewbucket\"\n  image: python:latest\n  environment: review\n  script:\n  - pip install awscli\n  - mkdir -p ./$CI_BUILD_REF_NAME\n  - cp ./*.html ./$CI_BUILD_REF_NAME/\n  - aws s3 cp ./ s3://$S3_BUCKET_NAME/ --recursive --exclude \"*\" --include \"*.html\"\n```\n\n\nThe interesting thing is where we got this `$CI_BUILD_REF_NAME` variable\nfrom.\n\nGitLab predefines [many environment\nvariables](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)\nso that you can use them in your jobs.\n\n\nNote that we defined the `S3_BUCKET_NAME` variable inside the *job*. You can\ndo this to rewrite top-level definitions.\n\n{: .alert .alert-info}\n\n\nVisual representation of this configuration:\n\n![Review apps]![How to use GitLab CI - update - 19 -\nupdated](https://res.cloudinary.com/about-gitlab-com/image/upload/v1749674077/Blog/Content%20Images/19-updated.png){:\n.illustration}\n\n\nThe details of the Review Apps implementation varies widely, depending upon\nyour real technology\n\nstack and on your deployment process, which is outside the scope of this\nblog post.\n\n\nIt will not be that straightforward, as it is with our static HTML website.\n\nFor example, you had to make these instances temporary, and booting up these\ninstances\n\nwith all required software and services automatically on the fly is not a\ntrivial task.\n\nHowever, it is doable, especially if you use Docker containers, or at least\nChef or Ansible.\n\n\nWe'll cover deployment with Docker in a future blog post.\n\nI feel a bit guilty for simplifying the deployment process to a simple HTML\nfiles copying, and not\n\nadding some hardcore scenarios. If you need some right now, I recommend you\nread the article [\"Building an Elixir Release into a Docker image using\nGitLab\nCI.\"](/blog/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/)\n\n\nFor now, let's talk about one final thing.\n\n\n### Deploying to different platforms\n\n\nIn real life, we are not limited to S3 and GitLab Pages. We host, and\ntherefore,\n\ndeploy our apps and packages to various services.\n\n\nMoreover, at some point, you could decide to move to a new platform and will\nneed to rewrite all your deployment scripts.\n\nYou can use a gem called `dpl` to minimize the damage.\n\n\nIn the examples above we used `awscli` as a tool to deliver code to an\nexample\n\nservice (Amazon S3).\n\nHowever, no matter what tool and what destination system you use, the\nprinciple is the same:\n\nYou run a command with some parameters and somehow pass a secret key for\nauthentication purposes.\n\n\nThe `dpl` deployment tool utilizes this principle and provides a\n\nunified interface for [this list of\nproviders](https://github.com/travis-ci/dpl#supported-providers).\n\n\nHere's how a production deployment _job_ would look if we use `dpl`:\n\n\n```yaml\n\nvariables:\n  S3_BUCKET_NAME: \"yourbucket\"\n\ndeploy to production:\n  environment: production\n  image: ruby:latest\n  script:\n  - gem install dpl\n  - dpl --provider=s3 --bucket=$S3_BUCKET_NAME\n  only:\n  - main\n```\n\n\nIf you deploy to different systems or change destination platform\nfrequently, consider\n\nusing `dpl` to make your deployment scripts look uniform.\n\n\n## Five key takeaways\n\n\n1. Deployment is just a command (or a set of commands) that is regularly\nexecuted. Therefore it can run inside GitLab CI.\n\n2. Most times you'll need to provide some secret key(s) to the command you\nexecute. Store these secret keys in **Settings > CI/CD > Variables**.\n\n3. With GitLab CI, you can flexibly specify which branches to deploy to.\n\n4. If you deploy to multiple environments, GitLab will conserve the history\nof deployments,\n\nwhich allows you to rollback to any previous version.\n\n5. For critical parts of your infrastructure, you can enable manual\ndeployment from GitLab interface, instead of automated deployment.\n\n\n\u003Cstyle>\n\nimg.illustration {\n  padding-left: 12%;\n  padding-right: 12%;\n\n}\n\n@media (max-width: 760px) {\n  img.illustration {\n    padding-left: 0px;\n    padding-right: 0px;\n  }\n}\n\n\u003C/style>\n","engineering",[24,25,26],"CI","CD","tutorial","2024-07-22",{"slug":29,"featured":6,"template":30},"ci-deployment-and-environments","BlogPost","content:en-us:blog:ci-deployment-and-environments.yml","yaml","Ci Deployment And Environments","content","en-us/blog/ci-deployment-and-environments.yml","en-us/blog/ci-deployment-and-environments","yml",{"_path":39,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"data":41,"_id":463,"_type":32,"title":464,"_source":34,"_file":465,"_stem":466,"_extension":37},"/shared/en-us/main-navigation","en-us",{"logo":42,"freeTrial":47,"sales":52,"login":57,"items":62,"search":394,"minimal":425,"duo":444,"pricingDeployment":453},{"config":43},{"href":44,"dataGaName":45,"dataGaLocation":46},"/","gitlab logo","header",{"text":48,"config":49},"Get free trial",{"href":50,"dataGaName":51,"dataGaLocation":46},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":53,"config":54},"Talk to sales",{"href":55,"dataGaName":56,"dataGaLocation":46},"/sales/","sales",{"text":58,"config":59},"Sign in",{"href":60,"dataGaName":61,"dataGaLocation":46},"https://gitlab.com/users/sign_in/","sign in",[63,107,205,210,315,375],{"text":64,"config":65,"cards":67,"footer":90},"Platform",{"dataNavLevelOne":66},"platform",[68,74,82],{"title":64,"description":69,"link":70},"The most comprehensive AI-powered DevSecOps Platform",{"text":71,"config":72},"Explore our Platform",{"href":73,"dataGaName":66,"dataGaLocation":46},"/platform/",{"title":75,"description":76,"link":77},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":78,"config":79},"Meet GitLab Duo",{"href":80,"dataGaName":81,"dataGaLocation":46},"/gitlab-duo/","gitlab duo ai",{"title":83,"description":84,"link":85},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":86,"config":87},"Learn more",{"href":88,"dataGaName":89,"dataGaLocation":46},"/why-gitlab/","why gitlab",{"title":91,"items":92},"Get started with",[93,98,103],{"text":94,"config":95},"Platform Engineering",{"href":96,"dataGaName":97,"dataGaLocation":46},"/solutions/platform-engineering/","platform engineering",{"text":99,"config":100},"Developer Experience",{"href":101,"dataGaName":102,"dataGaLocation":46},"/developer-experience/","Developer experience",{"text":104,"config":105},"MLOps",{"href":106,"dataGaName":104,"dataGaLocation":46},"/topics/devops/the-role-of-ai-in-devops/",{"text":108,"left":109,"config":110,"link":112,"lists":116,"footer":187},"Product",true,{"dataNavLevelOne":111},"solutions",{"text":113,"config":114},"View all Solutions",{"href":115,"dataGaName":111,"dataGaLocation":46},"/solutions/",[117,142,166],{"title":118,"description":119,"link":120,"items":125},"Automation","CI/CD and automation to accelerate deployment",{"config":121},{"icon":122,"href":123,"dataGaName":124,"dataGaLocation":46},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[126,130,134,138],{"text":127,"config":128},"CI/CD",{"href":129,"dataGaLocation":46,"dataGaName":127},"/solutions/continuous-integration/",{"text":131,"config":132},"AI-Assisted Development",{"href":80,"dataGaLocation":46,"dataGaName":133},"AI assisted development",{"text":135,"config":136},"Source Code Management",{"href":137,"dataGaLocation":46,"dataGaName":135},"/solutions/source-code-management/",{"text":139,"config":140},"Automated Software Delivery",{"href":123,"dataGaLocation":46,"dataGaName":141},"Automated software delivery",{"title":143,"description":144,"link":145,"items":150},"Security","Deliver code faster without compromising security",{"config":146},{"href":147,"dataGaName":148,"dataGaLocation":46,"icon":149},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[151,156,161],{"text":152,"config":153},"Application Security Testing",{"href":154,"dataGaName":155,"dataGaLocation":46},"/solutions/application-security-testing/","Application security testing",{"text":157,"config":158},"Software Supply Chain Security",{"href":159,"dataGaLocation":46,"dataGaName":160},"/solutions/supply-chain/","Software supply chain security",{"text":162,"config":163},"Software Compliance",{"href":164,"dataGaName":165,"dataGaLocation":46},"/solutions/software-compliance/","software compliance",{"title":167,"link":168,"items":173},"Measurement",{"config":169},{"icon":170,"href":171,"dataGaName":172,"dataGaLocation":46},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[174,178,182],{"text":175,"config":176},"Visibility & Measurement",{"href":171,"dataGaLocation":46,"dataGaName":177},"Visibility and Measurement",{"text":179,"config":180},"Value Stream Management",{"href":181,"dataGaLocation":46,"dataGaName":179},"/solutions/value-stream-management/",{"text":183,"config":184},"Analytics & Insights",{"href":185,"dataGaLocation":46,"dataGaName":186},"/solutions/analytics-and-insights/","Analytics and insights",{"title":188,"items":189},"GitLab for",[190,195,200],{"text":191,"config":192},"Enterprise",{"href":193,"dataGaLocation":46,"dataGaName":194},"/enterprise/","enterprise",{"text":196,"config":197},"Small Business",{"href":198,"dataGaLocation":46,"dataGaName":199},"/small-business/","small business",{"text":201,"config":202},"Public Sector",{"href":203,"dataGaLocation":46,"dataGaName":204},"/solutions/public-sector/","public sector",{"text":206,"config":207},"Pricing",{"href":208,"dataGaName":209,"dataGaLocation":46,"dataNavLevelOne":209},"/pricing/","pricing",{"text":211,"config":212,"link":214,"lists":218,"feature":302},"Resources",{"dataNavLevelOne":213},"resources",{"text":215,"config":216},"View all resources",{"href":217,"dataGaName":213,"dataGaLocation":46},"/resources/",[219,252,274],{"title":220,"items":221},"Getting started",[222,227,232,237,242,247],{"text":223,"config":224},"Install",{"href":225,"dataGaName":226,"dataGaLocation":46},"/install/","install",{"text":228,"config":229},"Quick start guides",{"href":230,"dataGaName":231,"dataGaLocation":46},"/get-started/","quick setup checklists",{"text":233,"config":234},"Learn",{"href":235,"dataGaLocation":46,"dataGaName":236},"https://university.gitlab.com/","learn",{"text":238,"config":239},"Product documentation",{"href":240,"dataGaName":241,"dataGaLocation":46},"https://docs.gitlab.com/","product documentation",{"text":243,"config":244},"Best practice videos",{"href":245,"dataGaName":246,"dataGaLocation":46},"/getting-started-videos/","best practice videos",{"text":248,"config":249},"Integrations",{"href":250,"dataGaName":251,"dataGaLocation":46},"/integrations/","integrations",{"title":253,"items":254},"Discover",[255,260,264,269],{"text":256,"config":257},"Customer success stories",{"href":258,"dataGaName":259,"dataGaLocation":46},"/customers/","customer success stories",{"text":261,"config":262},"Blog",{"href":263,"dataGaName":5,"dataGaLocation":46},"/blog/",{"text":265,"config":266},"Remote",{"href":267,"dataGaName":268,"dataGaLocation":46},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":270,"config":271},"TeamOps",{"href":272,"dataGaName":273,"dataGaLocation":46},"/teamops/","teamops",{"title":275,"items":276},"Connect",[277,282,287,292,297],{"text":278,"config":279},"GitLab Services",{"href":280,"dataGaName":281,"dataGaLocation":46},"/services/","services",{"text":283,"config":284},"Community",{"href":285,"dataGaName":286,"dataGaLocation":46},"/community/","community",{"text":288,"config":289},"Forum",{"href":290,"dataGaName":291,"dataGaLocation":46},"https://forum.gitlab.com/","forum",{"text":293,"config":294},"Events",{"href":295,"dataGaName":296,"dataGaLocation":46},"/events/","events",{"text":298,"config":299},"Partners",{"href":300,"dataGaName":301,"dataGaLocation":46},"/partners/","partners",{"backgroundColor":303,"textColor":304,"text":305,"image":306,"link":310},"#2f2a6b","#fff","Insights for the future of software development",{"altText":307,"config":308},"the source promo card",{"src":309},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":311,"config":312},"Read the latest",{"href":313,"dataGaName":314,"dataGaLocation":46},"/the-source/","the source",{"text":316,"config":317,"lists":319},"Company",{"dataNavLevelOne":318},"company",[320],{"items":321},[322,327,333,335,340,345,350,355,360,365,370],{"text":323,"config":324},"About",{"href":325,"dataGaName":326,"dataGaLocation":46},"/company/","about",{"text":328,"config":329,"footerGa":332},"Jobs",{"href":330,"dataGaName":331,"dataGaLocation":46},"/jobs/","jobs",{"dataGaName":331},{"text":293,"config":334},{"href":295,"dataGaName":296,"dataGaLocation":46},{"text":336,"config":337},"Leadership",{"href":338,"dataGaName":339,"dataGaLocation":46},"/company/team/e-group/","leadership",{"text":341,"config":342},"Team",{"href":343,"dataGaName":344,"dataGaLocation":46},"/company/team/","team",{"text":346,"config":347},"Handbook",{"href":348,"dataGaName":349,"dataGaLocation":46},"https://handbook.gitlab.com/","handbook",{"text":351,"config":352},"Investor relations",{"href":353,"dataGaName":354,"dataGaLocation":46},"https://ir.gitlab.com/","investor relations",{"text":356,"config":357},"Trust Center",{"href":358,"dataGaName":359,"dataGaLocation":46},"/security/","trust center",{"text":361,"config":362},"AI Transparency Center",{"href":363,"dataGaName":364,"dataGaLocation":46},"/ai-transparency-center/","ai transparency center",{"text":366,"config":367},"Newsletter",{"href":368,"dataGaName":369,"dataGaLocation":46},"/company/contact/","newsletter",{"text":371,"config":372},"Press",{"href":373,"dataGaName":374,"dataGaLocation":46},"/press/","press",{"text":376,"config":377,"lists":378},"Contact us",{"dataNavLevelOne":318},[379],{"items":380},[381,384,389],{"text":53,"config":382},{"href":55,"dataGaName":383,"dataGaLocation":46},"talk to sales",{"text":385,"config":386},"Get help",{"href":387,"dataGaName":388,"dataGaLocation":46},"/support/","get help",{"text":390,"config":391},"Customer portal",{"href":392,"dataGaName":393,"dataGaLocation":46},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":395,"login":396,"suggestions":403},"Close",{"text":397,"link":398},"To search repositories and projects, login to",{"text":399,"config":400},"gitlab.com",{"href":60,"dataGaName":401,"dataGaLocation":402},"search login","search",{"text":404,"default":405},"Suggestions",[406,408,412,414,418,422],{"text":75,"config":407},{"href":80,"dataGaName":75,"dataGaLocation":402},{"text":409,"config":410},"Code Suggestions (AI)",{"href":411,"dataGaName":409,"dataGaLocation":402},"/solutions/code-suggestions/",{"text":127,"config":413},{"href":129,"dataGaName":127,"dataGaLocation":402},{"text":415,"config":416},"GitLab on AWS",{"href":417,"dataGaName":415,"dataGaLocation":402},"/partners/technology-partners/aws/",{"text":419,"config":420},"GitLab on Google Cloud",{"href":421,"dataGaName":419,"dataGaLocation":402},"/partners/technology-partners/google-cloud-platform/",{"text":423,"config":424},"Why GitLab?",{"href":88,"dataGaName":423,"dataGaLocation":402},{"freeTrial":426,"mobileIcon":431,"desktopIcon":436,"secondaryButton":439},{"text":427,"config":428},"Start free trial",{"href":429,"dataGaName":51,"dataGaLocation":430},"https://gitlab.com/-/trials/new/","nav",{"altText":432,"config":433},"Gitlab Icon",{"src":434,"dataGaName":435,"dataGaLocation":430},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":432,"config":437},{"src":438,"dataGaName":435,"dataGaLocation":430},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":440,"config":441},"Get Started",{"href":442,"dataGaName":443,"dataGaLocation":430},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":445,"mobileIcon":449,"desktopIcon":451},{"text":446,"config":447},"Learn more about GitLab Duo",{"href":80,"dataGaName":448,"dataGaLocation":430},"gitlab duo",{"altText":432,"config":450},{"src":434,"dataGaName":435,"dataGaLocation":430},{"altText":432,"config":452},{"src":438,"dataGaName":435,"dataGaLocation":430},{"freeTrial":454,"mobileIcon":459,"desktopIcon":461},{"text":455,"config":456},"Back to pricing",{"href":208,"dataGaName":457,"dataGaLocation":430,"icon":458},"back to pricing","GoBack",{"altText":432,"config":460},{"src":434,"dataGaName":435,"dataGaLocation":430},{"altText":432,"config":462},{"src":438,"dataGaName":435,"dataGaLocation":430},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":468,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"title":469,"button":470,"image":475,"config":479,"_id":481,"_type":32,"_source":34,"_file":482,"_stem":483,"_extension":37},"/shared/en-us/banner","is now in public beta!",{"text":471,"config":472},"Try the Beta",{"href":473,"dataGaName":474,"dataGaLocation":46},"/gitlab-duo/agent-platform/","duo banner",{"altText":476,"config":477},"GitLab Duo Agent Platform",{"src":478},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":480},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":485,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"data":486,"_id":690,"_type":32,"title":691,"_source":34,"_file":692,"_stem":693,"_extension":37},"/shared/en-us/main-footer",{"text":487,"source":488,"edit":494,"contribute":499,"config":504,"items":509,"minimal":682},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":489,"config":490},"View page source",{"href":491,"dataGaName":492,"dataGaLocation":493},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":495,"config":496},"Edit this page",{"href":497,"dataGaName":498,"dataGaLocation":493},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":500,"config":501},"Please contribute",{"href":502,"dataGaName":503,"dataGaLocation":493},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":505,"facebook":506,"youtube":507,"linkedin":508},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[510,533,589,618,652],{"title":64,"links":511,"subMenu":516},[512],{"text":513,"config":514},"DevSecOps platform",{"href":73,"dataGaName":515,"dataGaLocation":493},"devsecops platform",[517],{"title":206,"links":518},[519,523,528],{"text":520,"config":521},"View plans",{"href":208,"dataGaName":522,"dataGaLocation":493},"view plans",{"text":524,"config":525},"Why Premium?",{"href":526,"dataGaName":527,"dataGaLocation":493},"/pricing/premium/","why premium",{"text":529,"config":530},"Why Ultimate?",{"href":531,"dataGaName":532,"dataGaLocation":493},"/pricing/ultimate/","why ultimate",{"title":534,"links":535},"Solutions",[536,541,543,545,550,555,559,562,566,571,573,576,579,584],{"text":537,"config":538},"Digital transformation",{"href":539,"dataGaName":540,"dataGaLocation":493},"/topics/digital-transformation/","digital transformation",{"text":152,"config":542},{"href":154,"dataGaName":152,"dataGaLocation":493},{"text":141,"config":544},{"href":123,"dataGaName":124,"dataGaLocation":493},{"text":546,"config":547},"Agile development",{"href":548,"dataGaName":549,"dataGaLocation":493},"/solutions/agile-delivery/","agile delivery",{"text":551,"config":552},"Cloud transformation",{"href":553,"dataGaName":554,"dataGaLocation":493},"/topics/cloud-native/","cloud transformation",{"text":556,"config":557},"SCM",{"href":137,"dataGaName":558,"dataGaLocation":493},"source code management",{"text":127,"config":560},{"href":129,"dataGaName":561,"dataGaLocation":493},"continuous integration & delivery",{"text":563,"config":564},"Value stream management",{"href":181,"dataGaName":565,"dataGaLocation":493},"value stream management",{"text":567,"config":568},"GitOps",{"href":569,"dataGaName":570,"dataGaLocation":493},"/solutions/gitops/","gitops",{"text":191,"config":572},{"href":193,"dataGaName":194,"dataGaLocation":493},{"text":574,"config":575},"Small business",{"href":198,"dataGaName":199,"dataGaLocation":493},{"text":577,"config":578},"Public sector",{"href":203,"dataGaName":204,"dataGaLocation":493},{"text":580,"config":581},"Education",{"href":582,"dataGaName":583,"dataGaLocation":493},"/solutions/education/","education",{"text":585,"config":586},"Financial services",{"href":587,"dataGaName":588,"dataGaLocation":493},"/solutions/finance/","financial services",{"title":211,"links":590},[591,593,595,597,600,602,604,606,608,610,612,614,616],{"text":223,"config":592},{"href":225,"dataGaName":226,"dataGaLocation":493},{"text":228,"config":594},{"href":230,"dataGaName":231,"dataGaLocation":493},{"text":233,"config":596},{"href":235,"dataGaName":236,"dataGaLocation":493},{"text":238,"config":598},{"href":240,"dataGaName":599,"dataGaLocation":493},"docs",{"text":261,"config":601},{"href":263,"dataGaName":5,"dataGaLocation":493},{"text":256,"config":603},{"href":258,"dataGaName":259,"dataGaLocation":493},{"text":265,"config":605},{"href":267,"dataGaName":268,"dataGaLocation":493},{"text":278,"config":607},{"href":280,"dataGaName":281,"dataGaLocation":493},{"text":270,"config":609},{"href":272,"dataGaName":273,"dataGaLocation":493},{"text":283,"config":611},{"href":285,"dataGaName":286,"dataGaLocation":493},{"text":288,"config":613},{"href":290,"dataGaName":291,"dataGaLocation":493},{"text":293,"config":615},{"href":295,"dataGaName":296,"dataGaLocation":493},{"text":298,"config":617},{"href":300,"dataGaName":301,"dataGaLocation":493},{"title":316,"links":619},[620,622,624,626,628,630,632,636,641,643,645,647],{"text":323,"config":621},{"href":325,"dataGaName":318,"dataGaLocation":493},{"text":328,"config":623},{"href":330,"dataGaName":331,"dataGaLocation":493},{"text":336,"config":625},{"href":338,"dataGaName":339,"dataGaLocation":493},{"text":341,"config":627},{"href":343,"dataGaName":344,"dataGaLocation":493},{"text":346,"config":629},{"href":348,"dataGaName":349,"dataGaLocation":493},{"text":351,"config":631},{"href":353,"dataGaName":354,"dataGaLocation":493},{"text":633,"config":634},"Sustainability",{"href":635,"dataGaName":633,"dataGaLocation":493},"/sustainability/",{"text":637,"config":638},"Diversity, inclusion and belonging (DIB)",{"href":639,"dataGaName":640,"dataGaLocation":493},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":356,"config":642},{"href":358,"dataGaName":359,"dataGaLocation":493},{"text":366,"config":644},{"href":368,"dataGaName":369,"dataGaLocation":493},{"text":371,"config":646},{"href":373,"dataGaName":374,"dataGaLocation":493},{"text":648,"config":649},"Modern Slavery Transparency Statement",{"href":650,"dataGaName":651,"dataGaLocation":493},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":653,"links":654},"Contact Us",[655,658,660,662,667,672,677],{"text":656,"config":657},"Contact an expert",{"href":55,"dataGaName":56,"dataGaLocation":493},{"text":385,"config":659},{"href":387,"dataGaName":388,"dataGaLocation":493},{"text":390,"config":661},{"href":392,"dataGaName":393,"dataGaLocation":493},{"text":663,"config":664},"Status",{"href":665,"dataGaName":666,"dataGaLocation":493},"https://status.gitlab.com/","status",{"text":668,"config":669},"Terms of use",{"href":670,"dataGaName":671,"dataGaLocation":493},"/terms/","terms of use",{"text":673,"config":674},"Privacy statement",{"href":675,"dataGaName":676,"dataGaLocation":493},"/privacy/","privacy statement",{"text":678,"config":679},"Cookie preferences",{"dataGaName":680,"dataGaLocation":493,"id":681,"isOneTrustButton":109},"cookie preferences","ot-sdk-btn",{"items":683},[684,686,688],{"text":668,"config":685},{"href":670,"dataGaName":671,"dataGaLocation":493},{"text":673,"config":687},{"href":675,"dataGaName":676,"dataGaLocation":493},{"text":678,"config":689},{"dataGaName":680,"dataGaLocation":493,"id":681,"isOneTrustButton":109},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[695,707],{"_path":696,"_dir":697,"_draft":6,"_partial":6,"_locale":7,"content":698,"config":702,"_id":704,"_type":32,"title":18,"_source":34,"_file":705,"_stem":706,"_extension":37},"/en-us/blog/authors/ivan-nemytchenko","authors",{"name":18,"config":699},{"headshot":700,"ctfId":701},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","Ivan-Nemytchenko",{"template":703},"BlogAuthor","content:en-us:blog:authors:ivan-nemytchenko.yml","en-us/blog/authors/ivan-nemytchenko.yml","en-us/blog/authors/ivan-nemytchenko",{"_path":708,"_dir":697,"_draft":6,"_partial":6,"_locale":7,"content":709,"config":713,"_id":714,"_type":32,"title":19,"_source":34,"_file":715,"_stem":716,"_extension":37},"/en-us/blog/authors/cesar-saavedra",{"name":19,"config":710},{"headshot":711,"ctfId":712},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659600/Blog/Author%20Headshots/csaavedra1-headshot.jpg","csaavedra1",{"template":703},"content:en-us:blog:authors:cesar-saavedra.yml","en-us/blog/authors/cesar-saavedra.yml","en-us/blog/authors/cesar-saavedra",{"_path":718,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"header":719,"eyebrow":720,"blurb":721,"button":722,"secondaryButton":726,"_id":728,"_type":32,"title":729,"_source":34,"_file":730,"_stem":731,"_extension":37},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":48,"config":723},{"href":724,"dataGaName":51,"dataGaLocation":725},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":53,"config":727},{"href":55,"dataGaName":56,"dataGaLocation":725},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758662313091]