Home / Serverless

Artifact Registry in Production: Remote/Virtual Repos, Cleanup Policies & IAM | 2026

Artifact Registry in Production: Remote/Virtual Repos, Cleanup Policies & IAM | 2026

Google Artifact Registry is GCP's enterprise artifact management platform for Docker images, Helm charts, Maven, npm, Python, and Go packages. In enterprise production environments, Artifact Registry goes beyond basic storage: Remote Repositories proxy and cache public registries (Docker Hub, PyPI, npm) to protect against upstream outages; Virtual Repositories combine multiple repositories under one single URL endpoint; and Cleanup Policies automatically delete old untagged artifacts. This guide demonstrates how to configure Virtual/Remote repositories, automated cleanup rules, KMS encryption, and IAM security controls in Terraform.

By Mateusz Chmielewski · Aug 29, 2026 · 16 min read

Standard, Remote, and Virtual Repositories Explained

Artifact Registry supports three repository modes: Standard repositories store proprietary artifacts built internally; Remote repositories act as pull-through caching proxies for external public registries (e.g. Docker Hub, npmjs, PyPI); and Virtual repositories aggregate multiple Standard and Remote repositories behind a single unified URL endpoint.

Think of Standard repositories like your company's private warehouse, Remote repositories like a local buffer hub stocking popular items from external suppliers, and a Virtual Repository like an intelligent concierge counter. When an engineer orders a package, they ask the concierge (Virtual Repo) — the concierge first checks the private warehouse (Standard Repo), then the buffer hub (Remote Repo cache), and if missing, fetches it from the factory (Docker Hub) and caches a copy for next time.

ConceptExplanationWhen to use
Standard RepositoryPrimary repository storing container images, language packages, or Helm charts published by your organization.Target destination for CI/CD build outputs (Cloud Build, GitHub Actions).
Remote RepositoryCaching proxy that intercepts requests for external registries (Docker Hub, PyPI, Maven) and caches artifacts inside your GCP project.Protect against public registry rate limits (Docker Hub 200 pulls/6h) and upstream outages.
Virtual RepositorySingle endpoint combining multiple Standard and Remote repos with configured search priority order.Provide developers and Kubernetes clusters with a single static registry URL.
Cleanup PolicyRule set automatically deleting artifacts matching criteria like tag prefix, age, or version count threshold.Keep production repositories clean and control storage costs automatically.

Why Enterprise Teams Need Remote/Virtual Repositories & Cleanup Policies

Relying directly on public registries like Docker Hub subjects production GKE/Cloud Run clusters to Docker Hub rate limits (`429 Too Many Requests`) and public outage risks. Unmanaged registries accumulate thousands of untagged, ephemeral CI/CD container builds over time, inflating GCP billing by hundreds of dollars per month.

Remote Repositories cache third-party base images inside GCP's internal network with zero rate limits. Virtual Repositories simplify developer authentication to a single domain. Automated Cleanup Policies purge ephemeral PR builds after 14 days, maintaining strict storage quotas. Integrate directly with [Cloud Build Production Pipelines](/tutorial/cloud-build-production-pipeline-triggers-artifact-registry-scanning-cloud-run), enforce image signatures via [Binary Authorization](/tutorial/binary-authorization-artifact-registry-signed-images-gke), and encrypt stored images with [CMEK Cloud KMS Keys](/tutorial/cmek-cloud-kms-disks-buckets-bigquery-terraform-rotation).

FeaturethisServicealtAaltB
Upstream AvailabilityCached in GCP (Resilient to Docker Hub outages)Direct pull from Docker Hub (Vulnerable)Manual mirror sync script
Pull Rate LimitsUnlimited intra-region pulls200 pulls / 6 hours per IPN/A
Storage Cost OptimizationAutomated Cleanup Policies (TTL + Keep Most Recent)Manual image deletion scriptsNo cleanup (growing storage bill)
Developer ExperienceSingle Virtual Repo URL for all internal/external packagesMultiple registry domain URLsMultiple registry domain URLs
Security & ScanningNative Vulnerability Scanning & CMEK EncryptionDependent on external registryManual scanning

Prerequisites

  • GCP Project with Artifact Registry API enabled
  • Terraform CLI v1.6+ configured
  • Docker CLI installed and authenticated via `gcloud auth configure-docker`

Step-by-Step Guide

Step 1: Configure Standard Docker Repository with CMEK Encryption in Terraform

Create a Standard Docker repository encrypted with a Customer-Managed Encryption Key (CMEK) from Cloud KMS. CMEK encryption guarantees compliance requirements by encrypting container images at rest using keys controlled by your security team.

# standard_repo.tf — Standard Docker Repository with CMEK
resource "google_artifact_registry_repository" "internal_docker" {
  location      = "europe-west1"
  repository_id = "internal-docker-repo"
  description   = "Proprietary internal container images"
  format        = "DOCKER"
  project       = var.project_id
  kms_key_name  = google_kms_crypto_key.ar_key.id

  docker_config {
    immutable_tags = false # Set true if tag mutability is prohibited
  }
}

Step 2: Deploy Remote Repository Caching Proxy for Docker Hub

Create a Remote Repository acting as a pull-through cache for Docker Hub images. Remote Repositories cache pulled images inside your region. Subsequent pulls by GKE or Cloud Run hit local Artifact Registry cache instead of Docker Hub.

# remote_repo.tf — Remote Repository proxying Docker Hub
resource "google_artifact_registry_repository" "dockerhub_remote" {
  location      = "europe-west1"
  repository_id = "dockerhub-remote-cache"
  description   = "Pull-through caching proxy for Docker Hub"
  format        = "DOCKER"
  mode          = "REMOTE_REPOSITORY"
  project       = var.project_id

  remote_repository_config {
    description = "Docker Hub official registry proxy"
    docker_repository {
      public_repository = "DOCKER_HUB"
    }
  }
}

