[{"data":1,"prerenderedAt":759},["ShallowReactive",2],{"/en-us/blog/gitops-with-gitlab-connecting-the-cluster":3,"navigation-en-us":36,"banner-en-us":463,"footer-en-us":480,"Viktor Nagy":725,"next-steps-en-us":738,"footer-source-/en-us/blog/gitops-with-gitlab-connecting-the-cluster/":753},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":26,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},"/en-us/blog/gitops-with-gitlab-connecting-the-cluster","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"GitOps with GitLab: Connect with a Kubernetes cluster","In our third article in our GitOps series, learn how to connect a Kubernetes cluster with GitLab for pull and push-based deployments.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663397/Blog/Hero%20Images/logoforblogpost.jpg","https://about.gitlab.com/blog/gitops-with-gitlab-connecting-the-cluster","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"GitOps with GitLab: Connect with a Kubernetes cluster\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Viktor Nagy\"}],\n        \"datePublished\": \"2021-11-18\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Viktor Nagy","2021-11-18","_It is possible to use GitLab as a best-in-class GitOps tool, and this blog\npost series is going to show you how. These easy-to-follow tutorials will\nfocus on different user problems, including provisioning, managing a base\ninfrastructure, and deploying various third-party or custom applications on\ntop of them. You can find the entire \"Ultimate guide to GitOps with GitLab\"\ntutorial series\n[here](/blog/the-ultimate-guide-to-gitops-with-gitlab/)._\n\n\n## GitOps with GitLab: connecting a Kubernetes cluster\n\n\nThis [GitOps](/topics/gitops/) with GitLab post shows how to connect a\nKubernetes cluster with GitLab for pull and push based deployments and easy\nsecurity integrations. In order to do so, the following elements are\nrequired:\n\n\n- A Kubernetes cluster that you can access and can create new resources,\nincluding `Role` and `RoleBinding` in it. \n\n- You will need `kubectl` and your local environment configured to access\nthe beforementioned cluster.\n\n- (Optional, recommended) Terraform and a Terraform project set up as shown\n[in the previous\narticle](/blog/gitops-with-gitlab-infrastructure-provisioning/)\nto retrieve an agent registration token from GitLab.\n\n- (Optional, recommended) `kpt` and `kustomize` to install the Agent into\nyour cluster.\n\n- (Optional, quickstart) If you prefer a less \"gitopsy\" approach, you will\nneed `docker` (Docker Desktop is not needed). This is simpler to follow, but\nprovides less control to you.\n\n\n## How to connect a cluster to GitLab\n\n\nThere are many ways how one can connect a cluster to GitLab:\n\n\n- you can set up a `$KUBECONTEXT` variable manually, manage all the related\nconnections and use GitLab CI/CD to push changes into your cluster\n\n- you can use a 3rd party tool, like\n[ArgoCD](https://argo-cd.readthedocs.io/en/stable/) or\n[Flux](https://fluxcd.io) to get pull based deployments\n\n- you can use the legacy, certificate-based cluster integration within\nGitLab in which case GitLab will manage the `$KUBECONTEXT` for you and you\ncan get easy metrics, log and monitoring integrations\n\n- or you can use the recommended approach, the [GitLab Agent for\nKubernetes](https://docs.gitlab.com/ee/user/clusters/agent/), to have pull\nand push based deployment support, network security policy integrations and\nthe possibility of metrics and monitoring too\n\n\nWe are going to focus on the Agent-based setup here as we believe that it\nserves and will serve our users best, hopefully you included.\n\n\n## How does the Agent work\n\n\nThe Agent has a component that needs to be installed into your cluster. We\ncall this component `agentk`. Once `agentk` is installed it reaches out to\nGitLab, and authenticates itself with an access token. So, the first step is\nto get a token from GitLab. We call this step \"the Agent registration.\" If\nthe authentication succeeds, `agentk` sets up a bidirectional GRPC channel\nbetween itself and GitLab. The emphasis here is on \"bidirectional.\" This\nenables requests and messages to be sent by either side and provides the\npossibility of much deeper integrations than the other approaches while\nstill being a nice citizen within your cluster.\n\n\nOnce the connection is established, the Agent retrieves its own\nconfiguration from GitLab. This configuration is a `config.yaml` file under\na repository, and you actually register the location of this configuration\nfile when you register a new Agent. The configuration describes the various\ncapabilities enabled of an Agent.\n\n\nOn the GitLab side, `agentk` communicates with - what we call - the\nKubernetes Agent Server, or `kas`. As most users do not have to deal with\nsetting up `kas`, I won't write about it here. You need to be a GitLab\nadministrator [to set up and manage\n`kas`](https://docs.gitlab.com/ee/administration/clusters/kas.html). If you\nare on gitlab.com, `kas` is available to you at `kas.gitlab.com`, thanks to\nour amazing SRE team.\n\n\nSo the steps we are going to take in this article are the following:\n\n\n1. Create a configuration file for the Agent\n\n1. Register the Agent and retrieve its authentication token\n\n1. Install `agentk` into the cluster together with the token\n\n\nFinally, we will set up an example pull-based deployment just to test that\neverything worked as expected. Let's get started!\n\n\n## How many Agents do you need for a larger setup\n\n\nWe recommend having a separate Agent registered at least against each of\nyour environments. If you have multiple clusters, have at least one agent\nregistered with each cluster. While it is possible to have many `agentk`\ndeployments with the same authentication token and thus configuration file,\nthis is not supported and might lead to syncronization problems!\n\n\nThe different agent configurations can use the same Kubernetes manifests for\ndeployments. So maintaining a multi-region cluster where all the clusters\nshould be identical does not require much effort. \n\n\nWe designed `agentk` to be very lightweight so you should not worry about\ndeploying multiple instances of it into a cluster. \n\n\nWe know users who use separate `agentk` instances by squad for example. In\nthese situations, the `squad` owns some namespaces in the cluster and each\nAgent can access only the namespaces available for their squad. This way\n`agentk` is not just a good citizen in your cluster, but is like a team\nmember in your squad.\n\n\n## Create a configuration file for the Agent\n\n\nNote:\n\nYou can use either the Terraform project from the previous step or start\nwith a new project. I will assume that we build on top of the Terraform\nsetup from the previous article, linked above, that will come in handy when\nwe want to register the Agent using Terraform. I won't go through setting up\nall the environment variables here for local Terraform run.\n\n\nDecide about your agent name, and create an empty file in your project under\n`.gitlab/agents/\u003Cyour agent name>/config.yaml`. Nota bene, that the\nextension is `yaml` not `yml` and your agent name must follow the [DNS label\nstandard from RFC\n1123](https://docs.gitlab.com/ee/user/clusters/agent/install/#create-an-agent-configuration-file).\nI'll call my agent `demo-agent`, so the file is under\n`.gitlab/demo-agent/config.yaml`.\n\n\n## Register the Agent\n\n\nThe next step is to register the Agent with GitLab. You can do this either\nthrough the GitLab UI or using Terraform. I will show you both approaches.\n\n\n### Registering through the UI\n\n\nOnce the configuration file is in place, visit `Infrastructure/Kubernetes`\nand add a new cluster using the Agent. A dialog will pop up where you can\nselect your agent.\n\n\nOnce you hit \"next,\" you will see the registration token and a `docker`\ncommand for easy installation. The `docker` command includes the token too\nand you can run it to quickly set up an `agentk` inside of your cluster.\n(You might need to create a namespace first!) Feel free to run the command\nfor a quickstart or follow the tutorial for a truly code-based approach.\n\n\n### Registering through code\n\n\nWe will use Terraform to register the Agent through code. Let's create the\nfollowing files:\n\n\n- Under `terraform/gitlab-agent/main.tf`\n\n\n```hcl\n\nterraform {\n  backend \"http\" {\n  }\n  required_version = \">= 0.13\"\n  required_providers {\n    gitlab = {\n      source = \"gitlabhq/gitlab\"\n      version = \"~>3.6.0\"\n    }\n  }\n}\n\n\nprovider \"gitlab\" {\n    token = var.gitlab_password\n}\n\n\nmodule \"gitlab_kubernetes_agent_registration\" {\n  source = \"gitlab.com/gitlab-org/kubernetes-agent-terraform-register-agent/local\"\n  version = \"0.0.2\"\n\n  gitlab_project_id = var.gitlab_project_id\n  gitlab_username = var.gitlab_username\n  gitlab_password = var.gitlab_password\n  gitlab_graphql_api_url = var.gitlab_graphql_api_url\n  agent_name = var.agent_name\n  token_name = var.token_name\n  token_description = var.token_description\n}\n\n```\n\n\nAs you can see we will use a module here. The module is hosted using the\nTerraform registry provided by GitLab. You can check out [the module source\ncode\nhere](https://gitlab.com/gitlab-org/configure/examples/kubernetes-agent-terraform-register-agent).\nYou might have guessed correctly that under the hood the module uses the\nGitLab GraphQL API to register the agent and retrieve a token. We will need\nto set up variables for it to work.\n\n\n- Create `terraform/gitlab-agent/variables.tf`\n\n\n```hcl\n\nvariable \"gitlab_project_id\" {\n  type = string\n}\n\n\nvariable \"gitlab_username\" {\n  type = string\n}\n\n\nvariable \"gitlab_password\" {\n  type = string\n}\n\n\nvariable \"agent_name\" {\n  type = string\n}\n\n\nvariable \"token_name\" {\n  type    = string\n  default = \"kas-token\"\n}\n\n\nvariable \"token_description\" {\n  type    = string\n  default = \"Token for KAS Agent Authentication\"\n}\n\n\nvariable \"gitlab_graphql_api_url\" {\n  type    = string\n  default = \"https://gitlab.com/api/graphql\"\n}\n\n```\n\n\n- Create `terraform/gitlab-agent/outputs.tf`\n\n\n```hcl\n\noutput \"agent_id\" {\n  value     = module.gitlab_kubernetes_agent_registration.agent_id\n}\n\n\noutput \"token_secret\" {\n  value     = module.gitlab_kubernetes_agent_registration.token_secret\n  sensitive = true\n}\n\n```\n\n\nOnce the registration is over, you'll be able to retrieve the agent ID and\nthe token using these Terraform outputs.\n\n\n### Run the Terraform project\n\n\nOnce the above code is in place, we need to run it to actually register the\nAgent. Here, I am going to extend the setup from the previous article.\n\n\n#### Running locally\n\n\n- Create `terraform/gitlab-agent/.envrc`  as you did for the network\nproject.\n\n\n```\n\nexport TF_STATE_NAME=${PWD##*terraform/}\n\nsource_env ../../.main.env\n\n```\n\n\nNow run Terraform\n\n\n```bash\n\nterraform init\n\nterraform plan\n\nterraform apply\n\n```\n\n\n#### Running from CI/CD pipeline\n\n\nExtend the `.gitlab-ci.yml` file with the following 3 jobs:\n\n\n```hcl\n\ngitlab-agent:init:\n  extends: .terraform:init\n  stage: init\n  variables:\n    TF_ROOT: terraform/gitlab-agent\n    TF_STATE_NAME: gitlab-agent\n  only:\n    changes:\n      - \"terraform/gitlab-agent/*\"\n\ngitlab-agent:review:\n  extends: .terraform:build\n  stage: build\n  variables:\n    TF_ROOT: terraform/gitlab-agent\n    TF_STATE_NAME: gitlab-agent\n  resource_group: tf:gitlab-agent\n  only:\n    changes:\n      - \"terraform/gitlab-agent/*\"\n\ngitlab-agent:deploy:\n  extends: .terraform:deploy\n  stage: deploy\n  variables:\n    TF_ROOT: terraform/gitlab-agent\n    TF_STATE_NAME: gitlab-agent\n  resource_group: tf:gitlab-agent\n  environment:\n    name: demo-agent\n  when: manual\n  only:\n    changes:\n      - \"terraform/gitlab-agent/*\"\n    variables:\n      - $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n```\n\n\nAs you can see these are the same jobs that we saw already, they are just\nparameterized for the `gitlab-agent` terraform project.\n\n\nNota bene, even if you use GitLab to register the Agent, you will need your\ncommand line to install `agentk` for the first time! As a result, you can\nnot avoid a local setup as you will need to run at least `terraform output`\nto retrieve the token!\n\n\n## Install `agentk`\n\n\nIn this tutorial we are going to follow [the advanced installation\ninstructions](https://docs.gitlab.com/ee/user/clusters/agent/install/index.html#advanced-installation)\nfrom the GitLab documentation. This approach is highly customizable using\n`kustomize` and `kpt`.\n\n\nFirst, let's retrieve the basic Kubernetes resource definitions using `kpt`:\n\n\n- Create a directory `packages` using `mkdir packages`\n\n- Run `kpt pkg get\nhttps://gitlab.com/gitlab-org/cluster-integration/gitlab-agent.git/build/deployment/gitlab-agent\npackages/gitlab-agent`\n\n\nThis will retrieve the most recent version of the `agentk` installation\nresources. You can request a tagged version with the well-known `@` syntax,\nfor example by running `kpt pkg get\nhttps://gitlab.com/gitlab-org/cluster-integration/gitlab-agent.git/build/deployment/gitlab-agent@v14.4.0\npackages/gitlab-agent`. You can see [all the available versions\nhere](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/tags).\n\n\n### Why `kpt` - could we make this a box?\n\n\nThe choice of `kpt` is because it allows sane upstream package management to\nyou. With `kpt` you will be able to regularly update your packages using\nsomething like `kpt pkg update packages/gitlab-agent@\u003Cnew version>\n--strategy=resource-merge`. It basically allows you to modify your package\nlocally, and will try to merge upstream changes into it. Read the `kpt pkg\nupdate -h` output for more information and alternative merge strategies.\n\n\n### Continue with the installation - if it's a box, this is not needed\n\n\nThe `kpt` packages you retrieved are actually a set up `kustomize` overlays.\nThe `base` defines only the `agentk` deployment and namespace; the `cluster`\ndefines some default RBAC around the deployment. Feel free to add your own\noverlays and use those. We will extend this package with custom overlays in\na part 6 of the series.\n\n\nTo configure the package, see the available configuration options using:\n\n\n```bash\n\nkustomize cfg list-setters packages/gitlab-agent\n        NAME                 VALUE               SET BY                  DESCRIPTION              COUNT   REQUIRED   IS SET  \n  agent-version       stable                 package-default   Image tag for agentk container     1       No         No      \n  kas-address         wss://kas.gitlab.com   package-default   kas address. Use                   1       No         No      \n                                                               grpc://host.docker.internal:8150                              \n                                                               if connecting from within Docker                              \n                                                               e.g. from kind.                                               \n  name-prefix                                                  Prefix for resource names          1       No         No      \n  namespace           gitlab-agent           package-default   Namespace to install GitLab        2       No         No      \n                                                               Kubernetes Agent into                                         \n  prometheus-scrape   true                   package-default   Enable or disable Prometheus       1       No         No      \n                                                               scraping of agentk metrics.                              \n```\n\n\nThe package default will be different if you used a tagged version for\ngetting the package. Let's set the version as using `stable` is not\nrecommended.\n\n\n```bash\n\nkustomize cfg set packages/gitlab-agent agent-version v14.4.1\n\nset 1 field(s) of setter \"agent-version\" to value \"v14.4.1\"\n\n```\n\n\nFeel free to adjust the other configuration options too or add you own\noverlays if that is needed.\n\n\n### Which agent-version to use - could we make this a box?\n\n\nIf possible the version of `agentk` should match the major and minor version\nof your GitLab instance. You can find our the version of your GitLab\ninstance under the Help menu on the UI.\n\n\nIf there is no agent version with your major and minor version, then pick\nthe agent with the highest major and minor below the version of your GitLab.\n\n\n### Continue with the installation - if it's a box, this is not needed\n\n\nWarning:\n\nBefore the next step, I want to warn you about never, ever committing\nunencrypted secrets into git, and the agent registration token is a secret!\n\n\nLet's retrieve the agent registration token from our Terraform project. Run\nthe following command in the `terraform/gitlab-agent` directory:\n\n\n```bash\n\nterraform output -raw token_secret >\n../../packages/gitlab-agent/base/secrets/agent.token\n\n```\n\n\nThis writes the registration token to a file on your local computer. Do not\ncommit these changes to git!\n\n\nAt this point, we are ready to deploy `agentk` into the cluster, so run:\n\n\n```bash\n\nkustomize build packages/gitlab-agent/cluster | kubectl apply -f -\n\n```\n\n\nLet's get rid of the secret:\n\n\n```bash\n\necho \"Invalid token\" > packages/gitlab-agent/base/secrets/agent.token\n\n```\n\n\nYou are good to commit your changes to `git` now!\n\n\n## Testing the setup\n\n\nWe have installed the Agent, now what? How can we start using it? In the\nnext article we will see in detail how to deploy a more serious application\ninto the cluster. Still, to check that cluster syncronization actually\nworks, let's deploy a `ConfigMap`.\n\n\n- Create `kubernetes/test_config.yaml` with the following content:\n\n\n```yaml\n\napiVersion: v1\n\nkind: ConfigMap\n\nmetadata:\n  name: gitlab-gitops\n  namespace: default\ndata:\n  key: It works!\n```\n\n\n- Modify your Agent configuration file under\n`.gitlab/demo-agent/config.yaml`, and add the following to it:\n\n\n```yaml\n\ngitops:\n  # Manifest projects are watched by the agent. Whenever a project changes,\n  # GitLab deploys the changes using the agent.\n  manifest_projects:\n  - id: path/to/your/project\n    default_namespace: gitlab-agent\n    # Paths inside of the repository to scan for manifest files.\n    # Directories with names starting with a dot are ignored.\n    paths:\n    - glob: 'kubernetes/test_config.yaml'\n    #- glob: 'kubernetes/**/*.yaml'\n```\n\n\nChange the `- id: path/to/your/project` line above to point to your\nproject's path!\n\n\nThe above configuration tells the Agent to kepp the\n`kubernetes/test_config.yaml` file in sync with the cluster. I've left a\ncommented line at the end to show how you could use wildcards. This will\ncome handy in future steps of this article. The`default_namespace` is used\nif no namespace is provided in the Kuberentes manifests. There are many\nother options to configure as well even for the `gitops` use case. You can\nread more about these in [the configuration file reference\ndocumentation](https://docs.gitlab.com/ee/user/clusters/agent/work_with_agent.html).\n\n\nOnce you commit the above changes, GitLab notifies `agentk` about the\nchanged files. First, `agentk` updates its configuration; second, it\nretrieves the `ConfigMap`.\n\n\nWait a few seconds, and run `kubectl describe configmap gitlab-gitops` to\ncheck that the changes got appliedd to your cluster. You should see\nsomething similar:\n\n\n```\n\nName:         gitlab-gitops\n\nNamespace:    default\n\nLabels:       \u003Cnone>\n\nAnnotations:  config.k8s.io/owning-inventory: 502-28431043\n              k8s-agent.gitlab.com/managed-object: managed\n\nData\n\n====\n\nkey:\n","engineering",[23,24,25],"kubernetes","integrations","inside GitLab",{"slug":27,"featured":6,"template":28},"gitops-with-gitlab-connecting-the-cluster","BlogPost","content:en-us:blog:gitops-with-gitlab-connecting-the-cluster.yml","yaml","Gitops With Gitlab Connecting The Cluster","content","en-us/blog/gitops-with-gitlab-connecting-the-cluster.yml","en-us/blog/gitops-with-gitlab-connecting-the-cluster","yml",{"_path":37,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":39,"_id":459,"_type":30,"title":460,"_source":32,"_file":461,"_stem":462,"_extension":35},"/shared/en-us/main-navigation","en-us",{"logo":40,"freeTrial":45,"sales":50,"login":55,"items":60,"search":390,"minimal":421,"duo":440,"pricingDeployment":449},{"config":41},{"href":42,"dataGaName":43,"dataGaLocation":44},"/","gitlab logo","header",{"text":46,"config":47},"Get free trial",{"href":48,"dataGaName":49,"dataGaLocation":44},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":51,"config":52},"Talk to sales",{"href":53,"dataGaName":54,"dataGaLocation":44},"/sales/","sales",{"text":56,"config":57},"Sign in",{"href":58,"dataGaName":59,"dataGaLocation":44},"https://gitlab.com/users/sign_in/","sign in",[61,105,202,207,311,371],{"text":62,"config":63,"cards":65,"footer":88},"Platform",{"dataNavLevelOne":64},"platform",[66,72,80],{"title":62,"description":67,"link":68},"The most comprehensive AI-powered DevSecOps Platform",{"text":69,"config":70},"Explore our Platform",{"href":71,"dataGaName":64,"dataGaLocation":44},"/platform/",{"title":73,"description":74,"link":75},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":76,"config":77},"Meet GitLab Duo",{"href":78,"dataGaName":79,"dataGaLocation":44},"/gitlab-duo/","gitlab duo ai",{"title":81,"description":82,"link":83},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":84,"config":85},"Learn more",{"href":86,"dataGaName":87,"dataGaLocation":44},"/why-gitlab/","why gitlab",{"title":89,"items":90},"Get started with",[91,96,101],{"text":92,"config":93},"Platform Engineering",{"href":94,"dataGaName":95,"dataGaLocation":44},"/solutions/platform-engineering/","platform engineering",{"text":97,"config":98},"Developer Experience",{"href":99,"dataGaName":100,"dataGaLocation":44},"/developer-experience/","Developer experience",{"text":102,"config":103},"MLOps",{"href":104,"dataGaName":102,"dataGaLocation":44},"/topics/devops/the-role-of-ai-in-devops/",{"text":106,"left":107,"config":108,"link":110,"lists":114,"footer":184},"Product",true,{"dataNavLevelOne":109},"solutions",{"text":111,"config":112},"View all Solutions",{"href":113,"dataGaName":109,"dataGaLocation":44},"/solutions/",[115,140,163],{"title":116,"description":117,"link":118,"items":123},"Automation","CI/CD and automation to accelerate deployment",{"config":119},{"icon":120,"href":121,"dataGaName":122,"dataGaLocation":44},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[124,128,132,136],{"text":125,"config":126},"CI/CD",{"href":127,"dataGaLocation":44,"dataGaName":125},"/solutions/continuous-integration/",{"text":129,"config":130},"AI-Assisted Development",{"href":78,"dataGaLocation":44,"dataGaName":131},"AI assisted development",{"text":133,"config":134},"Source Code Management",{"href":135,"dataGaLocation":44,"dataGaName":133},"/solutions/source-code-management/",{"text":137,"config":138},"Automated Software Delivery",{"href":121,"dataGaLocation":44,"dataGaName":139},"Automated software delivery",{"title":141,"description":142,"link":143,"items":148},"Security","Deliver code faster without compromising security",{"config":144},{"href":145,"dataGaName":146,"dataGaLocation":44,"icon":147},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[149,153,158],{"text":150,"config":151},"Application Security Testing",{"href":145,"dataGaName":152,"dataGaLocation":44},"Application security testing",{"text":154,"config":155},"Software Supply Chain Security",{"href":156,"dataGaLocation":44,"dataGaName":157},"/solutions/supply-chain/","Software supply chain security",{"text":159,"config":160},"Software Compliance",{"href":161,"dataGaName":162,"dataGaLocation":44},"/solutions/software-compliance/","software compliance",{"title":164,"link":165,"items":170},"Measurement",{"config":166},{"icon":167,"href":168,"dataGaName":169,"dataGaLocation":44},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[171,175,179],{"text":172,"config":173},"Visibility & Measurement",{"href":168,"dataGaLocation":44,"dataGaName":174},"Visibility and Measurement",{"text":176,"config":177},"Value Stream Management",{"href":178,"dataGaLocation":44,"dataGaName":176},"/solutions/value-stream-management/",{"text":180,"config":181},"Analytics & Insights",{"href":182,"dataGaLocation":44,"dataGaName":183},"/solutions/analytics-and-insights/","Analytics and insights",{"title":185,"items":186},"GitLab for",[187,192,197],{"text":188,"config":189},"Enterprise",{"href":190,"dataGaLocation":44,"dataGaName":191},"/enterprise/","enterprise",{"text":193,"config":194},"Small Business",{"href":195,"dataGaLocation":44,"dataGaName":196},"/small-business/","small business",{"text":198,"config":199},"Public Sector",{"href":200,"dataGaLocation":44,"dataGaName":201},"/solutions/public-sector/","public sector",{"text":203,"config":204},"Pricing",{"href":205,"dataGaName":206,"dataGaLocation":44,"dataNavLevelOne":206},"/pricing/","pricing",{"text":208,"config":209,"link":211,"lists":215,"feature":298},"Resources",{"dataNavLevelOne":210},"resources",{"text":212,"config":213},"View all resources",{"href":214,"dataGaName":210,"dataGaLocation":44},"/resources/",[216,248,270],{"title":217,"items":218},"Getting started",[219,224,229,234,239,244],{"text":220,"config":221},"Install",{"href":222,"dataGaName":223,"dataGaLocation":44},"/install/","install",{"text":225,"config":226},"Quick start guides",{"href":227,"dataGaName":228,"dataGaLocation":44},"/get-started/","quick setup checklists",{"text":230,"config":231},"Learn",{"href":232,"dataGaLocation":44,"dataGaName":233},"https://university.gitlab.com/","learn",{"text":235,"config":236},"Product documentation",{"href":237,"dataGaName":238,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":240,"config":241},"Best practice videos",{"href":242,"dataGaName":243,"dataGaLocation":44},"/getting-started-videos/","best practice videos",{"text":245,"config":246},"Integrations",{"href":247,"dataGaName":24,"dataGaLocation":44},"/integrations/",{"title":249,"items":250},"Discover",[251,256,260,265],{"text":252,"config":253},"Customer success stories",{"href":254,"dataGaName":255,"dataGaLocation":44},"/customers/","customer success stories",{"text":257,"config":258},"Blog",{"href":259,"dataGaName":5,"dataGaLocation":44},"/blog/",{"text":261,"config":262},"Remote",{"href":263,"dataGaName":264,"dataGaLocation":44},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":266,"config":267},"TeamOps",{"href":268,"dataGaName":269,"dataGaLocation":44},"/teamops/","teamops",{"title":271,"items":272},"Connect",[273,278,283,288,293],{"text":274,"config":275},"GitLab Services",{"href":276,"dataGaName":277,"dataGaLocation":44},"/services/","services",{"text":279,"config":280},"Community",{"href":281,"dataGaName":282,"dataGaLocation":44},"/community/","community",{"text":284,"config":285},"Forum",{"href":286,"dataGaName":287,"dataGaLocation":44},"https://forum.gitlab.com/","forum",{"text":289,"config":290},"Events",{"href":291,"dataGaName":292,"dataGaLocation":44},"/events/","events",{"text":294,"config":295},"Partners",{"href":296,"dataGaName":297,"dataGaLocation":44},"/partners/","partners",{"backgroundColor":299,"textColor":300,"text":301,"image":302,"link":306},"#2f2a6b","#fff","Insights for the future of software development",{"altText":303,"config":304},"the source promo card",{"src":305},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":307,"config":308},"Read the latest",{"href":309,"dataGaName":310,"dataGaLocation":44},"/the-source/","the source",{"text":312,"config":313,"lists":315},"Company",{"dataNavLevelOne":314},"company",[316],{"items":317},[318,323,329,331,336,341,346,351,356,361,366],{"text":319,"config":320},"About",{"href":321,"dataGaName":322,"dataGaLocation":44},"/company/","about",{"text":324,"config":325,"footerGa":328},"Jobs",{"href":326,"dataGaName":327,"dataGaLocation":44},"/jobs/","jobs",{"dataGaName":327},{"text":289,"config":330},{"href":291,"dataGaName":292,"dataGaLocation":44},{"text":332,"config":333},"Leadership",{"href":334,"dataGaName":335,"dataGaLocation":44},"/company/team/e-group/","leadership",{"text":337,"config":338},"Team",{"href":339,"dataGaName":340,"dataGaLocation":44},"/company/team/","team",{"text":342,"config":343},"Handbook",{"href":344,"dataGaName":345,"dataGaLocation":44},"https://handbook.gitlab.com/","handbook",{"text":347,"config":348},"Investor relations",{"href":349,"dataGaName":350,"dataGaLocation":44},"https://ir.gitlab.com/","investor relations",{"text":352,"config":353},"Trust Center",{"href":354,"dataGaName":355,"dataGaLocation":44},"/security/","trust center",{"text":357,"config":358},"AI Transparency Center",{"href":359,"dataGaName":360,"dataGaLocation":44},"/ai-transparency-center/","ai transparency center",{"text":362,"config":363},"Newsletter",{"href":364,"dataGaName":365,"dataGaLocation":44},"/company/contact/","newsletter",{"text":367,"config":368},"Press",{"href":369,"dataGaName":370,"dataGaLocation":44},"/press/","press",{"text":372,"config":373,"lists":374},"Contact us",{"dataNavLevelOne":314},[375],{"items":376},[377,380,385],{"text":51,"config":378},{"href":53,"dataGaName":379,"dataGaLocation":44},"talk to sales",{"text":381,"config":382},"Support portal",{"href":383,"dataGaName":384,"dataGaLocation":44},"https://support.gitlab.com","support portal",{"text":386,"config":387},"Customer portal",{"href":388,"dataGaName":389,"dataGaLocation":44},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":391,"login":392,"suggestions":399},"Close",{"text":393,"link":394},"To search repositories and projects, login to",{"text":395,"config":396},"gitlab.com",{"href":58,"dataGaName":397,"dataGaLocation":398},"search login","search",{"text":400,"default":401},"Suggestions",[402,404,408,410,414,418],{"text":73,"config":403},{"href":78,"dataGaName":73,"dataGaLocation":398},{"text":405,"config":406},"Code Suggestions (AI)",{"href":407,"dataGaName":405,"dataGaLocation":398},"/solutions/code-suggestions/",{"text":125,"config":409},{"href":127,"dataGaName":125,"dataGaLocation":398},{"text":411,"config":412},"GitLab on AWS",{"href":413,"dataGaName":411,"dataGaLocation":398},"/partners/technology-partners/aws/",{"text":415,"config":416},"GitLab on Google Cloud",{"href":417,"dataGaName":415,"dataGaLocation":398},"/partners/technology-partners/google-cloud-platform/",{"text":419,"config":420},"Why GitLab?",{"href":86,"dataGaName":419,"dataGaLocation":398},{"freeTrial":422,"mobileIcon":427,"desktopIcon":432,"secondaryButton":435},{"text":423,"config":424},"Start free trial",{"href":425,"dataGaName":49,"dataGaLocation":426},"https://gitlab.com/-/trials/new/","nav",{"altText":428,"config":429},"Gitlab Icon",{"src":430,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":428,"config":433},{"src":434,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":436,"config":437},"Get Started",{"href":438,"dataGaName":439,"dataGaLocation":426},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":441,"mobileIcon":445,"desktopIcon":447},{"text":442,"config":443},"Learn more about GitLab Duo",{"href":78,"dataGaName":444,"dataGaLocation":426},"gitlab duo",{"altText":428,"config":446},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":448},{"src":434,"dataGaName":431,"dataGaLocation":426},{"freeTrial":450,"mobileIcon":455,"desktopIcon":457},{"text":451,"config":452},"Back to pricing",{"href":205,"dataGaName":453,"dataGaLocation":426,"icon":454},"back to pricing","GoBack",{"altText":428,"config":456},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":458},{"src":434,"dataGaName":431,"dataGaLocation":426},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":464,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"title":465,"button":466,"image":471,"config":475,"_id":477,"_type":30,"_source":32,"_file":478,"_stem":479,"_extension":35},"/shared/en-us/banner","is now in public beta!",{"text":467,"config":468},"Try the Beta",{"href":469,"dataGaName":470,"dataGaLocation":44},"/gitlab-duo/agent-platform/","duo banner",{"altText":472,"config":473},"GitLab Duo Agent Platform",{"src":474},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":476},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":481,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":482,"_id":721,"_type":30,"title":722,"_source":32,"_file":723,"_stem":724,"_extension":35},"/shared/en-us/main-footer",{"text":483,"source":484,"edit":490,"contribute":495,"config":500,"items":505,"minimal":713},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":485,"config":486},"View page source",{"href":487,"dataGaName":488,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":491,"config":492},"Edit this page",{"href":493,"dataGaName":494,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":496,"config":497},"Please contribute",{"href":498,"dataGaName":499,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":501,"facebook":502,"youtube":503,"linkedin":504},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[506,553,606,650,679],{"title":203,"links":507,"subMenu":522},[508,512,517],{"text":509,"config":510},"View plans",{"href":205,"dataGaName":511,"dataGaLocation":489},"view plans",{"text":513,"config":514},"Why Premium?",{"href":515,"dataGaName":516,"dataGaLocation":489},"/pricing/premium/","why premium",{"text":518,"config":519},"Why Ultimate?",{"href":520,"dataGaName":521,"dataGaLocation":489},"/pricing/ultimate/","why ultimate",[523],{"title":524,"links":525},"Contact Us",[526,529,531,533,538,543,548],{"text":527,"config":528},"Contact sales",{"href":53,"dataGaName":54,"dataGaLocation":489},{"text":381,"config":530},{"href":383,"dataGaName":384,"dataGaLocation":489},{"text":386,"config":532},{"href":388,"dataGaName":389,"dataGaLocation":489},{"text":534,"config":535},"Status",{"href":536,"dataGaName":537,"dataGaLocation":489},"https://status.gitlab.com/","status",{"text":539,"config":540},"Terms of use",{"href":541,"dataGaName":542,"dataGaLocation":489},"/terms/","terms of use",{"text":544,"config":545},"Privacy statement",{"href":546,"dataGaName":547,"dataGaLocation":489},"/privacy/","privacy statement",{"text":549,"config":550},"Cookie preferences",{"dataGaName":551,"dataGaLocation":489,"id":552,"isOneTrustButton":107},"cookie preferences","ot-sdk-btn",{"title":106,"links":554,"subMenu":562},[555,559],{"text":556,"config":557},"DevSecOps platform",{"href":71,"dataGaName":558,"dataGaLocation":489},"devsecops platform",{"text":129,"config":560},{"href":78,"dataGaName":561,"dataGaLocation":489},"ai-assisted development",[563],{"title":564,"links":565},"Topics",[566,571,576,581,586,591,596,601],{"text":567,"config":568},"CICD",{"href":569,"dataGaName":570,"dataGaLocation":489},"/topics/ci-cd/","cicd",{"text":572,"config":573},"GitOps",{"href":574,"dataGaName":575,"dataGaLocation":489},"/topics/gitops/","gitops",{"text":577,"config":578},"DevOps",{"href":579,"dataGaName":580,"dataGaLocation":489},"/topics/devops/","devops",{"text":582,"config":583},"Version Control",{"href":584,"dataGaName":585,"dataGaLocation":489},"/topics/version-control/","version control",{"text":587,"config":588},"DevSecOps",{"href":589,"dataGaName":590,"dataGaLocation":489},"/topics/devsecops/","devsecops",{"text":592,"config":593},"Cloud Native",{"href":594,"dataGaName":595,"dataGaLocation":489},"/topics/cloud-native/","cloud native",{"text":597,"config":598},"AI for Coding",{"href":599,"dataGaName":600,"dataGaLocation":489},"/topics/devops/ai-for-coding/","ai for coding",{"text":602,"config":603},"Agentic AI",{"href":604,"dataGaName":605,"dataGaLocation":489},"/topics/agentic-ai/","agentic ai",{"title":607,"links":608},"Solutions",[609,611,613,618,622,625,629,632,634,637,640,645],{"text":150,"config":610},{"href":145,"dataGaName":150,"dataGaLocation":489},{"text":139,"config":612},{"href":121,"dataGaName":122,"dataGaLocation":489},{"text":614,"config":615},"Agile development",{"href":616,"dataGaName":617,"dataGaLocation":489},"/solutions/agile-delivery/","agile delivery",{"text":619,"config":620},"SCM",{"href":135,"dataGaName":621,"dataGaLocation":489},"source code management",{"text":567,"config":623},{"href":127,"dataGaName":624,"dataGaLocation":489},"continuous integration & delivery",{"text":626,"config":627},"Value stream management",{"href":178,"dataGaName":628,"dataGaLocation":489},"value stream management",{"text":572,"config":630},{"href":631,"dataGaName":575,"dataGaLocation":489},"/solutions/gitops/",{"text":188,"config":633},{"href":190,"dataGaName":191,"dataGaLocation":489},{"text":635,"config":636},"Small business",{"href":195,"dataGaName":196,"dataGaLocation":489},{"text":638,"config":639},"Public sector",{"href":200,"dataGaName":201,"dataGaLocation":489},{"text":641,"config":642},"Education",{"href":643,"dataGaName":644,"dataGaLocation":489},"/solutions/education/","education",{"text":646,"config":647},"Financial services",{"href":648,"dataGaName":649,"dataGaLocation":489},"/solutions/finance/","financial services",{"title":208,"links":651},[652,654,656,658,661,663,665,667,669,671,673,675,677],{"text":220,"config":653},{"href":222,"dataGaName":223,"dataGaLocation":489},{"text":225,"config":655},{"href":227,"dataGaName":228,"dataGaLocation":489},{"text":230,"config":657},{"href":232,"dataGaName":233,"dataGaLocation":489},{"text":235,"config":659},{"href":237,"dataGaName":660,"dataGaLocation":489},"docs",{"text":257,"config":662},{"href":259,"dataGaName":5,"dataGaLocation":489},{"text":252,"config":664},{"href":254,"dataGaName":255,"dataGaLocation":489},{"text":261,"config":666},{"href":263,"dataGaName":264,"dataGaLocation":489},{"text":274,"config":668},{"href":276,"dataGaName":277,"dataGaLocation":489},{"text":266,"config":670},{"href":268,"dataGaName":269,"dataGaLocation":489},{"text":279,"config":672},{"href":281,"dataGaName":282,"dataGaLocation":489},{"text":284,"config":674},{"href":286,"dataGaName":287,"dataGaLocation":489},{"text":289,"config":676},{"href":291,"dataGaName":292,"dataGaLocation":489},{"text":294,"config":678},{"href":296,"dataGaName":297,"dataGaLocation":489},{"title":312,"links":680},[681,683,685,687,689,691,693,697,702,704,706,708],{"text":319,"config":682},{"href":321,"dataGaName":314,"dataGaLocation":489},{"text":324,"config":684},{"href":326,"dataGaName":327,"dataGaLocation":489},{"text":332,"config":686},{"href":334,"dataGaName":335,"dataGaLocation":489},{"text":337,"config":688},{"href":339,"dataGaName":340,"dataGaLocation":489},{"text":342,"config":690},{"href":344,"dataGaName":345,"dataGaLocation":489},{"text":347,"config":692},{"href":349,"dataGaName":350,"dataGaLocation":489},{"text":694,"config":695},"Sustainability",{"href":696,"dataGaName":694,"dataGaLocation":489},"/sustainability/",{"text":698,"config":699},"Diversity, inclusion and belonging (DIB)",{"href":700,"dataGaName":701,"dataGaLocation":489},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":352,"config":703},{"href":354,"dataGaName":355,"dataGaLocation":489},{"text":362,"config":705},{"href":364,"dataGaName":365,"dataGaLocation":489},{"text":367,"config":707},{"href":369,"dataGaName":370,"dataGaLocation":489},{"text":709,"config":710},"Modern Slavery Transparency Statement",{"href":711,"dataGaName":712,"dataGaLocation":489},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":714},[715,717,719],{"text":539,"config":716},{"href":541,"dataGaName":542,"dataGaLocation":489},{"text":544,"config":718},{"href":546,"dataGaName":547,"dataGaLocation":489},{"text":549,"config":720},{"dataGaName":551,"dataGaLocation":489,"id":552,"isOneTrustButton":107},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[726],{"_path":727,"_dir":728,"_draft":6,"_partial":6,"_locale":7,"content":729,"config":733,"_id":735,"_type":30,"title":18,"_source":32,"_file":736,"_stem":737,"_extension":35},"/en-us/blog/authors/viktor-nagy","authors",{"name":18,"config":730},{"headshot":731,"ctfId":732},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749662918/Blog/Author%20Headshots/nagy-headshot.jpg","nagyvgitlab",{"template":734},"BlogAuthor","content:en-us:blog:authors:viktor-nagy.yml","en-us/blog/authors/viktor-nagy.yml","en-us/blog/authors/viktor-nagy",{"_path":739,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"header":740,"eyebrow":741,"blurb":742,"button":743,"secondaryButton":747,"_id":749,"_type":30,"title":750,"_source":32,"_file":751,"_stem":752,"_extension":35},"/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":46,"config":744},{"href":745,"dataGaName":49,"dataGaLocation":746},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":51,"config":748},{"href":53,"dataGaName":54,"dataGaLocation":746},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":754,"content":755,"config":758,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},{"title":9,"description":10,"authors":756,"heroImage":11,"date":19,"body":20,"category":21,"tags":757},[18],[23,24,25],{"slug":27,"featured":6,"template":28},1761814407459]