[{"data":1,"prerenderedAt":2120},["ShallowReactive",2],{"/blog/using-gitlab-container-virtual-registry-with-docker-hardened-images":3,"navigation-en-us":1333,"banner-en-us":1761,"footer-en-us":1771,"blog-post-authors-en-us-Tim Rizzi":2016,"blog-related-posts-en-us-using-gitlab-container-virtual-registry-with-docker-hardened-images":2031,"blog-promotions-en-us":2057,"next-steps-en-us":2110},{"id":4,"title":5,"authors":6,"body":8,"category":1313,"date":1314,"description":1315,"extension":1316,"externalUrl":1317,"faq":1317,"featured":1318,"heroImage":1319,"meta":1320,"navigation":267,"path":1321,"seo":1322,"slug":1325,"stem":1326,"tags":1327,"template":1331,"updatedDate":1317,"__hash__":1332},"blogPosts/en-us/blog/using-gitlab-container-virtual-registry-with-docker-hardened-images.md","How to use GitLab Container Virtual Registry with Docker Hardened Images",[7],"Tim Rizzi",{"type":9,"value":10,"toc":1289},"minimark",[11,15,21,26,31,34,39,44,49,59,62,67,70,105,108,119,122,125,129,132,143,146,152,156,164,167,193,196,202,208,218,224,228,231,236,395,399,402,468,472,478,562,566,690,694,701,856,860,863,921,925,928,1014,1018,1021,1071,1074,1077,1081,1084,1088,1091,1167,1170,1174,1177,1181,1189,1193,1200,1214,1218,1221,1229,1232,1236,1239,1250,1259,1263,1285],[12,13,14],"p",{},"If you're a platform engineer, you've probably had this conversation:",[12,16,17],{},[18,19,20],"em",{},"\"Security says we need to use hardened base images.\"",[12,22,23],{},[18,24,25],{},"\"Great, where do I configure credentials for yet another registry?\"",[12,27,28],{},[18,29,30],{},"\"Also, how do we make sure everyone actually uses them?\"",[12,32,33],{},"Or this one:",[12,35,36],{},[18,37,38],{},"\"Why are our builds so slow?\"",[12,40,41],{},[18,42,43],{},"\"We're pulling the same 500MB image from Docker Hub in every single job.\"",[12,45,46],{},[18,47,48],{},"\"Can't we just cache these somewhere?\"",[12,50,51,52,58],{},"I've been working on ",[53,54,57],"a",{"href":55,"rel":56},"https://docs.gitlab.com/user/packages/virtual_registry/container/",[],"Container Virtual Registry"," at GitLab specifically to solve these problems. It's a pull-through cache that sits in front of your upstream registries — Docker Hub, dhi.io (Docker Hardened Images), MCR, and Quay — and gives your teams a single endpoint to pull from. Images get cached on the first pull. Subsequent pulls come from the cache. Your developers don't need to know or care which upstream a particular image came from.",[12,60,61],{},"This article shows you how to set up Container Virtual Registry, specifically with Docker Hardened Images in mind, since that's a combination that makes a lot of sense for teams concerned about security and not making their developers' lives harder.",[63,64,66],"h2",{"id":65},"what-problem-are-we-actually-solving","What problem are we actually solving?",[12,68,69],{},"The Platform teams I usually talk to manage container images across three to five registries:",[71,72,73,81,87,93,99],"ul",{},[74,75,76,80],"li",{},[77,78,79],"strong",{},"Docker Hub"," for most base images",[74,82,83,86],{},[77,84,85],{},"dhi.io"," for Docker Hardened Images (security-conscious workloads)",[74,88,89,92],{},[77,90,91],{},"MCR"," for .NET and Azure tooling",[74,94,95,98],{},[77,96,97],{},"Quay.io"," for Red Hat ecosystem stuff",[74,100,101,104],{},[77,102,103],{},"Internal registries"," for proprietary images",[12,106,107],{},"Each one has its own:",[71,109,110,113,116],{},[74,111,112],{},"Authentication mechanism",[74,114,115],{},"Network latency characteristics",[74,117,118],{},"Way of organizing image paths",[12,120,121],{},"Your CI/CD configs end up littered with registry-specific logic. Credential management becomes a project unto itself. And every pipeline job pulls the same base images over the network, even though they haven't changed in weeks.",[12,123,124],{},"Container Virtual Registry consolidates this. One registry URL. One authentication flow (GitLab's). Cached images are served from GitLab's infrastructure rather than traversing the internet each time.",[63,126,128],{"id":127},"how-it-works","How it works",[12,130,131],{},"The model is straightforward:",[133,134,140],"pre",{"className":135,"code":137,"language":138,"meta":139},[136],"language-text","Your pipeline pulls:\n  gitlab.com/virtual_registries/container/1000016/python:3.13\n\nVirtual registry checks:\n  1. Do I have this cached? → Return it\n  2. No? → Fetch from upstream, cache it, return it\n\n","text","",[141,142,137],"code",{"__ignoreMap":139},[12,144,145],{},"You configure upstreams in priority order. When a pull request comes in, the virtual registry checks each upstream until it finds the image. The result gets cached for a configurable period (default 24 hours).",[133,147,150],{"className":148,"code":149,"language":138,"meta":139},[136],"┌─────────────────────────────────────────────────────────┐\n│                    CI/CD Pipeline                       │\n│                          │                              │\n│                          ▼                              │\n│   gitlab.com/virtual_registries/container/\u003Cid>/image   │\n└─────────────────────────────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────┐\n│            Container Virtual Registry                   │\n│                                                         │\n│  Upstream 1: Docker Hub ────────────────┐               │\n│  Upstream 2: dhi.io (Hardened) ────────┐│               │\n│  Upstream 3: MCR ─────────────────────┐││               │\n│  Upstream 4: Quay.io ────────────────┐│││               │\n│                                      ││││               │\n│                    ┌─────────────────┴┴┴┴──┐            │\n│                    │        Cache          │            │\n│                    │  (manifests + layers) │            │\n│                    └───────────────────────┘            │\n└─────────────────────────────────────────────────────────┘\n",[141,151,149],{"__ignoreMap":139},[63,153,155],{"id":154},"why-this-matters-for-docker-hardened-images","Why this matters for Docker Hardened Images",[12,157,158,163],{},[53,159,162],{"href":160,"rel":161},"https://docs.docker.com/dhi/",[],"Docker Hardened Images"," are great because of the minimal attack surface, near-zero CVEs, proper software bills of materials (SBOMs), and SLSA provenance. If you're evaluating base images for security-sensitive workloads, they should be on your list.",[12,165,166],{},"But adopting them creates the same operational friction as any new registry:",[71,168,169,175,181,187],{},[74,170,171,174],{},[77,172,173],{},"Credential distribution",": You need to get Docker credentials to every system that pulls images from dhi.io.",[74,176,177,180],{},[77,178,179],{},"CI/CD changes",": Every pipeline needs to be updated to authenticate with dhi.io.",[74,182,183,186],{},[77,184,185],{},"Developer friction",": People need to remember to use the hardened variants.",[74,188,189,192],{},[77,190,191],{},"Visibility gap",": It's difficult to tell if teams are actually using hardened images vs. regular ones.",[12,194,195],{},"Virtual registry addresses each of these:",[12,197,198,201],{},[77,199,200],{},"Single credential",": Teams authenticate to GitLab. The virtual registry handles upstream authentication. You configure Docker credentials once, at the registry level, and they apply to all pulls.",[12,203,204,207],{},[77,205,206],{},"No CI/CD changes per-team",": Point pipelines at your virtual registry. Done. The upstream configuration is centralized.",[12,209,210,213,214,217],{},[77,211,212],{},"Gradual adoption",": Since images get cached with their full path, you can see in the cache what's being pulled. If someone's pulling ",[141,215,216],{},"library/python:3.11"," instead of the hardened variant, you'll know.",[12,219,220,223],{},[77,221,222],{},"Audit trail",": The cache shows you exactly which images are in active use. Useful for compliance, useful for understanding what your fleet actually depends on.",[63,225,227],{"id":226},"setting-it-up","Setting it up",[12,229,230],{},"Here's a real setup using the Python client from this demo project.",[232,233,235],"h3",{"id":234},"create-the-virtual-registry","Create the virtual registry",[133,237,241],{"className":238,"code":239,"language":240,"meta":139,"style":139},"language-python shiki shiki-themes github-light","from virtual_registry_client import VirtualRegistryClient\n\nclient = VirtualRegistryClient()\n\nregistry = client.create_virtual_registry(\n    group_id=\"785414\",  # Your top-level group ID\n    name=\"platform-images\",\n    description=\"Cached container images for platform teams\"\n)\n\nprint(f\"Registry ID: {registry['id']}\")\n# You'll need this ID for the pull URL\n","python",[141,242,243,262,269,281,286,297,317,331,342,348,353,389],{"__ignoreMap":139},[244,245,248,252,256,259],"span",{"class":246,"line":247},"line",1,[244,249,251],{"class":250},"sD7c4","from",[244,253,255],{"class":254},"sgsFI"," virtual_registry_client ",[244,257,258],{"class":250},"import",[244,260,261],{"class":254}," VirtualRegistryClient\n",[244,263,265],{"class":246,"line":264},2,[244,266,268],{"emptyLinePlaceholder":267},true,"\n",[244,270,272,275,278],{"class":246,"line":271},3,[244,273,274],{"class":254},"client ",[244,276,277],{"class":250},"=",[244,279,280],{"class":254}," VirtualRegistryClient()\n",[244,282,284],{"class":246,"line":283},4,[244,285,268],{"emptyLinePlaceholder":267},[244,287,289,292,294],{"class":246,"line":288},5,[244,290,291],{"class":254},"registry ",[244,293,277],{"class":250},[244,295,296],{"class":254}," client.create_virtual_registry(\n",[244,298,300,304,306,310,313],{"class":246,"line":299},6,[244,301,303],{"class":302},"sqxcx","    group_id",[244,305,277],{"class":250},[244,307,309],{"class":308},"sYBdl","\"785414\"",[244,311,312],{"class":254},",  ",[244,314,316],{"class":315},"sAwPA","# Your top-level group ID\n",[244,318,320,323,325,328],{"class":246,"line":319},7,[244,321,322],{"class":302},"    name",[244,324,277],{"class":250},[244,326,327],{"class":308},"\"platform-images\"",[244,329,330],{"class":254},",\n",[244,332,334,337,339],{"class":246,"line":333},8,[244,335,336],{"class":302},"    description",[244,338,277],{"class":250},[244,340,341],{"class":308},"\"Cached container images for platform teams\"\n",[244,343,345],{"class":246,"line":344},9,[244,346,347],{"class":254},")\n",[244,349,351],{"class":246,"line":350},10,[244,352,268],{"emptyLinePlaceholder":267},[244,354,356,360,363,366,369,372,375,378,381,384,387],{"class":246,"line":355},11,[244,357,359],{"class":358},"sYu0t","print",[244,361,362],{"class":254},"(",[244,364,365],{"class":250},"f",[244,367,368],{"class":308},"\"Registry ID: ",[244,370,371],{"class":358},"{",[244,373,374],{"class":254},"registry[",[244,376,377],{"class":308},"'id'",[244,379,380],{"class":254},"]",[244,382,383],{"class":358},"}",[244,385,386],{"class":308},"\"",[244,388,347],{"class":254},[244,390,392],{"class":246,"line":391},12,[244,393,394],{"class":315},"# You'll need this ID for the pull URL\n",[232,396,398],{"id":397},"add-docker-hub-as-an-upstream","Add Docker Hub as an upstream",[12,400,401],{},"For official images like Alpine, Python, etc.:",[133,403,405],{"className":238,"code":404,"language":240,"meta":139,"style":139},"docker_upstream = client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://registry-1.docker.io\",\n    name=\"Docker Hub\",\n    cache_validity_hours=24\n)\n",[141,406,407,417,431,443,454,464],{"__ignoreMap":139},[244,408,409,412,414],{"class":246,"line":247},[244,410,411],{"class":254},"docker_upstream ",[244,413,277],{"class":250},[244,415,416],{"class":254}," client.create_upstream(\n",[244,418,419,422,424,426,428],{"class":246,"line":264},[244,420,421],{"class":302},"    registry_id",[244,423,277],{"class":250},[244,425,374],{"class":254},[244,427,377],{"class":308},[244,429,430],{"class":254},"],\n",[244,432,433,436,438,441],{"class":246,"line":271},[244,434,435],{"class":302},"    url",[244,437,277],{"class":250},[244,439,440],{"class":308},"\"https://registry-1.docker.io\"",[244,442,330],{"class":254},[244,444,445,447,449,452],{"class":246,"line":283},[244,446,322],{"class":302},[244,448,277],{"class":250},[244,450,451],{"class":308},"\"Docker Hub\"",[244,453,330],{"class":254},[244,455,456,459,461],{"class":246,"line":288},[244,457,458],{"class":302},"    cache_validity_hours",[244,460,277],{"class":250},[244,462,463],{"class":358},"24\n",[244,465,466],{"class":246,"line":299},[244,467,347],{"class":254},[232,469,471],{"id":470},"add-docker-hardened-images-dhiio","Add Docker Hardened Images (dhi.io)",[12,473,474,475,477],{},"Docker Hardened Images are hosted on ",[141,476,85],{},", a separate registry that requires authentication:",[133,479,481],{"className":238,"code":480,"language":240,"meta":139,"style":139},"dhi_upstream = client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://dhi.io\",\n    name=\"Docker Hardened Images\",\n    username=\"your-docker-username\",\n    password=\"your-docker-access-token\",\n    cache_validity_hours=24\n)\n",[141,482,483,492,504,515,526,538,550,558],{"__ignoreMap":139},[244,484,485,488,490],{"class":246,"line":247},[244,486,487],{"class":254},"dhi_upstream ",[244,489,277],{"class":250},[244,491,416],{"class":254},[244,493,494,496,498,500,502],{"class":246,"line":264},[244,495,421],{"class":302},[244,497,277],{"class":250},[244,499,374],{"class":254},[244,501,377],{"class":308},[244,503,430],{"class":254},[244,505,506,508,510,513],{"class":246,"line":271},[244,507,435],{"class":302},[244,509,277],{"class":250},[244,511,512],{"class":308},"\"https://dhi.io\"",[244,514,330],{"class":254},[244,516,517,519,521,524],{"class":246,"line":283},[244,518,322],{"class":302},[244,520,277],{"class":250},[244,522,523],{"class":308},"\"Docker Hardened Images\"",[244,525,330],{"class":254},[244,527,528,531,533,536],{"class":246,"line":288},[244,529,530],{"class":302},"    username",[244,532,277],{"class":250},[244,534,535],{"class":308},"\"your-docker-username\"",[244,537,330],{"class":254},[244,539,540,543,545,548],{"class":246,"line":299},[244,541,542],{"class":302},"    password",[244,544,277],{"class":250},[244,546,547],{"class":308},"\"your-docker-access-token\"",[244,549,330],{"class":254},[244,551,552,554,556],{"class":246,"line":319},[244,553,458],{"class":302},[244,555,277],{"class":250},[244,557,463],{"class":358},[244,559,560],{"class":246,"line":333},[244,561,347],{"class":254},[232,563,565],{"id":564},"add-other-upstreams","Add other upstreams",[133,567,569],{"className":238,"code":568,"language":240,"meta":139,"style":139},"# MCR for .NET teams\nclient.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://mcr.microsoft.com\",\n    name=\"Microsoft Container Registry\",\n    cache_validity_hours=48\n)\n\n# Quay for Red Hat stuff\nclient.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://quay.io\",\n    name=\"Quay.io\",\n    cache_validity_hours=24\n)\n",[141,570,571,576,581,593,604,615,624,628,632,637,641,653,664,676,685],{"__ignoreMap":139},[244,572,573],{"class":246,"line":247},[244,574,575],{"class":315},"# MCR for .NET teams\n",[244,577,578],{"class":246,"line":264},[244,579,580],{"class":254},"client.create_upstream(\n",[244,582,583,585,587,589,591],{"class":246,"line":271},[244,584,421],{"class":302},[244,586,277],{"class":250},[244,588,374],{"class":254},[244,590,377],{"class":308},[244,592,430],{"class":254},[244,594,595,597,599,602],{"class":246,"line":283},[244,596,435],{"class":302},[244,598,277],{"class":250},[244,600,601],{"class":308},"\"https://mcr.microsoft.com\"",[244,603,330],{"class":254},[244,605,606,608,610,613],{"class":246,"line":288},[244,607,322],{"class":302},[244,609,277],{"class":250},[244,611,612],{"class":308},"\"Microsoft Container Registry\"",[244,614,330],{"class":254},[244,616,617,619,621],{"class":246,"line":299},[244,618,458],{"class":302},[244,620,277],{"class":250},[244,622,623],{"class":358},"48\n",[244,625,626],{"class":246,"line":319},[244,627,347],{"class":254},[244,629,630],{"class":246,"line":333},[244,631,268],{"emptyLinePlaceholder":267},[244,633,634],{"class":246,"line":344},[244,635,636],{"class":315},"# Quay for Red Hat stuff\n",[244,638,639],{"class":246,"line":350},[244,640,580],{"class":254},[244,642,643,645,647,649,651],{"class":246,"line":355},[244,644,421],{"class":302},[244,646,277],{"class":250},[244,648,374],{"class":254},[244,650,377],{"class":308},[244,652,430],{"class":254},[244,654,655,657,659,662],{"class":246,"line":391},[244,656,435],{"class":302},[244,658,277],{"class":250},[244,660,661],{"class":308},"\"https://quay.io\"",[244,663,330],{"class":254},[244,665,667,669,671,674],{"class":246,"line":666},13,[244,668,322],{"class":302},[244,670,277],{"class":250},[244,672,673],{"class":308},"\"Quay.io\"",[244,675,330],{"class":254},[244,677,679,681,683],{"class":246,"line":678},14,[244,680,458],{"class":302},[244,682,277],{"class":250},[244,684,463],{"class":358},[244,686,688],{"class":246,"line":687},15,[244,689,347],{"class":254},[232,691,693],{"id":692},"update-your-cicd","Update your CI/CD",[12,695,696,697,700],{},"Here's a ",[141,698,699],{},".gitlab-ci.yml"," that pulls through the virtual registry:",[133,702,706],{"className":703,"code":704,"language":705,"meta":139,"style":139},"language-yaml shiki shiki-themes github-light","variables:\n  VIRTUAL_REGISTRY_ID: \u003Cyour_virtual_registry_ID>\n\n  \nbuild:\n  image: docker:24\n  services:\n    - docker:24-dind\n  before_script:\n    # Authenticate to GitLab (which handles upstream auth for you)\n    - echo \"${CI_JOB_TOKEN}\" | docker login -u gitlab-ci-token --password-stdin gitlab.com\n  script:\n    # All of these go through your single virtual registry\n    \n    # Official Docker Hub images (use library/ prefix)\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/library/alpine:latest\n    \n    # Docker Hardened Images from dhi.io (no prefix needed)\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/python:3.13\n    \n    # .NET from MCR\n    - docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/dotnet/sdk:8.0\n","yaml",[141,707,708,717,728,732,737,744,754,761,769,776,781,788,795,800,805,810,818,823,829,837,842,848],{"__ignoreMap":139},[244,709,710,714],{"class":246,"line":247},[244,711,713],{"class":712},"shJU0","variables",[244,715,716],{"class":254},":\n",[244,718,719,722,725],{"class":246,"line":264},[244,720,721],{"class":712},"  VIRTUAL_REGISTRY_ID",[244,723,724],{"class":254},": ",[244,726,727],{"class":308},"\u003Cyour_virtual_registry_ID>\n",[244,729,730],{"class":246,"line":271},[244,731,268],{"emptyLinePlaceholder":267},[244,733,734],{"class":246,"line":283},[244,735,736],{"class":254},"  \n",[244,738,739,742],{"class":246,"line":288},[244,740,741],{"class":712},"build",[244,743,716],{"class":254},[244,745,746,749,751],{"class":246,"line":299},[244,747,748],{"class":712},"  image",[244,750,724],{"class":254},[244,752,753],{"class":308},"docker:24\n",[244,755,756,759],{"class":246,"line":319},[244,757,758],{"class":712},"  services",[244,760,716],{"class":254},[244,762,763,766],{"class":246,"line":333},[244,764,765],{"class":254},"    - ",[244,767,768],{"class":308},"docker:24-dind\n",[244,770,771,774],{"class":246,"line":344},[244,772,773],{"class":712},"  before_script",[244,775,716],{"class":254},[244,777,778],{"class":246,"line":350},[244,779,780],{"class":315},"    # Authenticate to GitLab (which handles upstream auth for you)\n",[244,782,783,785],{"class":246,"line":355},[244,784,765],{"class":254},[244,786,787],{"class":308},"echo \"${CI_JOB_TOKEN}\" | docker login -u gitlab-ci-token --password-stdin gitlab.com\n",[244,789,790,793],{"class":246,"line":391},[244,791,792],{"class":712},"  script",[244,794,716],{"class":254},[244,796,797],{"class":246,"line":666},[244,798,799],{"class":315},"    # All of these go through your single virtual registry\n",[244,801,802],{"class":246,"line":678},[244,803,804],{"class":254},"    \n",[244,806,807],{"class":246,"line":687},[244,808,809],{"class":315},"    # Official Docker Hub images (use library/ prefix)\n",[244,811,813,815],{"class":246,"line":812},16,[244,814,765],{"class":254},[244,816,817],{"class":308},"docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/library/alpine:latest\n",[244,819,821],{"class":246,"line":820},17,[244,822,804],{"class":254},[244,824,826],{"class":246,"line":825},18,[244,827,828],{"class":315},"    # Docker Hardened Images from dhi.io (no prefix needed)\n",[244,830,832,834],{"class":246,"line":831},19,[244,833,765],{"class":254},[244,835,836],{"class":308},"docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/python:3.13\n",[244,838,840],{"class":246,"line":839},20,[244,841,804],{"class":254},[244,843,845],{"class":246,"line":844},21,[244,846,847],{"class":315},"    # .NET from MCR\n",[244,849,851,853],{"class":246,"line":850},22,[244,852,765],{"class":254},[244,854,855],{"class":308},"docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/dotnet/sdk:8.0\n",[232,857,859],{"id":858},"image-path-formats","Image path formats",[12,861,862],{},"Different registries use different path conventions:",[864,865,866,879],"table",{},[867,868,869],"thead",{},[870,871,872,876],"tr",{},[873,874,875],"th",{},"Registry",[873,877,878],{},"Pull URL Example",[880,881,882,893,903,912],"tbody",{},[870,883,884,888],{},[885,886,887],"td",{},"Docker Hub (official)",[885,889,890],{},[141,891,892],{},".../library/python:3.11-slim",[870,894,895,898],{},[885,896,897],{},"Docker Hardened Images (dhi.io)",[885,899,900],{},[141,901,902],{},".../python:3.13",[870,904,905,907],{},[885,906,91],{},[885,908,909],{},[141,910,911],{},".../dotnet/sdk:8.0",[870,913,914,916],{},[885,915,97],{},[885,917,918],{},[141,919,920],{},".../prometheus/prometheus:latest",[232,922,924],{"id":923},"verify-its-working","Verify it's working",[12,926,927],{},"After some pulls, check your cache:",[133,929,931],{"className":238,"code":930,"language":240,"meta":139,"style":139},"upstreams = client.list_registry_upstreams(registry['id'])\nfor upstream in upstreams:\n    entries = client.list_cache_entries(upstream['id'])\n    print(f\"{upstream['name']}: {len(entries)} cached entries\")\n\n",[141,932,933,948,962,976],{"__ignoreMap":139},[244,934,935,938,940,943,945],{"class":246,"line":247},[244,936,937],{"class":254},"upstreams ",[244,939,277],{"class":250},[244,941,942],{"class":254}," client.list_registry_upstreams(registry[",[244,944,377],{"class":308},[244,946,947],{"class":254},"])\n",[244,949,950,953,956,959],{"class":246,"line":264},[244,951,952],{"class":250},"for",[244,954,955],{"class":254}," upstream ",[244,957,958],{"class":250},"in",[244,960,961],{"class":254}," upstreams:\n",[244,963,964,967,969,972,974],{"class":246,"line":271},[244,965,966],{"class":254},"    entries ",[244,968,277],{"class":250},[244,970,971],{"class":254}," client.list_cache_entries(upstream[",[244,973,377],{"class":308},[244,975,947],{"class":254},[244,977,978,981,983,985,987,989,992,995,997,999,1001,1004,1007,1009,1012],{"class":246,"line":283},[244,979,980],{"class":358},"    print",[244,982,362],{"class":254},[244,984,365],{"class":250},[244,986,386],{"class":308},[244,988,371],{"class":358},[244,990,991],{"class":254},"upstream[",[244,993,994],{"class":308},"'name'",[244,996,380],{"class":254},[244,998,383],{"class":358},[244,1000,724],{"class":308},[244,1002,1003],{"class":358},"{len",[244,1005,1006],{"class":254},"(entries)",[244,1008,383],{"class":358},[244,1010,1011],{"class":308}," cached entries\"",[244,1013,347],{"class":254},[63,1015,1017],{"id":1016},"what-the-numbers-look-like","What the numbers look like",[12,1019,1020],{},"I ran tests pulling images through the virtual registry:",[864,1022,1023,1036],{},[867,1024,1025],{},[870,1026,1027,1030,1033],{},[873,1028,1029],{},"Metric",[873,1031,1032],{},"Without Cache",[873,1034,1035],{},"With Warm Cache",[880,1037,1038,1049,1060],{},[870,1039,1040,1043,1046],{},[885,1041,1042],{},"Pull time (Alpine)",[885,1044,1045],{},"10.3s",[885,1047,1048],{},"4.2s",[870,1050,1051,1054,1057],{},[885,1052,1053],{},"Pull time (Python 3.13 DHI)",[885,1055,1056],{},"11.6s",[885,1058,1059],{},"~4s",[870,1061,1062,1065,1068],{},[885,1063,1064],{},"Network roundtrips to upstream",[885,1066,1067],{},"Every pull",[885,1069,1070],{},"Cache misses only",[12,1072,1073],{},"The first pull is the same speed (it has to fetch from upstream). Every pull after that, for the cache validity period, comes straight from GitLab's storage. No network hop to Docker Hub, dhi.io, MCR, or wherever the image lives.",[12,1075,1076],{},"For a team running hundreds of pipeline jobs per day, that's hours of cumulative build time saved.",[63,1078,1080],{"id":1079},"practical-considerations","Practical considerations",[12,1082,1083],{},"Here are some considerations to keep in mind:",[232,1085,1087],{"id":1086},"cache-validity","Cache validity",[12,1089,1090],{},"24 hours is the default. For security-sensitive images where you want patches quickly, consider 12 hours or less:",[133,1092,1094],{"className":238,"code":1093,"language":240,"meta":139,"style":139},"client.create_upstream(\n    registry_id=registry['id'],\n    url=\"https://dhi.io\",\n    name=\"Docker Hardened Images\",\n    username=\"your-username\",\n    password=\"your-token\",\n    cache_validity_hours=12\n)\n",[141,1095,1096,1100,1112,1122,1132,1143,1154,1163],{"__ignoreMap":139},[244,1097,1098],{"class":246,"line":247},[244,1099,580],{"class":254},[244,1101,1102,1104,1106,1108,1110],{"class":246,"line":264},[244,1103,421],{"class":302},[244,1105,277],{"class":250},[244,1107,374],{"class":254},[244,1109,377],{"class":308},[244,1111,430],{"class":254},[244,1113,1114,1116,1118,1120],{"class":246,"line":271},[244,1115,435],{"class":302},[244,1117,277],{"class":250},[244,1119,512],{"class":308},[244,1121,330],{"class":254},[244,1123,1124,1126,1128,1130],{"class":246,"line":283},[244,1125,322],{"class":302},[244,1127,277],{"class":250},[244,1129,523],{"class":308},[244,1131,330],{"class":254},[244,1133,1134,1136,1138,1141],{"class":246,"line":288},[244,1135,530],{"class":302},[244,1137,277],{"class":250},[244,1139,1140],{"class":308},"\"your-username\"",[244,1142,330],{"class":254},[244,1144,1145,1147,1149,1152],{"class":246,"line":299},[244,1146,542],{"class":302},[244,1148,277],{"class":250},[244,1150,1151],{"class":308},"\"your-token\"",[244,1153,330],{"class":254},[244,1155,1156,1158,1160],{"class":246,"line":319},[244,1157,458],{"class":302},[244,1159,277],{"class":250},[244,1161,1162],{"class":358},"12\n",[244,1164,1165],{"class":246,"line":333},[244,1166,347],{"class":254},[12,1168,1169],{},"For stable, infrequently-updated images (like specific version tags), longer validity is fine.",[232,1171,1173],{"id":1172},"upstream-priority","Upstream priority",[12,1175,1176],{},"Upstreams are checked in order. If you have images with the same name on different registries, the first matching upstream wins.",[232,1178,1180],{"id":1179},"limits","Limits",[71,1182,1183,1186],{},[74,1184,1185],{},"Maximum of 20 virtual registries per group",[74,1187,1188],{},"Maximum of 20 upstreams per virtual registry",[63,1190,1192],{"id":1191},"configuration-via-ui","Configuration via UI",[12,1194,1195,1196,1199],{},"You can also configure virtual registries and upstreams directly from the GitLab UI—no API calls required. Navigate to your group's ",[77,1197,1198],{},"Settings > Packages and registries > Virtual Registry"," to:",[71,1201,1202,1205,1208,1211],{},[74,1203,1204],{},"Create and manage virtual registries",[74,1206,1207],{},"Add, edit, and reorder upstream registries",[74,1209,1210],{},"View and manage the cache",[74,1212,1213],{},"Monitor which images are being pulled",[63,1215,1217],{"id":1216},"whats-next","What's next",[12,1219,1220],{},"We're actively developing:",[71,1222,1223],{},[74,1224,1225,1228],{},[77,1226,1227],{},"Allow/deny lists",": Use regex to control which images can be pulled from specific upstreams.",[12,1230,1231],{},"This is beta software. It works, people are using it in production, but we're still iterating based on feedback.",[63,1233,1235],{"id":1234},"share-your-feedback","Share your feedback",[12,1237,1238],{},"If you're a platform engineer dealing with container registry sprawl, I'd like to understand your setup:",[71,1240,1241,1244,1247],{},[74,1242,1243],{},"How many upstream registries are you managing?",[74,1245,1246],{},"What's your biggest pain point with the current state?",[74,1248,1249],{},"Would something like this help, and if not, what's missing?",[12,1251,1252,1253,1258],{},"Please share your experiences in the ",[53,1254,1257],{"href":1255,"rel":1256},"https://gitlab.com/gitlab-org/gitlab/-/work_items/589630",[],"Container Virtual Registry feedback issue",".",[63,1260,1262],{"id":1261},"related-resources","Related resources",[71,1264,1265,1272,1278],{},[74,1266,1267],{},[53,1268,1271],{"href":1269,"rel":1270},"https://about.gitlab.com/blog/new-gitlab-metrics-and-registry-features-help-reduce-ci-cd-bottlenecks/#container-virtual-registry",[],"New GitLab metrics and registry features help reduce CI/CD bottlenecks",[74,1273,1274],{},[53,1275,1277],{"href":55,"rel":1276},[],"Container Virtual Registry documentation",[74,1279,1280],{},[53,1281,1284],{"href":1282,"rel":1283},"https://docs.gitlab.com/api/container_virtual_registries/",[],"Container Virtual Registry API",[1286,1287,1288],"style",{},"html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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 .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}",{"title":139,"searchDepth":264,"depth":264,"links":1290},[1291,1292,1293,1294,1303,1304,1309,1310,1311,1312],{"id":65,"depth":264,"text":66},{"id":127,"depth":264,"text":128},{"id":154,"depth":264,"text":155},{"id":226,"depth":264,"text":227,"children":1295},[1296,1297,1298,1299,1300,1301,1302],{"id":234,"depth":271,"text":235},{"id":397,"depth":271,"text":398},{"id":470,"depth":271,"text":471},{"id":564,"depth":271,"text":565},{"id":692,"depth":271,"text":693},{"id":858,"depth":271,"text":859},{"id":923,"depth":271,"text":924},{"id":1016,"depth":264,"text":1017},{"id":1079,"depth":264,"text":1080,"children":1305},[1306,1307,1308],{"id":1086,"depth":271,"text":1087},{"id":1172,"depth":271,"text":1173},{"id":1179,"depth":271,"text":1180},{"id":1191,"depth":264,"text":1192},{"id":1216,"depth":264,"text":1217},{"id":1234,"depth":264,"text":1235},{"id":1261,"depth":264,"text":1262},"engineering","2026-03-12","Learn how to simplify container image management with this step-by-step guide.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772111172/mwhgbjawn62kymfwrhle.png",{},"/en-us/blog/using-gitlab-container-virtual-registry-with-docker-hardened-images",{"config":1323,"title":1324,"description":1315},{"noIndex":1318},"GitLab Container Virtual Registry with Docker Hardened Images","using-gitlab-container-virtual-registry-with-docker-hardened-images","en-us/blog/using-gitlab-container-virtual-registry-with-docker-hardened-images",[1328,1329,1330],"tutorial","product","features","BlogPost","Tx5Og40-DQgBP6eeSeJ8Ky12533ws0MTJKPcP8cp7s0",{"logo":1334,"freeTrial":1339,"sales":1344,"login":1349,"items":1354,"search":1681,"minimal":1712,"duo":1731,"switchNav":1740,"pricingDeployment":1751},{"config":1335},{"href":1336,"dataGaName":1337,"dataGaLocation":1338},"/","gitlab logo","header",{"text":1340,"config":1341},"Get free trial",{"href":1342,"dataGaName":1343,"dataGaLocation":1338},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":1345,"config":1346},"Request a demo",{"href":1347,"dataGaName":1348,"dataGaLocation":1338},"/sales/?contact-topic=request-demo","sales",{"text":1350,"config":1351},"Sign in",{"href":1352,"dataGaName":1353,"dataGaLocation":1338},"https://gitlab.com/users/sign_in/","sign in",[1355,1384,1484,1489,1603,1659],{"text":1356,"config":1357,"menu":1359},"Platform",{"dataNavLevelOne":1358},"platform",{"type":1360,"columns":1361},"cards",[1362,1368,1376],{"title":1356,"description":1363,"link":1364},"The intelligent orchestration platform for DevSecOps",{"text":1365,"config":1366},"Explore our Platform",{"href":1367,"dataGaName":1358,"dataGaLocation":1338},"/platform/",{"title":1369,"description":1370,"link":1371},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":1372,"config":1373},"Meet GitLab Duo",{"href":1374,"dataGaName":1375,"dataGaLocation":1338},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":1377,"description":1378,"link":1379},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":1380,"config":1381},"Learn more",{"href":1382,"dataGaName":1383,"dataGaLocation":1338},"/why-gitlab/","why gitlab",{"text":1385,"left":267,"config":1386,"menu":1388},"Product",{"dataNavLevelOne":1387},"solutions",{"type":1389,"link":1390,"columns":1394,"feature":1463},"lists",{"text":1391,"config":1392},"View all Solutions",{"href":1393,"dataGaName":1387,"dataGaLocation":1338},"/solutions/",[1395,1419,1442],{"title":1396,"description":1397,"link":1398,"items":1403},"Automation","CI/CD and automation to accelerate deployment",{"config":1399},{"icon":1400,"href":1401,"dataGaName":1402,"dataGaLocation":1338},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[1404,1408,1411,1415],{"text":1405,"config":1406},"CI/CD",{"href":1407,"dataGaLocation":1338,"dataGaName":1405},"/solutions/continuous-integration/",{"text":1369,"config":1409},{"href":1374,"dataGaLocation":1338,"dataGaName":1410},"gitlab duo agent platform - product menu",{"text":1412,"config":1413},"Source Code Management",{"href":1414,"dataGaLocation":1338,"dataGaName":1412},"/solutions/source-code-management/",{"text":1416,"config":1417},"Automated Software Delivery",{"href":1401,"dataGaLocation":1338,"dataGaName":1418},"Automated software delivery",{"title":1420,"description":1421,"link":1422,"items":1427},"Security","Deliver code faster without compromising security",{"config":1423},{"href":1424,"dataGaName":1425,"dataGaLocation":1338,"icon":1426},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[1428,1432,1437],{"text":1429,"config":1430},"Application Security Testing",{"href":1424,"dataGaName":1431,"dataGaLocation":1338},"Application security testing",{"text":1433,"config":1434},"Software Supply Chain Security",{"href":1435,"dataGaLocation":1338,"dataGaName":1436},"/solutions/supply-chain/","Software supply chain security",{"text":1438,"config":1439},"Software Compliance",{"href":1440,"dataGaName":1441,"dataGaLocation":1338},"/solutions/software-compliance/","software compliance",{"title":1443,"link":1444,"items":1449},"Measurement",{"config":1445},{"icon":1446,"href":1447,"dataGaName":1448,"dataGaLocation":1338},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[1450,1454,1458],{"text":1451,"config":1452},"Visibility & Measurement",{"href":1447,"dataGaLocation":1338,"dataGaName":1453},"Visibility and Measurement",{"text":1455,"config":1456},"Value Stream Management",{"href":1457,"dataGaLocation":1338,"dataGaName":1455},"/solutions/value-stream-management/",{"text":1459,"config":1460},"Analytics & Insights",{"href":1461,"dataGaLocation":1338,"dataGaName":1462},"/solutions/analytics-and-insights/","Analytics and insights",{"title":1464,"type":1389,"items":1465},"GitLab for",[1466,1472,1478],{"text":1467,"config":1468},"Enterprise",{"icon":1469,"href":1470,"dataGaLocation":1338,"dataGaName":1471},"Building","/enterprise/","enterprise",{"text":1473,"config":1474},"Small Business",{"icon":1475,"href":1476,"dataGaLocation":1338,"dataGaName":1477},"Work","/small-business/","small business",{"text":1479,"config":1480},"Public Sector",{"icon":1481,"href":1482,"dataGaLocation":1338,"dataGaName":1483},"Organization","/solutions/public-sector/","public sector",{"text":1485,"config":1486},"Pricing",{"href":1487,"dataGaName":1488,"dataGaLocation":1338,"dataNavLevelOne":1488},"/pricing/","pricing",{"text":1490,"config":1491,"menu":1493},"Resources",{"dataNavLevelOne":1492},"resources",{"type":1389,"link":1494,"columns":1498,"feature":1592},{"text":1495,"config":1496},"View all resources",{"href":1497,"dataGaName":1492,"dataGaLocation":1338},"/resources/",[1499,1532,1559],{"title":1500,"items":1501},"Getting started",[1502,1507,1512,1517,1522,1527],{"text":1503,"config":1504},"Install",{"href":1505,"dataGaName":1506,"dataGaLocation":1338},"/install/","install",{"text":1508,"config":1509},"Quick start guides",{"href":1510,"dataGaName":1511,"dataGaLocation":1338},"/get-started/","quick setup checklists",{"text":1513,"config":1514},"Learn",{"href":1515,"dataGaLocation":1338,"dataGaName":1516},"https://university.gitlab.com/","learn",{"text":1518,"config":1519},"Product documentation",{"href":1520,"dataGaName":1521,"dataGaLocation":1338},"https://docs.gitlab.com/","product documentation",{"text":1523,"config":1524},"Best practice videos",{"href":1525,"dataGaName":1526,"dataGaLocation":1338},"/getting-started-videos/","best practice videos",{"text":1528,"config":1529},"Integrations",{"href":1530,"dataGaName":1531,"dataGaLocation":1338},"/integrations/","integrations",{"title":1533,"items":1534},"Discover",[1535,1540,1545,1550,1554],{"text":1536,"config":1537},"Customer success stories",{"href":1538,"dataGaName":1539,"dataGaLocation":1338},"/customers/","customer success stories",{"text":1541,"config":1542},"Blog",{"href":1543,"dataGaName":1544,"dataGaLocation":1338},"/blog/","blog",{"text":1546,"config":1547},"Demo Hub",{"href":1548,"dataGaName":1549,"dataGaLocation":1338},"/demo-hub/","demo hub",{"text":1551,"config":1552},"The Source",{"href":1553,"dataGaName":1544,"dataGaLocation":1338},"/the-source/",{"text":1555,"config":1556},"Remote",{"href":1557,"dataGaName":1558,"dataGaLocation":1338},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":1560,"items":1561},"Connect",[1562,1567,1572,1577,1582,1587],{"text":1563,"config":1564},"GitLab Services",{"href":1565,"dataGaName":1566,"dataGaLocation":1338},"/services/","services",{"text":1568,"config":1569},"Contribute",{"href":1570,"dataGaName":1571,"dataGaLocation":1338},"https://contributors.gitlab.com","contribute",{"text":1573,"config":1574},"Community",{"href":1575,"dataGaName":1576,"dataGaLocation":1338},"/community/","community",{"text":1578,"config":1579},"Forum",{"href":1580,"dataGaName":1581,"dataGaLocation":1338},"https://forum.gitlab.com/","forum",{"text":1583,"config":1584},"Events",{"href":1585,"dataGaName":1586,"dataGaLocation":1338},"/events/","events",{"text":1588,"config":1589},"Partners",{"href":1590,"dataGaName":1591,"dataGaLocation":1338},"/partners/","partners",{"config":1593,"title":1596,"text":1597,"link":1598},{"background":1594,"textColor":1595},"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":1599,"config":1600},"Read the latest",{"href":1601,"dataGaName":1602,"dataGaLocation":1338},"/whats-new/","whats new",{"text":1604,"config":1605,"menu":1607},"Company",{"dataNavLevelOne":1606},"company",{"type":1389,"columns":1608},[1609],{"items":1610},[1611,1616,1622,1624,1629,1634,1639,1644,1649,1654],{"text":1612,"config":1613},"About",{"href":1614,"dataGaName":1615,"dataGaLocation":1338},"/company/","about",{"text":1617,"config":1618,"footerGa":1621},"Jobs",{"href":1619,"dataGaName":1620,"dataGaLocation":1338},"/jobs/","jobs",{"dataGaName":1620},{"text":1583,"config":1623},{"href":1585,"dataGaName":1586,"dataGaLocation":1338},{"text":1625,"config":1626},"Leadership",{"href":1627,"dataGaName":1628,"dataGaLocation":1338},"/company/team/e-group/","leadership",{"text":1630,"config":1631},"Handbook",{"href":1632,"dataGaName":1633,"dataGaLocation":1338},"https://handbook.gitlab.com/","handbook",{"text":1635,"config":1636},"Investor relations",{"href":1637,"dataGaName":1638,"dataGaLocation":1338},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1640,"config":1641},"Trust Center",{"href":1642,"dataGaName":1643,"dataGaLocation":1338},"/security/","trust center",{"text":1645,"config":1646},"AI Transparency Center",{"href":1647,"dataGaName":1648,"dataGaLocation":1338},"/ai-transparency-center/","ai transparency center",{"text":1650,"config":1651},"Newsletter",{"href":1652,"dataGaName":1653,"dataGaLocation":1338},"/company/contact/#contact-forms","newsletter",{"text":1655,"config":1656},"Press",{"href":1657,"dataGaName":1658,"dataGaLocation":1338},"/press/","press",{"text":1660,"config":1661,"menu":1662},"Contact us",{"dataNavLevelOne":1606},{"type":1389,"columns":1663},[1664],{"items":1665},[1666,1671,1676],{"text":1667,"config":1668},"Talk to sales",{"href":1669,"dataGaName":1670,"dataGaLocation":1338},"/sales/","talk to sales",{"text":1672,"config":1673},"Support portal",{"href":1674,"dataGaName":1675,"dataGaLocation":1338},"https://support.gitlab.com/hc/en-us","support portal",{"text":1677,"config":1678},"Customer portal",{"href":1679,"dataGaName":1680,"dataGaLocation":1338},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1682,"login":1683,"suggestions":1690},"Close",{"text":1684,"link":1685},"To search repositories and projects, login to",{"text":1686,"config":1687},"gitlab.com",{"href":1352,"dataGaName":1688,"dataGaLocation":1689},"search login","search",{"text":1691,"default":1692},"Suggestions",[1693,1695,1699,1701,1705,1709],{"text":1369,"config":1694},{"href":1374,"dataGaName":1369,"dataGaLocation":1689},{"text":1696,"config":1697},"Code Suggestions (AI)",{"href":1698,"dataGaName":1696,"dataGaLocation":1689},"/solutions/code-suggestions/",{"text":1405,"config":1700},{"href":1407,"dataGaName":1405,"dataGaLocation":1689},{"text":1702,"config":1703},"GitLab on AWS",{"href":1704,"dataGaName":1702,"dataGaLocation":1689},"/partners/technology-partners/aws/",{"text":1706,"config":1707},"GitLab on Google Cloud",{"href":1708,"dataGaName":1706,"dataGaLocation":1689},"/partners/technology-partners/google-cloud-platform/",{"text":1710,"config":1711},"Why GitLab?",{"href":1382,"dataGaName":1710,"dataGaLocation":1689},{"freeTrial":1713,"mobileIcon":1718,"desktopIcon":1723,"secondaryButton":1726},{"text":1714,"config":1715},"Start free trial",{"href":1716,"dataGaName":1343,"dataGaLocation":1717},"https://gitlab.com/-/trials/new/","nav",{"altText":1719,"config":1720},"Gitlab Icon",{"src":1721,"dataGaName":1722,"dataGaLocation":1717},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1719,"config":1724},{"src":1725,"dataGaName":1722,"dataGaLocation":1717},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1727,"config":1728},"Get Started",{"href":1729,"dataGaName":1730,"dataGaLocation":1717},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1732,"mobileIcon":1736,"desktopIcon":1738},{"text":1733,"config":1734},"Learn more about GitLab Duo",{"href":1374,"dataGaName":1735,"dataGaLocation":1717},"gitlab duo",{"altText":1719,"config":1737},{"src":1721,"dataGaName":1722,"dataGaLocation":1717},{"altText":1719,"config":1739},{"src":1725,"dataGaName":1722,"dataGaLocation":1717},{"button":1741,"mobileIcon":1746,"desktopIcon":1748},{"text":1742,"config":1743},"/switch",{"href":1744,"dataGaName":1745,"dataGaLocation":1717},"#contact","switch",{"altText":1719,"config":1747},{"src":1721,"dataGaName":1722,"dataGaLocation":1717},{"altText":1719,"config":1749},{"src":1750,"dataGaName":1722,"dataGaLocation":1717},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1752,"mobileIcon":1757,"desktopIcon":1759},{"text":1753,"config":1754},"Back to pricing",{"href":1487,"dataGaName":1755,"dataGaLocation":1717,"icon":1756},"back to pricing","GoBack",{"altText":1719,"config":1758},{"src":1721,"dataGaName":1722,"dataGaLocation":1717},{"altText":1719,"config":1760},{"src":1725,"dataGaName":1722,"dataGaLocation":1717},{"title":1762,"titleMobile":1763,"button":1764,"config":1769},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":1380,"config":1765},{"href":1766,"dataGaName":1767,"dataGaLocation":1768},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1770,"disabled":1318},"release",{"data":1772},{"text":1773,"source":1774,"edit":1780,"contribute":1785,"config":1790,"items":1795,"minimal":2005},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1775,"config":1776},"View page source",{"href":1777,"dataGaName":1778,"dataGaLocation":1779},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1781,"config":1782},"Edit this page",{"href":1783,"dataGaName":1784,"dataGaLocation":1779},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1786,"config":1787},"Please contribute",{"href":1788,"dataGaName":1789,"dataGaLocation":1779},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1791,"facebook":1792,"youtube":1793,"linkedin":1794},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1796,1843,1897,1941,1973],{"title":1485,"links":1797,"subMenu":1812},[1798,1802,1807],{"text":1799,"config":1800},"View plans",{"href":1487,"dataGaName":1801,"dataGaLocation":1779},"view plans",{"text":1803,"config":1804},"Why Premium?",{"href":1805,"dataGaName":1806,"dataGaLocation":1779},"/pricing/premium/","why premium",{"text":1808,"config":1809},"Why Ultimate?",{"href":1810,"dataGaName":1811,"dataGaLocation":1779},"/pricing/ultimate/","why ultimate",[1813],{"title":1814,"links":1815},"Contact Us",[1816,1819,1821,1823,1828,1833,1838],{"text":1817,"config":1818},"Contact sales",{"href":1669,"dataGaName":1348,"dataGaLocation":1779},{"text":1672,"config":1820},{"href":1674,"dataGaName":1675,"dataGaLocation":1779},{"text":1677,"config":1822},{"href":1679,"dataGaName":1680,"dataGaLocation":1779},{"text":1824,"config":1825},"Status",{"href":1826,"dataGaName":1827,"dataGaLocation":1779},"https://status.gitlab.com/","status",{"text":1829,"config":1830},"Terms of use",{"href":1831,"dataGaName":1832,"dataGaLocation":1779},"/terms/","terms of use",{"text":1834,"config":1835},"Privacy statement",{"href":1836,"dataGaName":1837,"dataGaLocation":1779},"/privacy/","privacy statement",{"text":1839,"config":1840},"Cookie preferences",{"dataGaName":1841,"dataGaLocation":1779,"id":1842,"isOneTrustButton":267},"cookie preferences","ot-sdk-btn",{"title":1385,"links":1844,"subMenu":1853},[1845,1849],{"text":1846,"config":1847},"DevSecOps platform",{"href":1367,"dataGaName":1848,"dataGaLocation":1779},"devsecops platform",{"text":1850,"config":1851},"AI-Assisted Development",{"href":1374,"dataGaName":1852,"dataGaLocation":1779},"ai-assisted development",[1854],{"title":1855,"links":1856},"Topics",[1857,1862,1867,1872,1877,1882,1887,1892],{"text":1858,"config":1859},"CICD",{"href":1860,"dataGaName":1861,"dataGaLocation":1779},"/topics/ci-cd/","cicd",{"text":1863,"config":1864},"GitOps",{"href":1865,"dataGaName":1866,"dataGaLocation":1779},"/topics/gitops/","gitops",{"text":1868,"config":1869},"DevOps",{"href":1870,"dataGaName":1871,"dataGaLocation":1779},"/topics/devops/","devops",{"text":1873,"config":1874},"Version Control",{"href":1875,"dataGaName":1876,"dataGaLocation":1779},"/topics/version-control/","version control",{"text":1878,"config":1879},"DevSecOps",{"href":1880,"dataGaName":1881,"dataGaLocation":1779},"/topics/devsecops/","devsecops",{"text":1883,"config":1884},"Cloud Native",{"href":1885,"dataGaName":1886,"dataGaLocation":1779},"/topics/cloud-native/","cloud native",{"text":1888,"config":1889},"AI for Coding",{"href":1890,"dataGaName":1891,"dataGaLocation":1779},"/topics/devops/ai-for-coding/","ai for coding",{"text":1893,"config":1894},"Agentic AI",{"href":1895,"dataGaName":1896,"dataGaLocation":1779},"/topics/agentic-ai/","agentic ai",{"title":1898,"links":1899},"Solutions",[1900,1902,1904,1909,1913,1916,1920,1923,1925,1928,1931,1936],{"text":1429,"config":1901},{"href":1424,"dataGaName":1429,"dataGaLocation":1779},{"text":1418,"config":1903},{"href":1401,"dataGaName":1402,"dataGaLocation":1779},{"text":1905,"config":1906},"Agile development",{"href":1907,"dataGaName":1908,"dataGaLocation":1779},"/solutions/agile-delivery/","agile delivery",{"text":1910,"config":1911},"SCM",{"href":1414,"dataGaName":1912,"dataGaLocation":1779},"source code management",{"text":1858,"config":1914},{"href":1407,"dataGaName":1915,"dataGaLocation":1779},"continuous integration & delivery",{"text":1917,"config":1918},"Value stream management",{"href":1457,"dataGaName":1919,"dataGaLocation":1779},"value stream management",{"text":1863,"config":1921},{"href":1922,"dataGaName":1866,"dataGaLocation":1779},"/solutions/gitops/",{"text":1467,"config":1924},{"href":1470,"dataGaName":1471,"dataGaLocation":1779},{"text":1926,"config":1927},"Small business",{"href":1476,"dataGaName":1477,"dataGaLocation":1779},{"text":1929,"config":1930},"Public sector",{"href":1482,"dataGaName":1483,"dataGaLocation":1779},{"text":1932,"config":1933},"Education",{"href":1934,"dataGaName":1935,"dataGaLocation":1779},"/solutions/education/","education",{"text":1937,"config":1938},"Financial services",{"href":1939,"dataGaName":1940,"dataGaLocation":1779},"/solutions/finance/","financial services",{"title":1490,"links":1942},[1943,1945,1947,1949,1952,1954,1957,1959,1961,1963,1965,1967,1969,1971],{"text":1503,"config":1944},{"href":1505,"dataGaName":1506,"dataGaLocation":1779},{"text":1508,"config":1946},{"href":1510,"dataGaName":1511,"dataGaLocation":1779},{"text":1513,"config":1948},{"href":1515,"dataGaName":1516,"dataGaLocation":1779},{"text":1518,"config":1950},{"href":1520,"dataGaName":1951,"dataGaLocation":1779},"docs",{"text":1541,"config":1953},{"href":1543,"dataGaName":1544,"dataGaLocation":1779},{"text":1955,"config":1956},"What's new",{"href":1601,"dataGaName":1602,"dataGaLocation":1779},{"text":1536,"config":1958},{"href":1538,"dataGaName":1539,"dataGaLocation":1779},{"text":1555,"config":1960},{"href":1557,"dataGaName":1558,"dataGaLocation":1779},{"text":1563,"config":1962},{"href":1565,"dataGaName":1566,"dataGaLocation":1779},{"text":1568,"config":1964},{"href":1570,"dataGaName":1571,"dataGaLocation":1779},{"text":1573,"config":1966},{"href":1575,"dataGaName":1576,"dataGaLocation":1779},{"text":1578,"config":1968},{"href":1580,"dataGaName":1581,"dataGaLocation":1779},{"text":1583,"config":1970},{"href":1585,"dataGaName":1586,"dataGaLocation":1779},{"text":1588,"config":1972},{"href":1590,"dataGaName":1591,"dataGaLocation":1779},{"title":1604,"links":1974},[1975,1977,1979,1981,1983,1985,1989,1994,1996,1998,2000],{"text":1612,"config":1976},{"href":1614,"dataGaName":1606,"dataGaLocation":1779},{"text":1617,"config":1978},{"href":1619,"dataGaName":1620,"dataGaLocation":1779},{"text":1625,"config":1980},{"href":1627,"dataGaName":1628,"dataGaLocation":1779},{"text":1630,"config":1982},{"href":1632,"dataGaName":1633,"dataGaLocation":1779},{"text":1635,"config":1984},{"href":1637,"dataGaName":1638,"dataGaLocation":1779},{"text":1986,"config":1987},"Sustainability",{"href":1988,"dataGaName":1986,"dataGaLocation":1779},"/sustainability/",{"text":1990,"config":1991},"Diversity, inclusion and belonging (DIB)",{"href":1992,"dataGaName":1993,"dataGaLocation":1779},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1640,"config":1995},{"href":1642,"dataGaName":1643,"dataGaLocation":1779},{"text":1650,"config":1997},{"href":1652,"dataGaName":1653,"dataGaLocation":1779},{"text":1655,"config":1999},{"href":1657,"dataGaName":1658,"dataGaLocation":1779},{"text":2001,"config":2002},"Modern Slavery Transparency Statement",{"href":2003,"dataGaName":2004,"dataGaLocation":1779},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":2006},[2007,2010,2013],{"text":2008,"config":2009},"Terms",{"href":1831,"dataGaName":1832,"dataGaLocation":1779},{"text":2011,"config":2012},"Cookies",{"dataGaName":1841,"dataGaLocation":1779,"id":1842,"isOneTrustButton":267},{"text":2014,"config":2015},"Privacy",{"href":1836,"dataGaName":1837,"dataGaLocation":1779},[2017],{"id":2018,"title":7,"body":1317,"config":2019,"content":2021,"description":1317,"extension":2025,"meta":2026,"navigation":267,"path":2027,"seo":2028,"stem":2029,"__hash__":2030},"blogAuthors/en-us/blog/authors/tim-rizzi.yml",{"template":2020},"BlogAuthor",{"name":7,"config":2022},{"headshot":2023,"ctfId":2024},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749661866/Blog/Author%20Headshots/trizzi-headshot.jpg","trizzi","yml",{},"/en-us/blog/authors/tim-rizzi",{},"en-us/blog/authors/tim-rizzi","ADPqrpcnKveFJS0m_zFV0VLtb_h_txu59QVgz_YwKMc",[2032,2041,2049],{"title":2033,"description":2034,"heroImage":2035,"category":1313,"date":2036,"authors":2037,"slug":2040,"externalUrl":1317},"Confidential AI for GitLab Self-Hosted","Give developers AI coding agents in GitLab Duo without source code leaving a hardware-encrypted boundary — no GPUs needed.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773866173/vte9qh8rriznvyclhkes.png","2026-08-06",[2038,2039],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":2042,"description":2043,"heroImage":2044,"category":1313,"date":2045,"authors":2046,"slug":2048,"externalUrl":1317},"Green DevOps: Why carbon measurement belongs in your CI/CD pipeline","CI/CD pipelines have a hidden carbon cost. Here's why measuring it matters, and how you can get started with Eco CI and Carmen in GitLab.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1765809212/noh0mdfn9o94ry9ykura.png","2026-07-09",[2047],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":2050,"description":2051,"heroImage":2052,"category":1313,"date":2053,"authors":2054,"slug":2056,"externalUrl":1317},"How to build CI/CD observability at scale","This practical guide to GitLab pipeline analytics helps self-managed users gain operational insights using Prometheus and Grafana.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-28",[2055],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":2058},[2059,2073,2084,2096],{"id":2060,"categories":2061,"header":2063,"text":2064,"button":2065,"image":2070},"ai-modernization",[2062],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":2066,"config":2067},"Get your AI maturity score",{"href":2068,"dataGaName":2069,"dataGaLocation":1544},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":2071},{"src":2072},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":2074,"categories":2075,"header":2076,"text":2064,"button":2077,"image":2081},"devops-modernization",[1329,1881],"Are you just managing tools or shipping innovation?",{"text":2078,"config":2079},"Get your DevOps maturity score",{"href":2080,"dataGaName":2069,"dataGaLocation":1544},"/assessments/devops-modernization-assessment/",{"config":2082},{"src":2083},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":2085,"categories":2086,"header":2088,"text":2064,"button":2089,"image":2093},"security-modernization",[2087],"security","Are you trading speed for security?",{"text":2090,"config":2091},"Get your security maturity score",{"href":2092,"dataGaName":2069,"dataGaLocation":1544},"/assessments/security-modernization-assessment/",{"config":2094},{"src":2095},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":2097,"paths":2098,"header":2101,"text":2102,"button":2103,"image":2108},"github-azure-migration",[2099,2100],"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":2104,"config":2105},"See how GitLab compares to GitHub",{"href":2106,"dataGaName":2107,"dataGaLocation":1544},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":2109},{"src":2083},{"header":2111,"blurb":2112,"button":2113,"secondaryButton":2118},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":2114,"config":2115},"Get your free trial",{"href":2116,"dataGaName":1343,"dataGaLocation":2117},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1817,"config":2119},{"href":1669,"dataGaName":1348,"dataGaLocation":2117},1786803754113]