[{"data":1,"prerenderedAt":1764},["ShallowReactive",2],{"/blog/dependency-proxy-updates":3,"navigation-en-us":980,"banner-en-us":1407,"footer-en-us":1417,"blog-post-authors-en-us-Steve Abrams":1661,"blog-related-posts-en-us-dependency-proxy-updates":1676,"blog-promotions-en-us":1700,"next-steps-en-us":1754},{"id":4,"title":5,"authors":6,"body":8,"category":961,"date":962,"description":963,"extension":964,"externalUrl":965,"faq":965,"featured":966,"heroImage":967,"meta":968,"navigation":180,"path":969,"seo":970,"slug":974,"stem":975,"tags":976,"template":978,"updatedDate":965,"__hash__":979},"blogPosts/en-us/blog/dependency-proxy-updates.md","Using the Dependency Proxy to improve your pipelines",[7],"Steve Abrams",{"type":9,"value":10,"toc":950},"minimark",[11,15,31,34,37,50,55,64,68,71,78,82,89,92,97,100,139,142,159,162,209,216,220,229,232,252,255,262,265,281,357,360,514,517,520,526,529,543,546,550,553,556,563,597,603,693,709,763,766,834,837,919,923,926,934,938,946],[12,13,14],"p",{},"Hi! I'm Steve, a backend engineer at GitLab. I work on the Package stage, which includes the Dependency Proxy.",[12,16,17,18,24,25,30],{},"In versions 13.6 and 13.7, we improved the ",[19,20,23],"a",{"href":21,"rel":22},"https://docs.gitlab.com/user/packages/dependency_proxy/",[],"Dependency Proxy"," so it's no longer an ",[19,26,29],{"href":27,"rel":28},"https://handbook.gitlab.com/handbook/product/product-principles/#the-minimal-viable-change-mvc",[],"MVC feature",". Before, the Dependency Proxy was only available to paid users who may have been wary to use it, because they did not want to be forced to use a public group. Now the Dependency Proxy is a robust free feature that can really provide value for free and paid users alike.",[12,32,33],{},"If you haven't tried the feature out before, now is a great time to take a look. If you have previously tried the Dependency Proxy and found it was not quite the solution you were looking for, I invite you to take a look at the new functionality detailed here. The Dependency Proxy is more available, more secure, and easier to use than ever. These updates also come right as Docker Hub has rolled out rate limits on image pulls, which the Dependency Proxy can help alleviate.",[12,35,36],{},"You can also watch a demo of most of these features in this video:",[38,39,42,43],"figure",{"className":40},[41],"video_container","\n  ",[44,45,49],"iframe",{"src":46,"frameBorder":47,"allowFullScreen":48},"https://www.youtube.com/embed/Nc4nUo7Pq08","0","true"," ",[51,52,54],"h2",{"id":53},"move-to-core","Move to Core",[12,56,57,58,63],{},"In 13.6, we moved the ",[19,59,62],{"href":60,"rel":61},"https://docs.gitlab.com/releases/#the-dependency-proxy-is-now-open-source",[],"Dependency Proxy to Core",". The ability to speed up pipelines and create a safety net behind Docker Hub seemed like functionality that everyone should benefit from.",[51,65,67],{"id":66},"support-for-private-groups","Support for private groups",[12,69,70],{},"Starting in 13.7, you can now use the Dependency Proxy with all groups. Each group and subgroup can have its own space to cache images.",[12,72,73],{},[74,75],"img",{"alt":76,"src":77},"Dependency Proxy interface","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782398162/blog/Content%20Images/dependency_proxy_interface.png",[51,79,81],{"id":80},"authentication","Authentication",[12,83,84,88],{},[19,85,81],{"href":86,"rel":87},"https://docs.gitlab.com/user/packages/dependency_proxy/#authenticate-with-the-dependency-proxy",[]," is also new in 13.7. If you had previously used the Dependency Proxy, you will need to update your CI scripts or workflow to make sure that you are now logged in.",[12,90,91],{},"Authentication was not only necessary to enable the ability to support private groups with the Dependency Proxy, but it's also a security upgrade. The Dependency Proxy caches image data in your group's storage, so without authentication, public groups could easily be abused to store images that your group might not even be using.",[93,94,96],"h3",{"id":95},"how-does-it-work","How does it work",[12,98,99],{},"The Dependency Proxy is a proxy, so from the perspective of the Docker client, it is just another registry to authenticate with:",[101,102,107],"pre",{"className":103,"code":104,"language":105,"meta":106,"style":106},"language-shell shiki shiki-themes github-light","docker login --username stanley --password tanuki gitlab.com\n","shell","",[108,109,110],"code",{"__ignoreMap":106},[111,112,115,119,123,127,130,133,136],"span",{"class":113,"line":114},"line",1,[111,116,118],{"class":117},"s7eDp","docker",[111,120,122],{"class":121},"sYBdl"," login",[111,124,126],{"class":125},"sYu0t"," --username",[111,128,129],{"class":121}," stanley",[111,131,132],{"class":125}," --password",[111,134,135],{"class":121}," tanuki",[111,137,138],{"class":121}," gitlab.com\n",[12,140,141],{},"When Docker makes a request to a registry it first asks:",[101,143,145],{"className":103,"code":144,"language":105,"meta":106,"style":106},"GET gitlab.com/v2 # are you a registry?\n",[108,146,147],{"__ignoreMap":106},[111,148,149,152,155],{"class":113,"line":114},[111,150,151],{"class":117},"GET",[111,153,154],{"class":121}," gitlab.com/v2",[111,156,158],{"class":157},"sAwPA"," # are you a registry?\n",[12,160,161],{},"To which GitLab responds:",[101,163,165],{"className":103,"code":164,"language":105,"meta":106,"style":106},"401 Unauthorized\n\nWWW-Authenticate: Bearer realm=https://gitlab.com/auth/jwt, service=dependency_proxy\n# Yes! But you have to get permission to access me.\n# Please request a token from this other URL first.\n",[108,166,167,175,182,197,203],{"__ignoreMap":106},[111,168,169,172],{"class":113,"line":114},[111,170,171],{"class":117},"401",[111,173,174],{"class":121}," Unauthorized\n",[111,176,178],{"class":113,"line":177},2,[111,179,181],{"emptyLinePlaceholder":180},true,"\n",[111,183,185,188,191,194],{"class":113,"line":184},3,[111,186,187],{"class":117},"WWW-Authenticate:",[111,189,190],{"class":121}," Bearer",[111,192,193],{"class":121}," realm=https://gitlab.com/auth/jwt,",[111,195,196],{"class":121}," service=dependency_proxy\n",[111,198,200],{"class":113,"line":199},4,[111,201,202],{"class":157},"# Yes! But you have to get permission to access me.\n",[111,204,206],{"class":113,"line":205},5,[111,207,208],{"class":157},"# Please request a token from this other URL first.\n",[12,210,211,212,215],{},"Then Docker requests a token using the username and password you supplied, and if things check out, GitLab returns a JWT. Docker uses it to make its next request, which in the case of the Dependency Proxy is the image pull. If things don't check out, you'll likely see a ",[108,213,214],{},"403 Forbidden"," error.",[51,217,219],{"id":218},"docker-hub-rate-limiting","Docker Hub rate limiting",[12,221,222,223,228],{},"In November 2020, Docker Hub began rate limiting image pulls. The Dependency Proxy was already caching the image layers (blobs), so it made sense that ",[19,224,227],{"href":225,"rel":226},"https://docs.gitlab.com/user/packages/dependency_proxy/#docker-hub-rate-limits-and-the-dependency-proxy",[],"the Dependency Proxy should help mitigate this problem for users",".",[12,230,231],{},"It is not uncommon for a project's pipeline to run every time a user pushes a commit. In an active project or group, this could happen many times in an hour. If your CI script starts with something as simple as:",[101,233,237],{"className":234,"code":235,"language":236,"meta":106,"style":106},"language-yaml shiki shiki-themes github-light","image: node:latest\n","yaml",[108,238,239],{"__ignoreMap":106},[111,240,241,245,249],{"class":113,"line":114},[111,242,244],{"class":243},"shJU0","image",[111,246,248],{"class":247},"sgsFI",": ",[111,250,251],{"class":121},"node:latest\n",[12,253,254],{},"Every time your pipeline runs, even though you are using the same image every time, Docker will count an additional image pull against your account.",[12,256,257,258,261],{},"An image consists of many different files, and a ",[108,259,260],{},"docker pull"," command will make several requests. So what counts as one image pull?",[12,263,264],{},"There are two types of files that make up an image. First is the manifest. You can think of it as a table of contents for an image. It contains information about what layers, or blobs, the image is made of. Once the Docker client has received the manifest, it will make a request for each blob described in the manifest.",[12,266,267,268,273,274,277,278,280],{},"Docker uses the ",[19,269,272],{"href":270,"rel":271},"https://docs.docker.com/docker-hub/download-rate-limit/",[],"manifest requests to count the image pulls",". This means that if the Dependency Proxy is going to help mitigate the rate limiting, it needs to store the manifest in addition to the blobs. This presents a small problem: a manifest is usually requested by tag name, which is a mutable reference. If I request ",[108,275,276],{},"node:latest"," this week, it might be different than the ",[108,279,276],{}," I requested last week. Each manifest contains a digest, or hash signature, that can be used to tell if it has changed. You can see this digest when you pull the image:",[101,282,284],{"className":103,"code":283,"language":105,"meta":106,"style":106},"$ docker pull alpine:latest\n\nlatest: Pulling from library/alpine\nDigest: sha256:a126728cb7db157f0deb377bcba3c5e473e612d7bafc27f6bb4e5e083f9f08c2\nStatus: Image is up to date for alpine:latest\ndocker.io/library/alpine:latest\n",[108,285,286,300,304,318,326,351],{"__ignoreMap":106},[111,287,288,291,294,297],{"class":113,"line":114},[111,289,290],{"class":117},"$",[111,292,293],{"class":121}," docker",[111,295,296],{"class":121}," pull",[111,298,299],{"class":121}," alpine:latest\n",[111,301,302],{"class":113,"line":177},[111,303,181],{"emptyLinePlaceholder":180},[111,305,306,309,312,315],{"class":113,"line":184},[111,307,308],{"class":117},"latest:",[111,310,311],{"class":121}," Pulling",[111,313,314],{"class":121}," from",[111,316,317],{"class":121}," library/alpine\n",[111,319,320,323],{"class":113,"line":199},[111,321,322],{"class":117},"Digest:",[111,324,325],{"class":121}," sha256:a126728cb7db157f0deb377bcba3c5e473e612d7bafc27f6bb4e5e083f9f08c2\n",[111,327,328,331,334,337,340,343,346,349],{"class":113,"line":205},[111,329,330],{"class":117},"Status:",[111,332,333],{"class":121}," Image",[111,335,336],{"class":121}," is",[111,338,339],{"class":121}," up",[111,341,342],{"class":121}," to",[111,344,345],{"class":121}," date",[111,347,348],{"class":121}," for",[111,350,299],{"class":121},[111,352,354],{"class":113,"line":353},6,[111,355,356],{"class":117},"docker.io/library/alpine:latest\n",[12,358,359],{},"Docker has allowed HEAD requests to be made for a manifest for free. The HEAD request response contains the digest of the underlying manifest. So we can make a HEAD request to determine if the manifest we have cached in the Dependency Proxy is up to date.",[101,361,363],{"className":103,"code":362,"language":105,"meta":106,"style":106},"curl --head -H \"Authorization: Bearer $TOKEN\" https://registry-1.docker.io/v2/library/alpine/manifests/latest\n\nHTTP/1.1 200 OK\nContent-Length: 2782\nContent-Type: application/vnd.docker.distribution.manifest.v1+prettyjws\nDocker-Content-Digest: sha256:a126728cb7db157f0deb377bcba3c5e473e612d7bafc27f6bb4e5e083f9f08c2\nDocker-Distribution-Api-Version: registry/2.0\nEtag: \"sha256:a126728cb7db157f0deb377bcba3c5e473e612d7bafc27f6bb4e5e083f9f08c2\"\nDate: Wed, 15 Dec 2020 03:34:24 GMT\nStrict-Transport-Security: max-age=31536000\nRateLimit-Limit: 100;w=21600\nRateLimit-Remaining: 72;w=21600\n",[108,364,365,388,392,403,411,419,426,435,444,468,480,499],{"__ignoreMap":106},[111,366,367,370,373,376,379,382,385],{"class":113,"line":114},[111,368,369],{"class":117},"curl",[111,371,372],{"class":125}," --head",[111,374,375],{"class":125}," -H",[111,377,378],{"class":121}," \"Authorization: Bearer ",[111,380,381],{"class":247},"$TOKEN",[111,383,384],{"class":121},"\"",[111,386,387],{"class":121}," https://registry-1.docker.io/v2/library/alpine/manifests/latest\n",[111,389,390],{"class":113,"line":177},[111,391,181],{"emptyLinePlaceholder":180},[111,393,394,397,400],{"class":113,"line":184},[111,395,396],{"class":117},"HTTP/1.1",[111,398,399],{"class":125}," 200",[111,401,402],{"class":121}," OK\n",[111,404,405,408],{"class":113,"line":199},[111,406,407],{"class":117},"Content-Length:",[111,409,410],{"class":125}," 2782\n",[111,412,413,416],{"class":113,"line":205},[111,414,415],{"class":117},"Content-Type:",[111,417,418],{"class":121}," application/vnd.docker.distribution.manifest.v1+prettyjws\n",[111,420,421,424],{"class":113,"line":353},[111,422,423],{"class":117},"Docker-Content-Digest:",[111,425,325],{"class":121},[111,427,429,432],{"class":113,"line":428},7,[111,430,431],{"class":117},"Docker-Distribution-Api-Version:",[111,433,434],{"class":121}," registry/2.0\n",[111,436,438,441],{"class":113,"line":437},8,[111,439,440],{"class":117},"Etag:",[111,442,443],{"class":121}," \"sha256:a126728cb7db157f0deb377bcba3c5e473e612d7bafc27f6bb4e5e083f9f08c2\"\n",[111,445,447,450,453,456,459,462,465],{"class":113,"line":446},9,[111,448,449],{"class":117},"Date:",[111,451,452],{"class":121}," Wed,",[111,454,455],{"class":125}," 15",[111,457,458],{"class":121}," Dec",[111,460,461],{"class":125}," 2020",[111,463,464],{"class":121}," 03:34:24",[111,466,467],{"class":121}," GMT\n",[111,469,471,474,477],{"class":113,"line":470},10,[111,472,473],{"class":117},"Strict-Transport-Security:",[111,475,476],{"class":121}," max-age=",[111,478,479],{"class":125},"31536000\n",[111,481,483,486,489,492,496],{"class":113,"line":482},11,[111,484,485],{"class":117},"RateLimit-Limit:",[111,487,488],{"class":125}," 100",[111,490,491],{"class":247},";w",[111,493,495],{"class":494},"sD7c4","=",[111,497,498],{"class":121},"21600\n",[111,500,502,505,508,510,512],{"class":113,"line":501},12,[111,503,504],{"class":117},"RateLimit-Remaining:",[111,506,507],{"class":125}," 72",[111,509,491],{"class":247},[111,511,495],{"class":494},[111,513,498],{"class":121},[12,515,516],{},"The response even contains information telling us how many requests we have remaining within our rate limit. In this example, we see we have 72 out of 100 remaining.",[12,518,519],{},"When the Dependency Proxy first receives a request for the manifest, it decides whether or not it needs to pull an image from Docker Hub based on a few rules:",[12,521,522],{},[74,523],{"alt":524,"src":525},"Dependency Proxy manifest caching","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782398162/blog/Content%20Images/dependency_proxy_flow_chart.png",[12,527,528],{},"The really great thing about the Dependency Proxy is that you don't have to do anything special to take advantage of these abilities. If you simply update your CI script with your Dependency Proxy image prefix to something like:",[101,530,532],{"className":234,"code":531,"language":236,"meta":106,"style":106},"image: gitlab.com/super-awesome-group/dependency_proxy/containers/node:latest\n",[108,533,534],{"__ignoreMap":106},[111,535,536,538,540],{"class":113,"line":114},[111,537,244],{"class":243},[111,539,248],{"class":247},[111,541,542],{"class":121},"gitlab.com/super-awesome-group/dependency_proxy/containers/node:latest\n",[12,544,545],{},"Then you will automatically bypass Docker Hub rate limiting and your cache will have the most up-to-date version of each image tag.",[51,547,549],{"id":548},"cicd","CI/CD",[12,551,552],{},"The Dependency Proxy makes the most sense as a compliment to CI/CD pipelines. Rather than pulling directly from Docker Hub, you can use the Dependency Proxy to speed up your pipelines, avoid rate limiting, and create security in case of an upstream outage.",[12,554,555],{},"As of 13.9, runners log in to the Dependency Proxy automatically, so you don't need to explicitly log in unless you want to for reasons like using specific tokens.",[12,557,558,559,562],{},"To make the Dependency Proxy easier to use, we have added a few predefined environment variables you can use in your ",[108,560,561],{},".gitlab-ci.yml"," files.",[564,565,566,573,579,585,591],"ul",{},[567,568,569,572],"li",{},[108,570,571],{},"CI_DEPENDENCY_PROXY_USER",": A CI user for logging in to the Dependency Proxy.",[567,574,575,578],{},[108,576,577],{},"CI_DEPENDENCY_PROXY_PASSWORD",": A CI password for logging in to the Dependency Proxy.",[567,580,581,584],{},[108,582,583],{},"CI_DEPENDENCY_PROXY_SERVER",": The server for logging in to the Dependency Proxy.",[567,586,587,590],{},[108,588,589],{},"CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX",": The image prefix for pulling images through the Dependency Proxy. This pulls through the top-level group.",[567,592,593,596],{},[108,594,595],{},"CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX"," (starting in version 14.3): An alternative image prefix for pulling images through the Dependency Proxy. This pulls through the subgroup, or direct group the project exists in.",[12,598,599,600,602],{},"Depending on how your scripts and pipelines look you can use these variables in a variety of ways. If you are manually pulling images in the script using ",[108,601,260],{},", you can log in and pull like this:",[101,604,606],{"className":234,"code":605,"language":236,"meta":106,"style":106},"# .gitlab-ci.yml\n\ndependency-proxy-pull-master:\n  # Official docker image.\n  image: docker:latest\n  stage: build\n  services:\n    - docker:dind\n  before_script:\n    - docker login -u \"$CI_DEPENDENCY_PROXY_USER\" -p \"$CI_DEPENDENCY_PROXY_PASSWORD\" \"$CI_DEPENDENCY_PROXY_SERVER\"\n  script:\n    - docker pull \"$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX\"/alpine:latest\n\n",[108,607,608,613,617,625,630,640,650,657,665,672,679,686],{"__ignoreMap":106},[111,609,610],{"class":113,"line":114},[111,611,612],{"class":157},"# .gitlab-ci.yml\n",[111,614,615],{"class":113,"line":177},[111,616,181],{"emptyLinePlaceholder":180},[111,618,619,622],{"class":113,"line":184},[111,620,621],{"class":243},"dependency-proxy-pull-master",[111,623,624],{"class":247},":\n",[111,626,627],{"class":113,"line":199},[111,628,629],{"class":157},"  # Official docker image.\n",[111,631,632,635,637],{"class":113,"line":205},[111,633,634],{"class":243},"  image",[111,636,248],{"class":247},[111,638,639],{"class":121},"docker:latest\n",[111,641,642,645,647],{"class":113,"line":353},[111,643,644],{"class":243},"  stage",[111,646,248],{"class":247},[111,648,649],{"class":121},"build\n",[111,651,652,655],{"class":113,"line":428},[111,653,654],{"class":243},"  services",[111,656,624],{"class":247},[111,658,659,662],{"class":113,"line":437},[111,660,661],{"class":247},"    - ",[111,663,664],{"class":121},"docker:dind\n",[111,666,667,670],{"class":113,"line":446},[111,668,669],{"class":243},"  before_script",[111,671,624],{"class":247},[111,673,674,676],{"class":113,"line":470},[111,675,661],{"class":247},[111,677,678],{"class":121},"docker login -u \"$CI_DEPENDENCY_PROXY_USER\" -p \"$CI_DEPENDENCY_PROXY_PASSWORD\" \"$CI_DEPENDENCY_PROXY_SERVER\"\n",[111,680,681,684],{"class":113,"line":482},[111,682,683],{"class":243},"  script",[111,685,624],{"class":247},[111,687,688,690],{"class":113,"line":501},[111,689,661],{"class":247},[111,691,692],{"class":121},"docker pull \"$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX\"/alpine:latest\n",[12,694,695,696,698,699,704,705,708],{},"If you want to use the Dependency Proxy to pull images defined as ",[108,697,244],{}," yaml attributes (the base images of the script), you can ",[19,700,703],{"href":701,"rel":702},"https://docs.gitlab.com/ci/variables/#custom-cicd-variables",[],"create a custom environment variable"," named ",[108,706,707],{},"DOCKER_AUTH_CONFIG"," with a value of:",[101,710,712],{"className":234,"code":711,"language":236,"meta":106,"style":106},"{\n    \"auths\": {\n        \"https://gitlab.com:443\": { # if you are using $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX, you should explicitely include the port here.\n            \"auth\": \"(Base64 of username:password)\"\n        }\n    }\n}\n",[108,713,714,719,727,738,748,753,758],{"__ignoreMap":106},[111,715,716],{"class":113,"line":114},[111,717,718],{"class":247},"{\n",[111,720,721,724],{"class":113,"line":177},[111,722,723],{"class":121},"    \"auths\"",[111,725,726],{"class":247},": {\n",[111,728,729,732,735],{"class":113,"line":184},[111,730,731],{"class":121},"        \"https://gitlab.com:443\"",[111,733,734],{"class":247},": { ",[111,736,737],{"class":157},"# if you are using $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX, you should explicitely include the port here.\n",[111,739,740,743,745],{"class":113,"line":199},[111,741,742],{"class":121},"            \"auth\"",[111,744,248],{"class":247},[111,746,747],{"class":121},"\"(Base64 of username:password)\"\n",[111,749,750],{"class":113,"line":205},[111,751,752],{"class":247},"        }\n",[111,754,755],{"class":113,"line":353},[111,756,757],{"class":247},"    }\n",[111,759,760],{"class":113,"line":428},[111,761,762],{"class":247},"}\n",[12,764,765],{},"You will need to calculate the base64 value of your credentials. You can do this from the command line:",[101,767,769],{"className":103,"code":768,"language":105,"meta":106,"style":106},"# The use of \"-n\" - prevents encoding a newline in the password.\necho -n \"my_username:my_password\" | base64\n\n# Example output to copy\nbXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ==\n\n# A personal access token also works!\necho -n \"my_username:personal_access_token\" | base64\n",[108,770,771,776,793,797,802,812,816,821],{"__ignoreMap":106},[111,772,773],{"class":113,"line":114},[111,774,775],{"class":157},"# The use of \"-n\" - prevents encoding a newline in the password.\n",[111,777,778,781,784,787,790],{"class":113,"line":177},[111,779,780],{"class":125},"echo",[111,782,783],{"class":125}," -n",[111,785,786],{"class":121}," \"my_username:my_password\"",[111,788,789],{"class":494}," |",[111,791,792],{"class":117}," base64\n",[111,794,795],{"class":113,"line":184},[111,796,181],{"emptyLinePlaceholder":180},[111,798,799],{"class":113,"line":199},[111,800,801],{"class":157},"# Example output to copy\n",[111,803,804,807,809],{"class":113,"line":205},[111,805,806],{"class":247},"bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ",[111,808,495],{"class":494},[111,810,811],{"class":121},"=\n",[111,813,814],{"class":113,"line":353},[111,815,181],{"emptyLinePlaceholder":180},[111,817,818],{"class":113,"line":428},[111,819,820],{"class":157},"# A personal access token also works!\n",[111,822,823,825,827,830,832],{"class":113,"line":437},[111,824,780],{"class":125},[111,826,783],{"class":125},[111,828,829],{"class":121}," \"my_username:personal_access_token\"",[111,831,789],{"class":494},[111,833,792],{"class":117},[12,835,836],{},"Once you have the custom environment variable defined, you can use the Dependency Proxy without having to manually log in within your CI script:",[101,838,840],{"className":234,"code":839,"language":236,"meta":106,"style":106},"# This is a working script that would publish an NPM package to the GitLab package registry\n# if a properly formatted package.json file exists in the project root.\nimage: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:latest\n\nstages:\n  - deploy\n\ndeploy:\n  stage: deploy\n  script:\n    - echo \"//gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}\">.npmrc\n    - npm publish\n\n",[108,841,842,847,852,861,865,872,880,884,891,899,905,912],{"__ignoreMap":106},[111,843,844],{"class":113,"line":114},[111,845,846],{"class":157},"# This is a working script that would publish an NPM package to the GitLab package registry\n",[111,848,849],{"class":113,"line":177},[111,850,851],{"class":157},"# if a properly formatted package.json file exists in the project root.\n",[111,853,854,856,858],{"class":113,"line":184},[111,855,244],{"class":243},[111,857,248],{"class":247},[111,859,860],{"class":121},"${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:latest\n",[111,862,863],{"class":113,"line":199},[111,864,181],{"emptyLinePlaceholder":180},[111,866,867,870],{"class":113,"line":205},[111,868,869],{"class":243},"stages",[111,871,624],{"class":247},[111,873,874,877],{"class":113,"line":353},[111,875,876],{"class":247},"  - ",[111,878,879],{"class":121},"deploy\n",[111,881,882],{"class":113,"line":428},[111,883,181],{"emptyLinePlaceholder":180},[111,885,886,889],{"class":113,"line":437},[111,887,888],{"class":243},"deploy",[111,890,624],{"class":247},[111,892,893,895,897],{"class":113,"line":446},[111,894,644],{"class":243},[111,896,248],{"class":247},[111,898,879],{"class":121},[111,900,901,903],{"class":113,"line":470},[111,902,683],{"class":243},[111,904,624],{"class":247},[111,906,907,909],{"class":113,"line":482},[111,908,661],{"class":247},[111,910,911],{"class":121},"echo \"//gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}\">.npmrc\n",[111,913,914,916],{"class":113,"line":501},[111,915,661],{"class":247},[111,917,918],{"class":121},"npm publish\n",[51,920,922],{"id":921},"support-when-docker-hub-is-offline","Support when Docker Hub is offline",[12,924,925],{},"By caching all files that make up an image, we also now have the ability to keep pipelines green even if Docker Hub experiences an outage. As long as the Dependency Proxy has the image you are using cached, when it makes the HEAD request to check if the cached image is stale or not, if the HEAD request fails, we will just fall back to the cached image.",[12,927,928,929,933],{},"Thanks for reading! If you haven't used the Dependency Proxy yet, ",[19,930,932],{"href":21,"rel":931},[],"get started using it today","!",[51,935,937],{"id":936},"updates","Updates",[12,939,940,941,945],{},"Since this was published in December 2020, there have been many additional improvements and changes to the Dependency Proxy. As a result, some of the suggested approaches in this post have been improved or have become outdated. I suggest looking through ",[19,942,944],{"href":21,"rel":943},[],"the most recent documentation"," to learn more.",[947,948,949],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}",{"title":106,"searchDepth":177,"depth":177,"links":951},[952,953,954,957,958,959,960],{"id":53,"depth":177,"text":54},{"id":66,"depth":177,"text":67},{"id":80,"depth":177,"text":81,"children":955},[956],{"id":95,"depth":184,"text":96},{"id":218,"depth":177,"text":219},{"id":548,"depth":177,"text":549},{"id":921,"depth":177,"text":922},{"id":936,"depth":177,"text":937},"unfiltered","2020-12-15","The Dependency Proxy helps make pipelines faster and mitigates Docker Hub rate limits.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749681815/Blog/Hero%20Images/dependency_proxy_header.jpg",{},"/en-us/blog/dependency-proxy-updates",{"title":5,"description":963,"ogTitle":5,"ogDescription":963,"noIndex":966,"ogImage":967,"ogUrl":971,"ogSiteName":972,"ogType":973,"canonicalUrls":971},"https://about.gitlab.com/blog/dependency-proxy-updates","https://about.gitlab.com","article","dependency-proxy-updates","en-us/blog/dependency-proxy-updates",[549,977],"features","BlogPost","IPv2NlPs1SWqJPaJdDvGfHf4RfutewK2Sa_B7DhTwDg",{"logo":981,"freeTrial":986,"sales":991,"login":996,"items":1001,"search":1327,"minimal":1358,"duo":1377,"switchNav":1386,"pricingDeployment":1397},{"config":982},{"href":983,"dataGaName":984,"dataGaLocation":985},"/","gitlab logo","header",{"text":987,"config":988},"Get free trial",{"href":989,"dataGaName":990,"dataGaLocation":985},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":992,"config":993},"Request a demo",{"href":994,"dataGaName":995,"dataGaLocation":985},"/sales/?contact-topic=request-demo","sales",{"text":997,"config":998},"Sign in",{"href":999,"dataGaName":1000,"dataGaLocation":985},"https://gitlab.com/users/sign_in/","sign in",[1002,1031,1130,1135,1249,1305],{"text":1003,"config":1004,"menu":1006},"Platform",{"dataNavLevelOne":1005},"platform",{"type":1007,"columns":1008},"cards",[1009,1015,1023],{"title":1003,"description":1010,"link":1011},"The intelligent orchestration platform for DevSecOps",{"text":1012,"config":1013},"Explore our Platform",{"href":1014,"dataGaName":1005,"dataGaLocation":985},"/platform/",{"title":1016,"description":1017,"link":1018},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":1019,"config":1020},"Meet GitLab Duo",{"href":1021,"dataGaName":1022,"dataGaLocation":985},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":1024,"description":1025,"link":1026},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":1027,"config":1028},"Learn more",{"href":1029,"dataGaName":1030,"dataGaLocation":985},"/why-gitlab/","why gitlab",{"text":1032,"left":180,"config":1033,"menu":1035},"Product",{"dataNavLevelOne":1034},"solutions",{"type":1036,"link":1037,"columns":1041,"feature":1109},"lists",{"text":1038,"config":1039},"View all Solutions",{"href":1040,"dataGaName":1034,"dataGaLocation":985},"/solutions/",[1042,1065,1088],{"title":1043,"description":1044,"link":1045,"items":1050},"Automation","CI/CD and automation to accelerate deployment",{"config":1046},{"icon":1047,"href":1048,"dataGaName":1049,"dataGaLocation":985},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[1051,1054,1057,1061],{"text":549,"config":1052},{"href":1053,"dataGaLocation":985,"dataGaName":549},"/solutions/continuous-integration/",{"text":1016,"config":1055},{"href":1021,"dataGaLocation":985,"dataGaName":1056},"gitlab duo agent platform - product menu",{"text":1058,"config":1059},"Source Code Management",{"href":1060,"dataGaLocation":985,"dataGaName":1058},"/solutions/source-code-management/",{"text":1062,"config":1063},"Automated Software Delivery",{"href":1048,"dataGaLocation":985,"dataGaName":1064},"Automated software delivery",{"title":1066,"description":1067,"link":1068,"items":1073},"Security","Deliver code faster without compromising security",{"config":1069},{"href":1070,"dataGaName":1071,"dataGaLocation":985,"icon":1072},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[1074,1078,1083],{"text":1075,"config":1076},"Application Security Testing",{"href":1070,"dataGaName":1077,"dataGaLocation":985},"Application security testing",{"text":1079,"config":1080},"Software Supply Chain Security",{"href":1081,"dataGaLocation":985,"dataGaName":1082},"/solutions/supply-chain/","Software supply chain security",{"text":1084,"config":1085},"Software Compliance",{"href":1086,"dataGaName":1087,"dataGaLocation":985},"/solutions/software-compliance/","software compliance",{"title":1089,"link":1090,"items":1095},"Measurement",{"config":1091},{"icon":1092,"href":1093,"dataGaName":1094,"dataGaLocation":985},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[1096,1100,1104],{"text":1097,"config":1098},"Visibility & Measurement",{"href":1093,"dataGaLocation":985,"dataGaName":1099},"Visibility and Measurement",{"text":1101,"config":1102},"Value Stream Management",{"href":1103,"dataGaLocation":985,"dataGaName":1101},"/solutions/value-stream-management/",{"text":1105,"config":1106},"Analytics & Insights",{"href":1107,"dataGaLocation":985,"dataGaName":1108},"/solutions/analytics-and-insights/","Analytics and insights",{"title":1110,"type":1036,"items":1111},"GitLab for",[1112,1118,1124],{"text":1113,"config":1114},"Enterprise",{"icon":1115,"href":1116,"dataGaLocation":985,"dataGaName":1117},"Building","/enterprise/","enterprise",{"text":1119,"config":1120},"Small Business",{"icon":1121,"href":1122,"dataGaLocation":985,"dataGaName":1123},"Work","/small-business/","small business",{"text":1125,"config":1126},"Public Sector",{"icon":1127,"href":1128,"dataGaLocation":985,"dataGaName":1129},"Organization","/solutions/public-sector/","public sector",{"text":1131,"config":1132},"Pricing",{"href":1133,"dataGaName":1134,"dataGaLocation":985,"dataNavLevelOne":1134},"/pricing/","pricing",{"text":1136,"config":1137,"menu":1139},"Resources",{"dataNavLevelOne":1138},"resources",{"type":1036,"link":1140,"columns":1144,"feature":1238},{"text":1141,"config":1142},"View all resources",{"href":1143,"dataGaName":1138,"dataGaLocation":985},"/resources/",[1145,1178,1205],{"title":1146,"items":1147},"Getting started",[1148,1153,1158,1163,1168,1173],{"text":1149,"config":1150},"Install",{"href":1151,"dataGaName":1152,"dataGaLocation":985},"/install/","install",{"text":1154,"config":1155},"Quick start guides",{"href":1156,"dataGaName":1157,"dataGaLocation":985},"/get-started/","quick setup checklists",{"text":1159,"config":1160},"Learn",{"href":1161,"dataGaLocation":985,"dataGaName":1162},"https://university.gitlab.com/","learn",{"text":1164,"config":1165},"Product documentation",{"href":1166,"dataGaName":1167,"dataGaLocation":985},"https://docs.gitlab.com/","product documentation",{"text":1169,"config":1170},"Best practice videos",{"href":1171,"dataGaName":1172,"dataGaLocation":985},"/getting-started-videos/","best practice videos",{"text":1174,"config":1175},"Integrations",{"href":1176,"dataGaName":1177,"dataGaLocation":985},"/integrations/","integrations",{"title":1179,"items":1180},"Discover",[1181,1186,1191,1196,1200],{"text":1182,"config":1183},"Customer success stories",{"href":1184,"dataGaName":1185,"dataGaLocation":985},"/customers/","customer success stories",{"text":1187,"config":1188},"Blog",{"href":1189,"dataGaName":1190,"dataGaLocation":985},"/blog/","blog",{"text":1192,"config":1193},"Demo Hub",{"href":1194,"dataGaName":1195,"dataGaLocation":985},"/demo-hub/","demo hub",{"text":1197,"config":1198},"The Source",{"href":1199,"dataGaName":1190,"dataGaLocation":985},"/the-source/",{"text":1201,"config":1202},"Remote",{"href":1203,"dataGaName":1204,"dataGaLocation":985},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":1206,"items":1207},"Connect",[1208,1213,1218,1223,1228,1233],{"text":1209,"config":1210},"GitLab Services",{"href":1211,"dataGaName":1212,"dataGaLocation":985},"/services/","services",{"text":1214,"config":1215},"Contribute",{"href":1216,"dataGaName":1217,"dataGaLocation":985},"https://contributors.gitlab.com","contribute",{"text":1219,"config":1220},"Community",{"href":1221,"dataGaName":1222,"dataGaLocation":985},"/community/","community",{"text":1224,"config":1225},"Forum",{"href":1226,"dataGaName":1227,"dataGaLocation":985},"https://forum.gitlab.com/","forum",{"text":1229,"config":1230},"Events",{"href":1231,"dataGaName":1232,"dataGaLocation":985},"/events/","events",{"text":1234,"config":1235},"Partners",{"href":1236,"dataGaName":1237,"dataGaLocation":985},"/partners/","partners",{"config":1239,"title":1242,"text":1243,"link":1244},{"background":1240,"textColor":1241},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":1245,"config":1246},"Read the latest",{"href":1247,"dataGaName":1248,"dataGaLocation":985},"/whats-new/","whats new",{"text":1250,"config":1251,"menu":1253},"Company",{"dataNavLevelOne":1252},"company",{"type":1036,"columns":1254},[1255],{"items":1256},[1257,1262,1268,1270,1275,1280,1285,1290,1295,1300],{"text":1258,"config":1259},"About",{"href":1260,"dataGaName":1261,"dataGaLocation":985},"/company/","about",{"text":1263,"config":1264,"footerGa":1267},"Jobs",{"href":1265,"dataGaName":1266,"dataGaLocation":985},"/jobs/","jobs",{"dataGaName":1266},{"text":1229,"config":1269},{"href":1231,"dataGaName":1232,"dataGaLocation":985},{"text":1271,"config":1272},"Leadership",{"href":1273,"dataGaName":1274,"dataGaLocation":985},"/company/team/e-group/","leadership",{"text":1276,"config":1277},"Handbook",{"href":1278,"dataGaName":1279,"dataGaLocation":985},"https://handbook.gitlab.com/","handbook",{"text":1281,"config":1282},"Investor relations",{"href":1283,"dataGaName":1284,"dataGaLocation":985},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1286,"config":1287},"Trust Center",{"href":1288,"dataGaName":1289,"dataGaLocation":985},"/security/","trust center",{"text":1291,"config":1292},"AI Transparency Center",{"href":1293,"dataGaName":1294,"dataGaLocation":985},"/ai-transparency-center/","ai transparency center",{"text":1296,"config":1297},"Newsletter",{"href":1298,"dataGaName":1299,"dataGaLocation":985},"/company/contact/#contact-forms","newsletter",{"text":1301,"config":1302},"Press",{"href":1303,"dataGaName":1304,"dataGaLocation":985},"/press/","press",{"text":1306,"config":1307,"menu":1308},"Contact us",{"dataNavLevelOne":1252},{"type":1036,"columns":1309},[1310],{"items":1311},[1312,1317,1322],{"text":1313,"config":1314},"Talk to sales",{"href":1315,"dataGaName":1316,"dataGaLocation":985},"/sales/","talk to sales",{"text":1318,"config":1319},"Support portal",{"href":1320,"dataGaName":1321,"dataGaLocation":985},"https://support.gitlab.com/hc/en-us","support portal",{"text":1323,"config":1324},"Customer portal",{"href":1325,"dataGaName":1326,"dataGaLocation":985},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1328,"login":1329,"suggestions":1336},"Close",{"text":1330,"link":1331},"To search repositories and projects, login to",{"text":1332,"config":1333},"gitlab.com",{"href":999,"dataGaName":1334,"dataGaLocation":1335},"search login","search",{"text":1337,"default":1338},"Suggestions",[1339,1341,1345,1347,1351,1355],{"text":1016,"config":1340},{"href":1021,"dataGaName":1016,"dataGaLocation":1335},{"text":1342,"config":1343},"Code Suggestions (AI)",{"href":1344,"dataGaName":1342,"dataGaLocation":1335},"/solutions/code-suggestions/",{"text":549,"config":1346},{"href":1053,"dataGaName":549,"dataGaLocation":1335},{"text":1348,"config":1349},"GitLab on AWS",{"href":1350,"dataGaName":1348,"dataGaLocation":1335},"/partners/technology-partners/aws/",{"text":1352,"config":1353},"GitLab on Google Cloud",{"href":1354,"dataGaName":1352,"dataGaLocation":1335},"/partners/technology-partners/google-cloud-platform/",{"text":1356,"config":1357},"Why GitLab?",{"href":1029,"dataGaName":1356,"dataGaLocation":1335},{"freeTrial":1359,"mobileIcon":1364,"desktopIcon":1369,"secondaryButton":1372},{"text":1360,"config":1361},"Start free trial",{"href":1362,"dataGaName":990,"dataGaLocation":1363},"https://gitlab.com/-/trials/new/","nav",{"altText":1365,"config":1366},"Gitlab Icon",{"src":1367,"dataGaName":1368,"dataGaLocation":1363},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1365,"config":1370},{"src":1371,"dataGaName":1368,"dataGaLocation":1363},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1373,"config":1374},"Get Started",{"href":1375,"dataGaName":1376,"dataGaLocation":1363},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1378,"mobileIcon":1382,"desktopIcon":1384},{"text":1379,"config":1380},"Learn more about GitLab Duo",{"href":1021,"dataGaName":1381,"dataGaLocation":1363},"gitlab duo",{"altText":1365,"config":1383},{"src":1367,"dataGaName":1368,"dataGaLocation":1363},{"altText":1365,"config":1385},{"src":1371,"dataGaName":1368,"dataGaLocation":1363},{"button":1387,"mobileIcon":1392,"desktopIcon":1394},{"text":1388,"config":1389},"/switch",{"href":1390,"dataGaName":1391,"dataGaLocation":1363},"#contact","switch",{"altText":1365,"config":1393},{"src":1367,"dataGaName":1368,"dataGaLocation":1363},{"altText":1365,"config":1395},{"src":1396,"dataGaName":1368,"dataGaLocation":1363},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1398,"mobileIcon":1403,"desktopIcon":1405},{"text":1399,"config":1400},"Back to pricing",{"href":1133,"dataGaName":1401,"dataGaLocation":1363,"icon":1402},"back to pricing","GoBack",{"altText":1365,"config":1404},{"src":1367,"dataGaName":1368,"dataGaLocation":1363},{"altText":1365,"config":1406},{"src":1371,"dataGaName":1368,"dataGaLocation":1363},{"title":1408,"titleMobile":1409,"button":1410,"config":1415},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":1027,"config":1411},{"href":1412,"dataGaName":1413,"dataGaLocation":1414},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1416,"disabled":966},"release",{"data":1418},{"text":1419,"source":1420,"edit":1426,"contribute":1431,"config":1436,"items":1441,"minimal":1650},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1421,"config":1422},"View page source",{"href":1423,"dataGaName":1424,"dataGaLocation":1425},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1427,"config":1428},"Edit this page",{"href":1429,"dataGaName":1430,"dataGaLocation":1425},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1432,"config":1433},"Please contribute",{"href":1434,"dataGaName":1435,"dataGaLocation":1425},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1437,"facebook":1438,"youtube":1439,"linkedin":1440},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1442,1489,1542,1586,1618],{"title":1131,"links":1443,"subMenu":1458},[1444,1448,1453],{"text":1445,"config":1446},"View plans",{"href":1133,"dataGaName":1447,"dataGaLocation":1425},"view plans",{"text":1449,"config":1450},"Why Premium?",{"href":1451,"dataGaName":1452,"dataGaLocation":1425},"/pricing/premium/","why premium",{"text":1454,"config":1455},"Why Ultimate?",{"href":1456,"dataGaName":1457,"dataGaLocation":1425},"/pricing/ultimate/","why ultimate",[1459],{"title":1460,"links":1461},"Contact Us",[1462,1465,1467,1469,1474,1479,1484],{"text":1463,"config":1464},"Contact sales",{"href":1315,"dataGaName":995,"dataGaLocation":1425},{"text":1318,"config":1466},{"href":1320,"dataGaName":1321,"dataGaLocation":1425},{"text":1323,"config":1468},{"href":1325,"dataGaName":1326,"dataGaLocation":1425},{"text":1470,"config":1471},"Status",{"href":1472,"dataGaName":1473,"dataGaLocation":1425},"https://status.gitlab.com/","status",{"text":1475,"config":1476},"Terms of use",{"href":1477,"dataGaName":1478,"dataGaLocation":1425},"/terms/","terms of use",{"text":1480,"config":1481},"Privacy statement",{"href":1482,"dataGaName":1483,"dataGaLocation":1425},"/privacy/","privacy statement",{"text":1485,"config":1486},"Cookie preferences",{"dataGaName":1487,"dataGaLocation":1425,"id":1488,"isOneTrustButton":180},"cookie preferences","ot-sdk-btn",{"title":1032,"links":1490,"subMenu":1499},[1491,1495],{"text":1492,"config":1493},"DevSecOps platform",{"href":1014,"dataGaName":1494,"dataGaLocation":1425},"devsecops platform",{"text":1496,"config":1497},"AI-Assisted Development",{"href":1021,"dataGaName":1498,"dataGaLocation":1425},"ai-assisted development",[1500],{"title":1501,"links":1502},"Topics",[1503,1507,1512,1517,1522,1527,1532,1537],{"text":1504,"config":1505},"CICD",{"href":1506,"dataGaName":548,"dataGaLocation":1425},"/topics/ci-cd/",{"text":1508,"config":1509},"GitOps",{"href":1510,"dataGaName":1511,"dataGaLocation":1425},"/topics/gitops/","gitops",{"text":1513,"config":1514},"DevOps",{"href":1515,"dataGaName":1516,"dataGaLocation":1425},"/topics/devops/","devops",{"text":1518,"config":1519},"Version Control",{"href":1520,"dataGaName":1521,"dataGaLocation":1425},"/topics/version-control/","version control",{"text":1523,"config":1524},"DevSecOps",{"href":1525,"dataGaName":1526,"dataGaLocation":1425},"/topics/devsecops/","devsecops",{"text":1528,"config":1529},"Cloud Native",{"href":1530,"dataGaName":1531,"dataGaLocation":1425},"/topics/cloud-native/","cloud native",{"text":1533,"config":1534},"AI for Coding",{"href":1535,"dataGaName":1536,"dataGaLocation":1425},"/topics/devops/ai-for-coding/","ai for coding",{"text":1538,"config":1539},"Agentic AI",{"href":1540,"dataGaName":1541,"dataGaLocation":1425},"/topics/agentic-ai/","agentic ai",{"title":1543,"links":1544},"Solutions",[1545,1547,1549,1554,1558,1561,1565,1568,1570,1573,1576,1581],{"text":1075,"config":1546},{"href":1070,"dataGaName":1075,"dataGaLocation":1425},{"text":1064,"config":1548},{"href":1048,"dataGaName":1049,"dataGaLocation":1425},{"text":1550,"config":1551},"Agile development",{"href":1552,"dataGaName":1553,"dataGaLocation":1425},"/solutions/agile-delivery/","agile delivery",{"text":1555,"config":1556},"SCM",{"href":1060,"dataGaName":1557,"dataGaLocation":1425},"source code management",{"text":1504,"config":1559},{"href":1053,"dataGaName":1560,"dataGaLocation":1425},"continuous integration & delivery",{"text":1562,"config":1563},"Value stream management",{"href":1103,"dataGaName":1564,"dataGaLocation":1425},"value stream management",{"text":1508,"config":1566},{"href":1567,"dataGaName":1511,"dataGaLocation":1425},"/solutions/gitops/",{"text":1113,"config":1569},{"href":1116,"dataGaName":1117,"dataGaLocation":1425},{"text":1571,"config":1572},"Small business",{"href":1122,"dataGaName":1123,"dataGaLocation":1425},{"text":1574,"config":1575},"Public sector",{"href":1128,"dataGaName":1129,"dataGaLocation":1425},{"text":1577,"config":1578},"Education",{"href":1579,"dataGaName":1580,"dataGaLocation":1425},"/solutions/education/","education",{"text":1582,"config":1583},"Financial services",{"href":1584,"dataGaName":1585,"dataGaLocation":1425},"/solutions/finance/","financial services",{"title":1136,"links":1587},[1588,1590,1592,1594,1597,1599,1602,1604,1606,1608,1610,1612,1614,1616],{"text":1149,"config":1589},{"href":1151,"dataGaName":1152,"dataGaLocation":1425},{"text":1154,"config":1591},{"href":1156,"dataGaName":1157,"dataGaLocation":1425},{"text":1159,"config":1593},{"href":1161,"dataGaName":1162,"dataGaLocation":1425},{"text":1164,"config":1595},{"href":1166,"dataGaName":1596,"dataGaLocation":1425},"docs",{"text":1187,"config":1598},{"href":1189,"dataGaName":1190,"dataGaLocation":1425},{"text":1600,"config":1601},"What's new",{"href":1247,"dataGaName":1248,"dataGaLocation":1425},{"text":1182,"config":1603},{"href":1184,"dataGaName":1185,"dataGaLocation":1425},{"text":1201,"config":1605},{"href":1203,"dataGaName":1204,"dataGaLocation":1425},{"text":1209,"config":1607},{"href":1211,"dataGaName":1212,"dataGaLocation":1425},{"text":1214,"config":1609},{"href":1216,"dataGaName":1217,"dataGaLocation":1425},{"text":1219,"config":1611},{"href":1221,"dataGaName":1222,"dataGaLocation":1425},{"text":1224,"config":1613},{"href":1226,"dataGaName":1227,"dataGaLocation":1425},{"text":1229,"config":1615},{"href":1231,"dataGaName":1232,"dataGaLocation":1425},{"text":1234,"config":1617},{"href":1236,"dataGaName":1237,"dataGaLocation":1425},{"title":1250,"links":1619},[1620,1622,1624,1626,1628,1630,1634,1639,1641,1643,1645],{"text":1258,"config":1621},{"href":1260,"dataGaName":1252,"dataGaLocation":1425},{"text":1263,"config":1623},{"href":1265,"dataGaName":1266,"dataGaLocation":1425},{"text":1271,"config":1625},{"href":1273,"dataGaName":1274,"dataGaLocation":1425},{"text":1276,"config":1627},{"href":1278,"dataGaName":1279,"dataGaLocation":1425},{"text":1281,"config":1629},{"href":1283,"dataGaName":1284,"dataGaLocation":1425},{"text":1631,"config":1632},"Sustainability",{"href":1633,"dataGaName":1631,"dataGaLocation":1425},"/sustainability/",{"text":1635,"config":1636},"Diversity, inclusion and belonging (DIB)",{"href":1637,"dataGaName":1638,"dataGaLocation":1425},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1286,"config":1640},{"href":1288,"dataGaName":1289,"dataGaLocation":1425},{"text":1296,"config":1642},{"href":1298,"dataGaName":1299,"dataGaLocation":1425},{"text":1301,"config":1644},{"href":1303,"dataGaName":1304,"dataGaLocation":1425},{"text":1646,"config":1647},"Modern Slavery Transparency Statement",{"href":1648,"dataGaName":1649,"dataGaLocation":1425},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1651},[1652,1655,1658],{"text":1653,"config":1654},"Terms",{"href":1477,"dataGaName":1478,"dataGaLocation":1425},{"text":1656,"config":1657},"Cookies",{"dataGaName":1487,"dataGaLocation":1425,"id":1488,"isOneTrustButton":180},{"text":1659,"config":1660},"Privacy",{"href":1482,"dataGaName":1483,"dataGaLocation":1425},[1662],{"id":1663,"title":7,"body":965,"config":1664,"content":1666,"description":965,"extension":1670,"meta":1671,"navigation":180,"path":1672,"seo":1673,"stem":1674,"__hash__":1675},"blogAuthors/en-us/blog/authors/steve-abrams.yml",{"template":1665},"BlogAuthor",{"name":7,"config":1667},{"headshot":1668,"ctfId":1669},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749681809/Blog/Author%20Headshots/sabrams-headshot.png","sabrams","yml",{},"/en-us/blog/authors/steve-abrams",{},"en-us/blog/authors/steve-abrams","3MdOGncPQLlG4q7-TSNXPwgDWzaF-qyUT3_K6ZjVI_I",[1677,1685,1693],{"title":1678,"description":1679,"heroImage":1680,"category":961,"date":1681,"authors":1682,"slug":1684,"externalUrl":965},"CEO Shadow Takeaways from Jacie","Recap of my experience in the CEO Shadow Program.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664102/Blog/Hero%20Images/gitlab-values-cover.png","2021-05-18",[1683],"Jacie Bandur","ceo-shadow-recap",{"title":1686,"description":1687,"heroImage":1688,"category":961,"date":1689,"authors":1690,"slug":1692,"externalUrl":965},"Why I love contributing to GitLab","Making small meaningful changes is what it's all about.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749679501/Blog/Hero%20Images/new-feature.png","2021-05-11",[1691],"Austin Regnery","why-i-love-contributing-to-gitlab",{"title":1694,"description":1695,"heroImage":1696,"category":961,"date":1689,"authors":1697,"slug":1699,"externalUrl":965},"Placebo Lines on the Pipeline Graph","Have you noticed the connecting lines missing on your pipelines lately? Here's why","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749679507/Blog/Hero%20Images/ci-cd.png",[1698],"Sam Beckham","placebo-lines-on-the-pipeline-graph",{"promotions":1701},[1702,1716,1728,1740],{"id":1703,"categories":1704,"header":1706,"text":1707,"button":1708,"image":1713},"ai-modernization",[1705],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1709,"config":1710},"Get your AI maturity score",{"href":1711,"dataGaName":1712,"dataGaLocation":1190},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1714},{"src":1715},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1717,"categories":1718,"header":1720,"text":1707,"button":1721,"image":1725},"devops-modernization",[1719,1526],"product","Are you just managing tools or shipping innovation?",{"text":1722,"config":1723},"Get your DevOps maturity score",{"href":1724,"dataGaName":1712,"dataGaLocation":1190},"/assessments/devops-modernization-assessment/",{"config":1726},{"src":1727},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1729,"categories":1730,"header":1732,"text":1707,"button":1733,"image":1737},"security-modernization",[1731],"security","Are you trading speed for security?",{"text":1734,"config":1735},"Get your security maturity score",{"href":1736,"dataGaName":1712,"dataGaLocation":1190},"/assessments/security-modernization-assessment/",{"config":1738},{"src":1739},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1741,"paths":1742,"header":1745,"text":1746,"button":1747,"image":1752},"github-azure-migration",[1743,1744],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":1748,"config":1749},"See how GitLab compares to GitHub",{"href":1750,"dataGaName":1751,"dataGaLocation":1190},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1753},{"src":1727},{"header":1755,"blurb":1756,"button":1757,"secondaryButton":1762},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1758,"config":1759},"Get your free trial",{"href":1760,"dataGaName":990,"dataGaLocation":1761},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1463,"config":1763},{"href":1315,"dataGaName":995,"dataGaLocation":1761},1786803758656]