[{"data":1,"prerenderedAt":761},["ShallowReactive",2],{"/en-us/blog/how-gitlab-can-help-mitigate-deletion-open-source-images-docker-hub":3,"navigation-en-us":37,"banner-en-us":465,"footer-en-us":482,"Michael Friedrich":727,"next-steps-en-us":740,"footer-source-/en-us/blog/how-gitlab-can-help-mitigate-deletion-open-source-images-docker-hub/":755},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":27,"_id":30,"_type":31,"title":32,"_source":33,"_file":34,"_stem":35,"_extension":36},"/en-us/blog/how-gitlab-can-help-mitigate-deletion-open-source-images-docker-hub","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"GitLab helps mitigate Docker Hub's open source image removal","CI/CD and Kubernetes deployments can be affected by Docker Hub tier changes. This tutorial walks through analysis, mitigations, and long-term solutions.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659883/Blog/Hero%20Images/post-cover-image.jpg","https://about.gitlab.com/blog/how-gitlab-can-help-mitigate-deletion-open-source-images-docker-hub","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How GitLab can help mitigate deletion of open source container images on Docker Hub\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Michael Friedrich\"}],\n        \"datePublished\": \"2023-03-16\",\n      }",{"title":17,"description":10,"authors":18,"heroImage":11,"date":20,"body":21,"category":22,"tags":23},"How GitLab can help mitigate deletion of open source container images on Docker Hub",[19],"Michael Friedrich","2023-03-16","Docker, Inc. shared an email update to Docker Hub users that it will [sunset\nFree Team\norganizations](https://www.infoworld.com/article/3690890/docker-sunsets-free-team-subscriptions-roiling-open-source-projects.html).\nIf accounts do not upgrade to a paid plan before April 14, 2023, their\norganization's images may be deleted after 30 days. This change can affect\nopen source organizations that publish their images on Docker Hub, as well\nas consumers of these container images, used in CI/CD pipelines, Kubernetes\ncluster deployments, or docker-compose demo environments. This blog post\ndiscusses tools and features on the GitLab DevSecOps platform to help users\nanalyze and mitigate the potential impact on production environments.\n\n\n_Update (March 20, 2023): Docker, Inc. [published an apology blog\npost](https://www.docker.com/blog/we-apologize-we-did-a-terrible-job-announcing-the-end-of-docker-free-teams/),\nincluding a FAQ, and clarifies that the company will not delete container\nimages by themselves. Maintainers can migrate to a personal account, join\nthe Docker-sponsored open source program, or opt into a paid plan. If open\nsource container image maintainers do nothing, this leads into another\nissue: Stale container images can become a security problem. The following\nblog post can help with security analysis and migration too._\n\n\n_Update (March 27, 2023): On March 24, 2023, Docker, Inc. [published another\nblog\npost](https://www.docker.com/blog/no-longer-sunsetting-the-free-team-plan/)\nannouncing the reversal of the decision to sunset the Free team plan and\nupdated its [FAQ for Free Team\norganization](https://www.docker.com/developers/free-team-faq/). While this\nis a welcome development for the entire community, it is still crucial to\nensure the reliability of your software development lifecycle by ensuring\nredundancies are in place for your container registries, as detailed in this\nblog post._\n\n\n### Inventory of used container images\n\n\nCI/CD pipelines in GitLab can execute jobs in containers. This is specified\nby the [`image` keyword](https://docs.gitlab.com/ee/ci/yaml/#image) in jobs,\njob templates, or as a global\n[`default`](https://docs.gitlab.com/ee/ci/yaml/#default) attribute. For the\nfirst iteration, you can clone a GitLab project locally, and search for the\n`image` string in all CI/CD configuration files. The following example shows\nhow to execute the `find` command on the command line interface (CLI),\nsearching for files matching the name pattern `*ci.yml`, and looking for the\n`image` string in the file content. The command line prints a list of search\npattern matches, and the corresponding file name to the standard output. The\nexample inspects the [project](https://gitlab.com/gitlab-com/www-gitlab-com)\nfor the [GitLab handbook](https://handbook.gitlab.com/handbook/) and\n[website](https://about.gitlab.com/) to analyze whether its CI/CD deployment\npipelines could be affected by the Docker Hub changes.\n\n\n```bash\n\n$ git clone https://gitlab.com/gitlab-com/www-gitlab-com && cd\nwww-gitlab-com\n\n\n$ find . -type f -iname '*ci.yml' -exec sh -c \"grep 'image:' '{}' && echo\n{}\" \\;\n\n  image: registry.gitlab.com/gitlab-org/gitlab-build-images:www-gitlab-com-debian-${DEBIAN_VERSION}-ruby-3.0-node-16\n  image: alpine:edge\n  image: alpine:edge\n  image: debian:stable-slim\n  image: debian:stable-slim\n  image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger\n./.gitlab-ci.yml\n\n```\n\n\nA [discussion on Hacker News](https://news.ycombinator.com/item?id=35168802)\nmentions that \"official Docker images\" are not affected, but this is not\nofficially confirmed by Docker yet. [Official Docker\nimages](https://hub.docker.com/u/library) do not use a namespace prefix,\ni.e. `namespace/imagename` but instead `debian:\u003Ctagname>` for example.\n`registry.gitlab.com/gitlab-org/gitlab-build-images:danger` uses a full URL\nimage string, which includes the image registry server domain,\n`registry.gitlab.com` in the shown example.\n\n\nIf there is no full URL prefix in the image string, this is an indicator\nthat this image could be pulled from Docker Hub by default. There might be\nother infrastructure safety nets put in place, for example a cloud provider\nregistry which caches the Docker Hub images (Google Cloud, AWS, Azure,\netc.).\n\n\n#### Advanced search for images\n\n\nYou can use the [project lint API\nendpoint](https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration)\nto fetch the CI configuration. The following script uses the [python-gitlab\nAPI\nlibrary](https://python-gitlab.readthedocs.io/en/stable/gl_objects/ci_lint.html)\nto implement the API endpoint:\n\n\n1. Collect all projects from either a single project ID, a group ID with\nprojects, or from the instance.\n\n2. Run the `project.ci_lint.get()` method to get a merged yaml configuration\nfor CI/CD from the current GitLab project.\n\n3. Parse the yaml content and print only the job names, and the image keys.\n\n\nThe [full script is located\nhere](https://gitlab.com/gitlab-da/use-cases/gitlab-api/gitlab-api-python/-/blob/main/get_all_cicd_job_images.py),\nand is open source, licensed under MIT.\n\n\n```python\n\n#!/usr/bin/env python\n\n\nimport gitlab\n\nimport os\n\nimport sys\n\nimport yaml\n\n\nGITLAB_SERVER = os.environ.get('GL_SERVER', 'https://gitlab.com')\n\nGITLAB_TOKEN = os.environ.get('GL_TOKEN') # token requires developer\npermissions\n\nPROJECT_ID = os.environ.get('GL_PROJECT_ID') #optional\n\n# https://gitlab.com/gitlab-da/use-cases/docker\n\nGROUP_ID = os.environ.get('GL_GROUP_ID', 65096153) #optional\n\n\n#################\n\n# Main\n\n\nif __name__ == \"__main__\":\n    if not GITLAB_TOKEN:\n        print(\"🤔 Please set the GL_TOKEN env variable.\")\n        sys.exit(1)\n\n    gl = gitlab.Gitlab(GITLAB_SERVER, private_token=GITLAB_TOKEN)\n\n    # Collect all projects, or prefer projects from a group id, or a project id\n    projects = []\n\n    # Direct project ID\n    if PROJECT_ID:\n        projects.append(gl.projects.get(PROJECT_ID))\n\n    # Groups and projects inside\n    elif GROUP_ID:\n        group = gl.groups.get(GROUP_ID)\n\n        for project in group.projects.list(include_subgroups=True, all=True):\n            # https://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#examples\n            manageable_project = gl.projects.get(project.id)\n            projects.append(manageable_project)\n\n    # All projects on the instance (may take a while to process)\n    else:\n        projects = gl.projects.list(get_all=True)\n\n    print(\"# Summary of projects and their CI/CD image usage\")\n\n    # Loop over projects, fetch .gitlab-ci.yml, run the linter to get the full translated config, and extract the `image:` setting\n    for project in projects:\n\n        print(\"# Project: {name}, ID: {id}\\n\\n\".format(name=project.name_with_namespace, id=project.id))\n\n        # https://python-gitlab.readthedocs.io/en/stable/gl_objects/ci_lint.html\n        lint_result = project.ci_lint.get()\n\n        data = yaml.safe_load(lint_result.merged_yaml)\n\n        for d in data:\n            print(\"Job name: {n}\".format(n=d))\n            for attr in data[d]:\n                if 'image' in attr:\n                    print(\"Image: {i}\".format(i=data[d][attr]))\n\n        print(\"\\n\\n\")\n\nsys.exit(0)\n\n```\n\n\nThe\n[script](https://gitlab.com/gitlab-de/use-cases/gitlab-api/gitlab-api-python/-/blob/main/get_all_cicd_job_images.py)\nrequires Python (tested with 3.11) and the python-gitlab and pyyaml modules.\nExample on macOS with Homebrew:\n\n\n```shell\n\n$ brew install python\n\n$ pip3 install python-gitlab pyyaml\n\n```\n\n\nYou can execute the script and set the different environment variables to\ncontrol its behavior:\n\n\n```shell\n\n$ export GL_TOKEN=$GITLAB_TOKEN\n\n\n$ export GL_GROUP_ID=12345\n\n$ export GL_PROJECT_ID=98765\n\n\n$ python3 get_all_cicd_job_images.py\n\n\n# Summary of projects and their CI/CD image usage\n\n# Project: Developer Evangelism at GitLab  / use-cases / Docker Use cases  /\nCustom Container Image Python, ID: 44352983\n\n\nJob name: docker-build\n\nImage: docker:latest\n\n\n# Project: Developer Evangelism at GitLab  / use-cases / Docker Use cases  /\nGitlab Dependency Proxy, ID: 44351128\n\n\nJob name: .test-python-version\n\nJob name: image-docker-hub\n\nImage: python:3.11\n\nJob name: image-docker-hub-dep-proxy\n\nImage: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/python:3.11\n\n```\n\n\nPlease verify the script and fork it for your own analysis and mitigation.\nThe missing parts are checking the image URLs, and doing a more\nsophisticated search. The code has been prepared to either check against a\nsingle project, a group with projects, or an instance (this may take very\nlong, use with care).\n\n\nYou can perform a more history-focused analysis by fetching the CI/CD job\nlogs from GitLab and search for the pulled container image to get an\noverview of past Docker executor runs – for example: `Using Docker executor\nwith image python:3.11 ...`. The screenshot shows the CI/CD job logs UI\nsearch – you can automate the search using the GitLab API, and the\n[python-gitlab\nlibrary](https://python-gitlab.readthedocs.io/en/stable/gl_objects/pipelines_and_jobs.html#jobs),\nfor example.\n\n\n![GitLab CI/CD job logs, searching for the `image`\nkeyword](https://about.gitlab.com/images/blogimages/docker-hub-oss-image-deletion-mitigation/cicd_gitlab_job_logs_search_image.png)\n\n\nThis snippet can be used in combination with the code shared for the CI lint\nAPI endpoint. It fetches the job trace logs, and searches for the `image`\nkeyword in the log. The missing parts are splitting the log line by line,\nand extracting the image key information. This is left as an exercise for\nthe reader.\n\n\n```python\n        for job in project.jobs.list():\n            log_trace = str(job.trace())\n\n            print(log_trace)\n\n            if 'image' in log_trace:\n                print(\"Job ID: {i}, URL {u}\".format(i=job.id, u=job.web_url))\n                print(log_trace)\n```\n\n\n### More inventory considerations\n\n\nSimilar to the API script for CI/CD navigating through all projects, you\nwill need to analyze all Kubernetes manifest configuration files – using\neither a pull- or push-based approach. This can be achieved by using the\n[python-gitlab methods to load files from the\nrepository](https://python-gitlab.readthedocs.io/en/stable/gl_objects/projects.html#project-files)\nand searching the content in similar ways. Helm charts use container images,\ntoo, and will require additional analysis.\n\n\nAn additional search possibility: Custom-built container images that use\nDocker Hub images as a source. A project will consist of:\n\n\n1. `Dockerfile` file that uses `FROM \u003Cimagename>`\n\n2. `.gitlab-ci.yml` configuration file that builds container images (using\nDocker-in-Docker, Kaniko, etc.)\n\n\nAn alternative search method for customers is available by using the\n[Advanced\nSearch](https://docs.gitlab.com/ee/user/search/advanced_search.html) through\nthe GitLab UI and API. The following example uses the [scope:\nblobs](https://docs.gitlab.com/ee/api/search.html#scope-blobs-premium-2) to\nsearch for the `FROM` string:\n\n\n```shell\n\n$ export GITLAB_TOKEN=xxxxxxxxx\n\n\n# Search in https://gitlab.com/gitlab-da\n\n/use-cases/docker/custom-container-image-python\n\n\n$ curl --header \"PRIVATE-TOKEN: $GITLAB_TOKEN\"\n\"https://gitlab.com/api/v4/projects/44352983/search?scope=blobs&search=FROM%20filename:Dockerfile*\"\n\n```\n\n\n![Command line output from Advanced Search API, scope blobs, search `FROM`\nin `Dockerfile*` file\nnames.](https://about.gitlab.com/images/blogimages/docker-hub-oss-image-deletion-mitigation/cli_gitlab_advanced_search_api_dockerfile_from.png)\n\n\n## Mitigations and solutions\n\n\nThe following sections discuss potential mitigation strategies, and\nlong-term solutions.\n\n\n### Mitigation: GitLab dependency proxy\n\n\nThe dependency proxy provides a caching mechanism for Docker Hub images. It\nhelps reduce the bandwidth and time required to download and pull the\nimages. It also helped to [mitigate the Docker Hub pull rate limits\nintroduced in\n2020](/blog/minor-breaking-change-dependency-proxy/). The\ndependency proxy can be configured for public and private projects.\n\n\nThe [dependency\nproxy](https://docs.gitlab.com/ee/user/packages/dependency_proxy/) needs to\nbe enabled for a group. It also needs to be enabled by an instance\nadministrator for self-managed environments, if turned off.\n\n\nThe following example creates two jobs: `image-docker-hub` and\n`image-docker-hub-dep-proxy`. The dependency proxy job uses the\n`CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX` CI/CD variable to instruct GitLab\nto store the image in the cache, and only pull it once when not available.\n\n\n```yaml\n\n.test-python-version:\n  script:\n    - echo \"Testing Python version:\"\n    - python --version\n\nimage-docker-hub:\n  extends: .test-python-version\n  image: python:3.11\n\nimage-docker-hub-dep-proxy:\n  extends: .test-python-version\n  image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/python:3.11\n```\n\n\nThe configuration is available in [this\nproject](https://gitlab.com/gitlab-de/use-cases/docker/gitlab-dependency-proxy).\n\n\nThe stored container image is visible at the group level in the `Package and\ncontainer registries > Dependency Proxy` menu.\n\n\n### Mitigation: Container registry mirror\n\n\n[This blog\npost](/blog/mitigating-the-impact-of-docker-hub-pull-requests-limits/)\ndescribes how to run a local container registry mirror. Skopeo from Red Hat\nis another alternative for syncing container image registries, a practical\nexample is described [in this\narticle](https://marcbrandner.com/blog/transporting-container-images-with-skopeo/).\n\n\nThe GitLab Cloud Native installation ([Helm\ncharts](https://docs.gitlab.com/charts/) and\n[Operator](https://docs.gitlab.com/operator/)) use a [mirror of tagged\nimages](https://gitlab.com/gitlab-org/cloud-native/mirror/images) consumed\nby the related projects. Other product stages follow a similar approach, the\n[security scanners are shipped in container\nimages](https://docs.gitlab.com/ee/user/application_security/offline_deployments/#container-registries-and-package-repositories)\nmaintained by GitLab. This also enables self-managed airgapped\ninstallations.\n\n\n### Mitigation: Custom images in GitLab container registry\n\n\nReproducible builds and compliance requirements may have required you to\ncreate custom container images for CI/CD and Kubernetes already. This is\nalso key to verify that no untested and untrusted images are being used in\nproduction. GitLab provides a fully integrated [container\nregistry](https://docs.gitlab.com/ee/user/packages/container_registry/),\nwhich can be used natively within CI/CD pipelines and [GitOps workflows with\nthe agent for\nKubernetes](https://docs.gitlab.com/ee/user/clusters/agent/gitops.html).\n\n\nThe following `Dockerfile` example extends an existing image layer, and\ninstalls additional tools using the Debian Apt package manager.\n\n\n```\n\nFROM python:3.11-bullseye\n\n\nENV DEBIAN_FRONTEND noninteractive\n\n\nRUN apt update && apt -y install git curl jq && rm -rf /var/lib/apt/lists/*\n\n```\n\n\nYou can [use Docker to build container\nimages](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html), and\nalternative options are Kaniko or Podman. On GitLab.com SaaS, you can use\nthe Docker CI/CD template to build and push images. The following example\nmodifies the `docker-build` job to only build the latest tag from the\ndefault branch:\n\n\n```yaml\n\ninclude:\n  - template: Docker.gitlab-ci.yml\n\ndocker-build:\n  stage: build\n  rules:\n    - if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG'\n      #when: manual\n      #allow_failure: true\n```\n\n\nFor this example, we specifically want to provide a Git tag that gets used\nfor the container image tag as well.\n\n\n```\n\n$ git tag 3-11-bullseye\n\n$ git push --tags\n\n```\n\n\nThe image will be available at the GitLab container registry URL and the\nproject namespace path.This path needs to be replaced in all projects that\nuse a Python-based image. You can [create scripts for the GitLab\nAPI](/blog/efficient-devsecops-workflows-hands-on-python-gitlab-api-automation/)\nto update files and create MRs automatically,\n\n\n```\n\nimage:\nregistry.gitlab.com/gitlab-da/use-cases/docker/custom-container-image-python:3-11-bullseye\n\n```\n\n\n_Note: This is a demo project and not actively maintained. Please fork/copy\nit for your own needs._\n\n\n## Observability and security\n\n\nThe [number of failed CI/CD\npipelines](https://docs.gitlab.com/ee/user/analytics/ci_cd_analytics.html)\ncan be a good service level indicator (SLI) to verify whether the\nenvironment is affected by the Docker Hub changes. The same SLI applies for\nCI/CD jobs that build container images, using a `Dockerfile` file, which is\nbased on Docker Hub images (FROM \u003Cimagename>).\n\n\nA similar SLI applies to Kubernetes cluster deployments – if they continue\nto generate failures in GitOps pull or CI/CD push scenarios, additional\nanalysis and actions are required. The pod status `ErrImagePull` and\n[`ImagePullBackOff`](https://kubernetes.io/docs/concepts/containers/images/#imagepullbackoff)\nwill immediately show the problems. The [image pull\npolicy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy)\nshould also be revised – `Always` will immediately cause a problem, while\n`IfNotPresent` will use the local image cache.\n\n\n[This alert rule\nexample](https://awesome-prometheus-alerts.grep.to/rules.html#rule-kubernetes-1-18)\nfor Prometheus observing a Kubernetes cluster can help detect the pod state\nas not healthy.\n\n\n```yaml\n  - alert: KubernetesPodNotHealthy\n    expr: sum by (namespace, pod) (kube_pod_status_phase{phase=~\"Pending|Unknown|Failed\"}) > 0\n    for: 15m\n    labels:\n      severity: critical\n    annotations:\n      summary: Kubernetes Pod not healthy (instance {{ $labels.instance }})\n      description: \"Pod has been in a non-ready state for longer than 15 minutes.\\n  VALUE = {{ $value }}\\n  LABELS = {{ $labels }}\"\n```\n\n\nCI/CD pipeline linters and Git hooks can also be helpful to enforce using a\nGitLab registry URL prefix in all `image` tags, when new updates to CI/CD\nconfigurations are being pushed into merge requests.\n\n\nKubernetes deployment images can be controlled through additional\nintegrations with the [Open Policy Agent\nGatekeeper](https://www.openpolicyagent.org/docs/latest/kubernetes-introduction/)\nor\n[Kyverno](https://kyverno.io/policies/best-practices/restrict_image_registries/restrict_image_registries/).\nKyverno also allows you to [mutate the image registry\nlocation](https://kyverno.io/policies/other/replace_image_registry/replace_image_registry/),\nand redirect the pod image to trusted sources.\n\n\n[Operational container\nscanning](https://docs.gitlab.com/ee/user/clusters/agent/vulnerabilities.html)\nin Kubernetes clusters and [container scanning in CI/CD\npipelines](https://docs.gitlab.com/ee/user/application_security/container_scanning/)\nare recommended. This ensures that all images do not expose security\nvulnerabilities.\n\n\n## Long-term solutions\n\n\nAs a long-term solution, analyze the affected Docker Hub organizations\nimages and match them against your image usage inventory. Some organizations\nhave raised their concerns in [this Docker Hub feedback\nissue](https://github.com/docker/hub-feedback/issues/2314). Be sure to\nidentify critical production CI/CD workflows and replace all external\ndependencies with local maintained images.\n\n\nFork/copy project Dockerfile files from the upstream Git repositories, and\nuse them as the single source of truth for custom container builds. This\nwill also require training and documentation for DevSecOps teams, for\nexample optimizing container images for [efficient CI/CD\npipelines](https://docs.gitlab.com/ee/ci/pipelines/pipeline_efficiency.html).\nMore DevSecOps efficiency tips can be found in my Chemnitz Linux Days talk\nabout \"Efficient DevSecOps Pipelines in a Cloud Native World\"\n([slides](https://go.gitlab.com/RPog2h)).\n\n\n\u003Ciframe\nsrc=\"https://docs.google.com/presentation/d/e/2PACX-1vT3jcfpddKL2jq7leX01QX6S4Y8vfLLBZMz4L1ZHMLY3xzB4IGOOIExODLEzH8YQM1atCNPm07Bw9m_/embed?start=false&loop=true&delayms=3000\"\nframeborder=\"0\" width=\"960\" height=\"569\" allowfullscreen=\"true\"\nmozallowfullscreen=\"true\" webkitallowfullscreen=\"true\">\u003C/iframe>\n\n\nPlease share your ideas and thoughts about Docker Hub change mitigations and\ntools on the [GitLab community forum](https://forum.gitlab.com/). Thank you!\n\n\nCover image by [Roger Hoyles](https://unsplash.com/photos/sTOQyRD8m74) on\n[Unsplash](https://www.unsplash.com)\n\n{: .note}\n","engineering",[24,25,26],"CI","kubernetes","open source",{"slug":28,"featured":6,"template":29},"how-gitlab-can-help-mitigate-deletion-open-source-images-docker-hub","BlogPost","content:en-us:blog:how-gitlab-can-help-mitigate-deletion-open-source-images-docker-hub.yml","yaml","How Gitlab Can Help Mitigate Deletion Open Source Images Docker Hub","content","en-us/blog/how-gitlab-can-help-mitigate-deletion-open-source-images-docker-hub.yml","en-us/blog/how-gitlab-can-help-mitigate-deletion-open-source-images-docker-hub","yml",{"_path":38,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"data":40,"_id":461,"_type":31,"title":462,"_source":33,"_file":463,"_stem":464,"_extension":36},"/shared/en-us/main-navigation","en-us",{"logo":41,"freeTrial":46,"sales":51,"login":56,"items":61,"search":392,"minimal":423,"duo":442,"pricingDeployment":451},{"config":42},{"href":43,"dataGaName":44,"dataGaLocation":45},"/","gitlab logo","header",{"text":47,"config":48},"Get free trial",{"href":49,"dataGaName":50,"dataGaLocation":45},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":52,"config":53},"Talk to sales",{"href":54,"dataGaName":55,"dataGaLocation":45},"/sales/","sales",{"text":57,"config":58},"Sign in",{"href":59,"dataGaName":60,"dataGaLocation":45},"https://gitlab.com/users/sign_in/","sign in",[62,106,203,208,313,373],{"text":63,"config":64,"cards":66,"footer":89},"Platform",{"dataNavLevelOne":65},"platform",[67,73,81],{"title":63,"description":68,"link":69},"The most comprehensive AI-powered DevSecOps Platform",{"text":70,"config":71},"Explore our Platform",{"href":72,"dataGaName":65,"dataGaLocation":45},"/platform/",{"title":74,"description":75,"link":76},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":77,"config":78},"Meet GitLab Duo",{"href":79,"dataGaName":80,"dataGaLocation":45},"/gitlab-duo/","gitlab duo ai",{"title":82,"description":83,"link":84},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":85,"config":86},"Learn more",{"href":87,"dataGaName":88,"dataGaLocation":45},"/why-gitlab/","why gitlab",{"title":90,"items":91},"Get started with",[92,97,102],{"text":93,"config":94},"Platform Engineering",{"href":95,"dataGaName":96,"dataGaLocation":45},"/solutions/platform-engineering/","platform engineering",{"text":98,"config":99},"Developer Experience",{"href":100,"dataGaName":101,"dataGaLocation":45},"/developer-experience/","Developer experience",{"text":103,"config":104},"MLOps",{"href":105,"dataGaName":103,"dataGaLocation":45},"/topics/devops/the-role-of-ai-in-devops/",{"text":107,"left":108,"config":109,"link":111,"lists":115,"footer":185},"Product",true,{"dataNavLevelOne":110},"solutions",{"text":112,"config":113},"View all Solutions",{"href":114,"dataGaName":110,"dataGaLocation":45},"/solutions/",[116,141,164],{"title":117,"description":118,"link":119,"items":124},"Automation","CI/CD and automation to accelerate deployment",{"config":120},{"icon":121,"href":122,"dataGaName":123,"dataGaLocation":45},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[125,129,133,137],{"text":126,"config":127},"CI/CD",{"href":128,"dataGaLocation":45,"dataGaName":126},"/solutions/continuous-integration/",{"text":130,"config":131},"AI-Assisted Development",{"href":79,"dataGaLocation":45,"dataGaName":132},"AI assisted development",{"text":134,"config":135},"Source Code Management",{"href":136,"dataGaLocation":45,"dataGaName":134},"/solutions/source-code-management/",{"text":138,"config":139},"Automated Software Delivery",{"href":122,"dataGaLocation":45,"dataGaName":140},"Automated software delivery",{"title":142,"description":143,"link":144,"items":149},"Security","Deliver code faster without compromising security",{"config":145},{"href":146,"dataGaName":147,"dataGaLocation":45,"icon":148},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[150,154,159],{"text":151,"config":152},"Application Security Testing",{"href":146,"dataGaName":153,"dataGaLocation":45},"Application security testing",{"text":155,"config":156},"Software Supply Chain Security",{"href":157,"dataGaLocation":45,"dataGaName":158},"/solutions/supply-chain/","Software supply chain security",{"text":160,"config":161},"Software Compliance",{"href":162,"dataGaName":163,"dataGaLocation":45},"/solutions/software-compliance/","software compliance",{"title":165,"link":166,"items":171},"Measurement",{"config":167},{"icon":168,"href":169,"dataGaName":170,"dataGaLocation":45},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[172,176,180],{"text":173,"config":174},"Visibility & Measurement",{"href":169,"dataGaLocation":45,"dataGaName":175},"Visibility and Measurement",{"text":177,"config":178},"Value Stream Management",{"href":179,"dataGaLocation":45,"dataGaName":177},"/solutions/value-stream-management/",{"text":181,"config":182},"Analytics & Insights",{"href":183,"dataGaLocation":45,"dataGaName":184},"/solutions/analytics-and-insights/","Analytics and insights",{"title":186,"items":187},"GitLab for",[188,193,198],{"text":189,"config":190},"Enterprise",{"href":191,"dataGaLocation":45,"dataGaName":192},"/enterprise/","enterprise",{"text":194,"config":195},"Small Business",{"href":196,"dataGaLocation":45,"dataGaName":197},"/small-business/","small business",{"text":199,"config":200},"Public Sector",{"href":201,"dataGaLocation":45,"dataGaName":202},"/solutions/public-sector/","public sector",{"text":204,"config":205},"Pricing",{"href":206,"dataGaName":207,"dataGaLocation":45,"dataNavLevelOne":207},"/pricing/","pricing",{"text":209,"config":210,"link":212,"lists":216,"feature":300},"Resources",{"dataNavLevelOne":211},"resources",{"text":213,"config":214},"View all resources",{"href":215,"dataGaName":211,"dataGaLocation":45},"/resources/",[217,250,272],{"title":218,"items":219},"Getting started",[220,225,230,235,240,245],{"text":221,"config":222},"Install",{"href":223,"dataGaName":224,"dataGaLocation":45},"/install/","install",{"text":226,"config":227},"Quick start guides",{"href":228,"dataGaName":229,"dataGaLocation":45},"/get-started/","quick setup checklists",{"text":231,"config":232},"Learn",{"href":233,"dataGaLocation":45,"dataGaName":234},"https://university.gitlab.com/","learn",{"text":236,"config":237},"Product documentation",{"href":238,"dataGaName":239,"dataGaLocation":45},"https://docs.gitlab.com/","product documentation",{"text":241,"config":242},"Best practice videos",{"href":243,"dataGaName":244,"dataGaLocation":45},"/getting-started-videos/","best practice videos",{"text":246,"config":247},"Integrations",{"href":248,"dataGaName":249,"dataGaLocation":45},"/integrations/","integrations",{"title":251,"items":252},"Discover",[253,258,262,267],{"text":254,"config":255},"Customer success stories",{"href":256,"dataGaName":257,"dataGaLocation":45},"/customers/","customer success stories",{"text":259,"config":260},"Blog",{"href":261,"dataGaName":5,"dataGaLocation":45},"/blog/",{"text":263,"config":264},"Remote",{"href":265,"dataGaName":266,"dataGaLocation":45},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":268,"config":269},"TeamOps",{"href":270,"dataGaName":271,"dataGaLocation":45},"/teamops/","teamops",{"title":273,"items":274},"Connect",[275,280,285,290,295],{"text":276,"config":277},"GitLab Services",{"href":278,"dataGaName":279,"dataGaLocation":45},"/services/","services",{"text":281,"config":282},"Community",{"href":283,"dataGaName":284,"dataGaLocation":45},"/community/","community",{"text":286,"config":287},"Forum",{"href":288,"dataGaName":289,"dataGaLocation":45},"https://forum.gitlab.com/","forum",{"text":291,"config":292},"Events",{"href":293,"dataGaName":294,"dataGaLocation":45},"/events/","events",{"text":296,"config":297},"Partners",{"href":298,"dataGaName":299,"dataGaLocation":45},"/partners/","partners",{"backgroundColor":301,"textColor":302,"text":303,"image":304,"link":308},"#2f2a6b","#fff","Insights for the future of software development",{"altText":305,"config":306},"the source promo card",{"src":307},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":309,"config":310},"Read the latest",{"href":311,"dataGaName":312,"dataGaLocation":45},"/the-source/","the source",{"text":314,"config":315,"lists":317},"Company",{"dataNavLevelOne":316},"company",[318],{"items":319},[320,325,331,333,338,343,348,353,358,363,368],{"text":321,"config":322},"About",{"href":323,"dataGaName":324,"dataGaLocation":45},"/company/","about",{"text":326,"config":327,"footerGa":330},"Jobs",{"href":328,"dataGaName":329,"dataGaLocation":45},"/jobs/","jobs",{"dataGaName":329},{"text":291,"config":332},{"href":293,"dataGaName":294,"dataGaLocation":45},{"text":334,"config":335},"Leadership",{"href":336,"dataGaName":337,"dataGaLocation":45},"/company/team/e-group/","leadership",{"text":339,"config":340},"Team",{"href":341,"dataGaName":342,"dataGaLocation":45},"/company/team/","team",{"text":344,"config":345},"Handbook",{"href":346,"dataGaName":347,"dataGaLocation":45},"https://handbook.gitlab.com/","handbook",{"text":349,"config":350},"Investor relations",{"href":351,"dataGaName":352,"dataGaLocation":45},"https://ir.gitlab.com/","investor relations",{"text":354,"config":355},"Trust Center",{"href":356,"dataGaName":357,"dataGaLocation":45},"/security/","trust center",{"text":359,"config":360},"AI Transparency Center",{"href":361,"dataGaName":362,"dataGaLocation":45},"/ai-transparency-center/","ai transparency center",{"text":364,"config":365},"Newsletter",{"href":366,"dataGaName":367,"dataGaLocation":45},"/company/contact/","newsletter",{"text":369,"config":370},"Press",{"href":371,"dataGaName":372,"dataGaLocation":45},"/press/","press",{"text":374,"config":375,"lists":376},"Contact us",{"dataNavLevelOne":316},[377],{"items":378},[379,382,387],{"text":52,"config":380},{"href":54,"dataGaName":381,"dataGaLocation":45},"talk to sales",{"text":383,"config":384},"Support portal",{"href":385,"dataGaName":386,"dataGaLocation":45},"https://support.gitlab.com","support portal",{"text":388,"config":389},"Customer portal",{"href":390,"dataGaName":391,"dataGaLocation":45},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":393,"login":394,"suggestions":401},"Close",{"text":395,"link":396},"To search repositories and projects, login to",{"text":397,"config":398},"gitlab.com",{"href":59,"dataGaName":399,"dataGaLocation":400},"search login","search",{"text":402,"default":403},"Suggestions",[404,406,410,412,416,420],{"text":74,"config":405},{"href":79,"dataGaName":74,"dataGaLocation":400},{"text":407,"config":408},"Code Suggestions (AI)",{"href":409,"dataGaName":407,"dataGaLocation":400},"/solutions/code-suggestions/",{"text":126,"config":411},{"href":128,"dataGaName":126,"dataGaLocation":400},{"text":413,"config":414},"GitLab on AWS",{"href":415,"dataGaName":413,"dataGaLocation":400},"/partners/technology-partners/aws/",{"text":417,"config":418},"GitLab on Google Cloud",{"href":419,"dataGaName":417,"dataGaLocation":400},"/partners/technology-partners/google-cloud-platform/",{"text":421,"config":422},"Why GitLab?",{"href":87,"dataGaName":421,"dataGaLocation":400},{"freeTrial":424,"mobileIcon":429,"desktopIcon":434,"secondaryButton":437},{"text":425,"config":426},"Start free trial",{"href":427,"dataGaName":50,"dataGaLocation":428},"https://gitlab.com/-/trials/new/","nav",{"altText":430,"config":431},"Gitlab Icon",{"src":432,"dataGaName":433,"dataGaLocation":428},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":430,"config":435},{"src":436,"dataGaName":433,"dataGaLocation":428},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":438,"config":439},"Get Started",{"href":440,"dataGaName":441,"dataGaLocation":428},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":443,"mobileIcon":447,"desktopIcon":449},{"text":444,"config":445},"Learn more about GitLab Duo",{"href":79,"dataGaName":446,"dataGaLocation":428},"gitlab duo",{"altText":430,"config":448},{"src":432,"dataGaName":433,"dataGaLocation":428},{"altText":430,"config":450},{"src":436,"dataGaName":433,"dataGaLocation":428},{"freeTrial":452,"mobileIcon":457,"desktopIcon":459},{"text":453,"config":454},"Back to pricing",{"href":206,"dataGaName":455,"dataGaLocation":428,"icon":456},"back to pricing","GoBack",{"altText":430,"config":458},{"src":432,"dataGaName":433,"dataGaLocation":428},{"altText":430,"config":460},{"src":436,"dataGaName":433,"dataGaLocation":428},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":466,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"title":467,"button":468,"image":473,"config":477,"_id":479,"_type":31,"_source":33,"_file":480,"_stem":481,"_extension":36},"/shared/en-us/banner","is now in public beta!",{"text":469,"config":470},"Try the Beta",{"href":471,"dataGaName":472,"dataGaLocation":45},"/gitlab-duo/agent-platform/","duo banner",{"altText":474,"config":475},"GitLab Duo Agent Platform",{"src":476},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":478},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":483,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"data":484,"_id":723,"_type":31,"title":724,"_source":33,"_file":725,"_stem":726,"_extension":36},"/shared/en-us/main-footer",{"text":485,"source":486,"edit":492,"contribute":497,"config":502,"items":507,"minimal":715},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":487,"config":488},"View page source",{"href":489,"dataGaName":490,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":493,"config":494},"Edit this page",{"href":495,"dataGaName":496,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":498,"config":499},"Please contribute",{"href":500,"dataGaName":501,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":503,"facebook":504,"youtube":505,"linkedin":506},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[508,555,608,652,681],{"title":204,"links":509,"subMenu":524},[510,514,519],{"text":511,"config":512},"View plans",{"href":206,"dataGaName":513,"dataGaLocation":491},"view plans",{"text":515,"config":516},"Why Premium?",{"href":517,"dataGaName":518,"dataGaLocation":491},"/pricing/premium/","why premium",{"text":520,"config":521},"Why Ultimate?",{"href":522,"dataGaName":523,"dataGaLocation":491},"/pricing/ultimate/","why ultimate",[525],{"title":526,"links":527},"Contact Us",[528,531,533,535,540,545,550],{"text":529,"config":530},"Contact sales",{"href":54,"dataGaName":55,"dataGaLocation":491},{"text":383,"config":532},{"href":385,"dataGaName":386,"dataGaLocation":491},{"text":388,"config":534},{"href":390,"dataGaName":391,"dataGaLocation":491},{"text":536,"config":537},"Status",{"href":538,"dataGaName":539,"dataGaLocation":491},"https://status.gitlab.com/","status",{"text":541,"config":542},"Terms of use",{"href":543,"dataGaName":544,"dataGaLocation":491},"/terms/","terms of use",{"text":546,"config":547},"Privacy statement",{"href":548,"dataGaName":549,"dataGaLocation":491},"/privacy/","privacy statement",{"text":551,"config":552},"Cookie preferences",{"dataGaName":553,"dataGaLocation":491,"id":554,"isOneTrustButton":108},"cookie preferences","ot-sdk-btn",{"title":107,"links":556,"subMenu":564},[557,561],{"text":558,"config":559},"DevSecOps platform",{"href":72,"dataGaName":560,"dataGaLocation":491},"devsecops platform",{"text":130,"config":562},{"href":79,"dataGaName":563,"dataGaLocation":491},"ai-assisted development",[565],{"title":566,"links":567},"Topics",[568,573,578,583,588,593,598,603],{"text":569,"config":570},"CICD",{"href":571,"dataGaName":572,"dataGaLocation":491},"/topics/ci-cd/","cicd",{"text":574,"config":575},"GitOps",{"href":576,"dataGaName":577,"dataGaLocation":491},"/topics/gitops/","gitops",{"text":579,"config":580},"DevOps",{"href":581,"dataGaName":582,"dataGaLocation":491},"/topics/devops/","devops",{"text":584,"config":585},"Version Control",{"href":586,"dataGaName":587,"dataGaLocation":491},"/topics/version-control/","version control",{"text":589,"config":590},"DevSecOps",{"href":591,"dataGaName":592,"dataGaLocation":491},"/topics/devsecops/","devsecops",{"text":594,"config":595},"Cloud Native",{"href":596,"dataGaName":597,"dataGaLocation":491},"/topics/cloud-native/","cloud native",{"text":599,"config":600},"AI for Coding",{"href":601,"dataGaName":602,"dataGaLocation":491},"/topics/devops/ai-for-coding/","ai for coding",{"text":604,"config":605},"Agentic AI",{"href":606,"dataGaName":607,"dataGaLocation":491},"/topics/agentic-ai/","agentic ai",{"title":609,"links":610},"Solutions",[611,613,615,620,624,627,631,634,636,639,642,647],{"text":151,"config":612},{"href":146,"dataGaName":151,"dataGaLocation":491},{"text":140,"config":614},{"href":122,"dataGaName":123,"dataGaLocation":491},{"text":616,"config":617},"Agile development",{"href":618,"dataGaName":619,"dataGaLocation":491},"/solutions/agile-delivery/","agile delivery",{"text":621,"config":622},"SCM",{"href":136,"dataGaName":623,"dataGaLocation":491},"source code management",{"text":569,"config":625},{"href":128,"dataGaName":626,"dataGaLocation":491},"continuous integration & delivery",{"text":628,"config":629},"Value stream management",{"href":179,"dataGaName":630,"dataGaLocation":491},"value stream management",{"text":574,"config":632},{"href":633,"dataGaName":577,"dataGaLocation":491},"/solutions/gitops/",{"text":189,"config":635},{"href":191,"dataGaName":192,"dataGaLocation":491},{"text":637,"config":638},"Small business",{"href":196,"dataGaName":197,"dataGaLocation":491},{"text":640,"config":641},"Public sector",{"href":201,"dataGaName":202,"dataGaLocation":491},{"text":643,"config":644},"Education",{"href":645,"dataGaName":646,"dataGaLocation":491},"/solutions/education/","education",{"text":648,"config":649},"Financial services",{"href":650,"dataGaName":651,"dataGaLocation":491},"/solutions/finance/","financial services",{"title":209,"links":653},[654,656,658,660,663,665,667,669,671,673,675,677,679],{"text":221,"config":655},{"href":223,"dataGaName":224,"dataGaLocation":491},{"text":226,"config":657},{"href":228,"dataGaName":229,"dataGaLocation":491},{"text":231,"config":659},{"href":233,"dataGaName":234,"dataGaLocation":491},{"text":236,"config":661},{"href":238,"dataGaName":662,"dataGaLocation":491},"docs",{"text":259,"config":664},{"href":261,"dataGaName":5,"dataGaLocation":491},{"text":254,"config":666},{"href":256,"dataGaName":257,"dataGaLocation":491},{"text":263,"config":668},{"href":265,"dataGaName":266,"dataGaLocation":491},{"text":276,"config":670},{"href":278,"dataGaName":279,"dataGaLocation":491},{"text":268,"config":672},{"href":270,"dataGaName":271,"dataGaLocation":491},{"text":281,"config":674},{"href":283,"dataGaName":284,"dataGaLocation":491},{"text":286,"config":676},{"href":288,"dataGaName":289,"dataGaLocation":491},{"text":291,"config":678},{"href":293,"dataGaName":294,"dataGaLocation":491},{"text":296,"config":680},{"href":298,"dataGaName":299,"dataGaLocation":491},{"title":314,"links":682},[683,685,687,689,691,693,695,699,704,706,708,710],{"text":321,"config":684},{"href":323,"dataGaName":316,"dataGaLocation":491},{"text":326,"config":686},{"href":328,"dataGaName":329,"dataGaLocation":491},{"text":334,"config":688},{"href":336,"dataGaName":337,"dataGaLocation":491},{"text":339,"config":690},{"href":341,"dataGaName":342,"dataGaLocation":491},{"text":344,"config":692},{"href":346,"dataGaName":347,"dataGaLocation":491},{"text":349,"config":694},{"href":351,"dataGaName":352,"dataGaLocation":491},{"text":696,"config":697},"Sustainability",{"href":698,"dataGaName":696,"dataGaLocation":491},"/sustainability/",{"text":700,"config":701},"Diversity, inclusion and belonging (DIB)",{"href":702,"dataGaName":703,"dataGaLocation":491},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":354,"config":705},{"href":356,"dataGaName":357,"dataGaLocation":491},{"text":364,"config":707},{"href":366,"dataGaName":367,"dataGaLocation":491},{"text":369,"config":709},{"href":371,"dataGaName":372,"dataGaLocation":491},{"text":711,"config":712},"Modern Slavery Transparency Statement",{"href":713,"dataGaName":714,"dataGaLocation":491},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":716},[717,719,721],{"text":541,"config":718},{"href":543,"dataGaName":544,"dataGaLocation":491},{"text":546,"config":720},{"href":548,"dataGaName":549,"dataGaLocation":491},{"text":551,"config":722},{"dataGaName":553,"dataGaLocation":491,"id":554,"isOneTrustButton":108},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[728],{"_path":729,"_dir":730,"_draft":6,"_partial":6,"_locale":7,"content":731,"config":735,"_id":737,"_type":31,"title":19,"_source":33,"_file":738,"_stem":739,"_extension":36},"/en-us/blog/authors/michael-friedrich","authors",{"name":19,"config":732},{"headshot":733,"ctfId":734},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659879/Blog/Author%20Headshots/dnsmichi-headshot.jpg","dnsmichi",{"template":736},"BlogAuthor","content:en-us:blog:authors:michael-friedrich.yml","en-us/blog/authors/michael-friedrich.yml","en-us/blog/authors/michael-friedrich",{"_path":741,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"header":742,"eyebrow":743,"blurb":744,"button":745,"secondaryButton":749,"_id":751,"_type":31,"title":752,"_source":33,"_file":753,"_stem":754,"_extension":36},"/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":47,"config":746},{"href":747,"dataGaName":50,"dataGaLocation":748},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":52,"config":750},{"href":54,"dataGaName":55,"dataGaLocation":748},"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":756,"content":757,"config":760,"_id":30,"_type":31,"title":32,"_source":33,"_file":34,"_stem":35,"_extension":36},{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},{"title":17,"description":10,"authors":758,"heroImage":11,"date":20,"body":21,"category":22,"tags":759},[19],[24,25,26],{"slug":28,"featured":6,"template":29},1761814420070]