Step 3: Create Virtual Repository Aggregating Internal and Remote Sources

Create a Virtual Repository unifying `internal-docker-repo` and `dockerhub-remote-cache` under a single registry URL. Engineers and Kubernetes manifests configure a single registry URL. Artifact Registry evaluates upstream repositories in specified priority order.

# virtual_repo.tf — Virtual Repository unifying internal and remote repos
resource "google_artifact_registry_repository" "virtual_docker" {
  location      = "europe-west1"
  repository_id = "unified-docker-virtual"
  description   = "Unified Virtual Repository for internal and cached images"
  format        = "DOCKER"
  mode          = "VIRTUAL_REPOSITORY"
  project       = var.project_id

  virtual_repository_config {
    upstream_policies {
      id         = "internal-repo-policy"
      repository = google_artifact_registry_repository.internal_docker.id
      priority   = 100 # Highest priority: check internal repo first
    }

    upstream_policies {
      id         = "dockerhub-cache-policy"
      repository = google_artifact_registry_repository.dockerhub_remote.id
      priority   = 200 # Second priority: fallback to Remote cache
    }
  }
}

Step 4: Implement Automated Cleanup Policies for Tagged and Untagged Artifacts

Define declarative Cleanup Policies on the Standard repository to prune untagged images and keep only the 10 most recent release versions. Without automated cleanup, repositories accumulate gigabytes of obsolete build artifacts, inflating storage costs and slowing down vulnerability scanning.

# cleanup_policies.tf — Automated cleanup and retention rules
resource "google_artifact_registry_repository" "managed_repo" {
  location      = "europe-west1"
  repository_id = "app-artifacts-with-cleanup"
  format        = "DOCKER"
  project       = var.project_id

  cleanup_policy_dry_run = false # Set true during testing to preview deletions

  # Rule 1: Delete untagged container images older than 7 days
  cleanup_policies {
    id     = "delete-untagged-after-7-days"
    action = "DELETE"
    condition {
      tag_state  = "UNTAGGED"
      older_than = "604800s" # 7 days in seconds
    }
  }

  # Rule 2: Delete dev/PR preview tags older than 14 days
  cleanup_policies {
    id     = "delete-pr-previews-after-14-days"
    action = "DELETE"
    condition {
      tag_state    = "TAGGED"
      tag_prefixes = ["pr-", "dev-", "sha-"]
      older_than   = "1209600s" # 14 days in seconds
    }
  }

  # Rule 3: Keep the 10 most recent release tags regardless of age
  cleanup_policies {
    id     = "keep-recent-release-versions"
    action = "KEEP"
    most_recent_versions {
      package_name = "api-service"
      tag_prefixes = ["v"]
      keep_count   = 10
    }
  }
}

Step 5: Configure Granular IAM Security and Verify Pull/Push

Assign `roles/artifactregistry.reader` to GKE nodes and `roles/artifactregistry.writer` to Cloud Build service accounts. Granting read-only access to deployment environments prevents compromised application pods from overwriting or pushing malicious container images.

# Step 1: Assign Reader role to GKE Node Service Account
gcloud artifacts repositories add-iam-policy-binding unified-docker-virtual \
  --location=europe-west1 \
  --member="serviceAccount:gke-node-sa@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/artifactregistry.reader"

# Step 2: Authenticate Docker CLI to Artifact Registry Virtual Repo
gcloud auth configure-docker europe-west1-docker.pkg.dev

# Step 3: Pull Docker Hub image through Virtual Repository proxy
docker pull europe-west1-docker.pkg.dev/PROJECT_ID/unified-docker-virtual/library/nginx:latest

# Step 4: Verify image was cached in Remote Repository
gcloud artifacts docker images list \
  europe-west1-docker.pkg.dev/PROJECT_ID/dockerhub-remote-cache

Verification & Health Check

Best Practices

  • Use Virtual Repositories to Hide Upstream Registry Topology
  • Enforce Least-Privilege IAM Roles on Repositories

Common Mistakes

  • {"errorCode":"DOCKER_HUB_RATE_LIMIT","symptoms":"Cloud Build or GKE pod startup fails with '429 Too Many Requests' when pulling base images.","rootCause":"Images are being pulled directly from `docker.io` instead of through an Artifact Registry Remote Repository proxy.","fixCommand":"Update Dockerfile `FROM` lines to point to your Artifact Registry Remote Repository cache URL.","code":"# Dockerfile — Pull base image from internal Remote Repository cache\nFROM europe-west1-docker.pkg.dev/PROJECT_ID/dockerhub-remote-cache/library/python:3.11-slim\n","language":"dockerfile","filename":"Dockerfile","prevention":"Configure Remote Repositories for all external registries and enforce Virtual Repo usage via Org Policies."}

Cost Analysis

Featuremetriccost1kcost10kcost100kcost1m
Artifact Registry Storage (500GB @ $0.10/GB/mo)$50.00/mo$50.00/mo$50.00/mo$600.00/yr
Savings from Cleanup Policies (pruning ~1.5 TB untagged)- $150.00/mo- $150.00/mo- $150.00/mo- $1,800.00/yr
Intra-Region Egress (GKE & Cloud Run in same region)$0.00$0.00$0.00$0.00
Net Artifact Registry Cost (with Cleanup Policies)~$50.00/mo~$50.00/mo~$50.00/mo~$600.00/yr

References

Browse all tutorials