Artifact Registry Explained: Docker Images, Packages, and Access Control in GCP
Artifact Registry is GCP’s managed private registry for container images and software packages. Your build pipeline pushes artefacts into it, your deployment tools pull from it, IAM controls who can read or write, and built-in vulnerability scanning keeps you informed about CVEs in your images. It is the modern replacement for Container Registry (gcr.io) and the only registry you should use for new GCP projects.
Simple explanation
Before a container image can run on Cloud Run or GKE, it needs to be stored somewhere that GCP can pull from. Artifact Registry is that storage layer: a managed private registry where your pipeline pushes images and your runtime services pull them at deploy time.
The same pattern applies to language packages. If your team maintains a private npm library, internal Java packages, or shared Python tooling, Artifact Registry can host those too, with the same IAM controls and audit trail as your container images.
Your CI/CD pipeline is the supplier. It builds artefacts and places them on the warehouse shelf (Artifact Registry). Cloud Run, GKE, and other services are the customers. They take artefacts off the shelf at deploy time. IAM is the security guard who decides who is allowed in and whether they can stock shelves or only pick items up.
How Artifact Registry works
You start by creating a repository: a named storage location scoped to a single format (Docker, npm, Maven, and so on) in a specific region. Each repository has its own IAM policy, vulnerability scanning settings, and cleanup rules.
Once a repository exists, your build tools authenticate using Application
Default Credentials or a service account, then push artefacts using the
native client for that format (docker push for images,
npm publish for packages). Every push is recorded in
Cloud Audit Logs,
giving you a full history of who pushed what and when.
At deploy time, services reference artefacts using the full registry URI. For a Docker image, that looks like this:
us-central1-docker.pkg.dev/my-project/my-repo/my-app:v1.2.0The URI encodes the region, project ID, repository name, image name, and tag. IAM determines whether the caller is authorised to pull. If scanning is enabled, Artifact Analysis runs immediately after push and continues checking as new CVE data is published. A clean result at push time can change later as new vulnerabilities are disclosed.
Cleanup policies run on a schedule and remove untagged or stale images, keeping storage costs predictable. Remote repositories let you cache upstream registries like Docker Hub inside your own project, which eliminates rate-limiting problems and reduces external dependency.
When to use Artifact Registry
Artifact Registry is the right choice in most GCP workloads that involve containers or private packages:
Docker images for Cloud Run. Every Cloud Run deployment requires a container image. Hosting that image in Artifact Registry in the same region as your service is the standard pattern.
Application images for GKE. Pod specs reference images by URI. Hosting those in Artifact Registry gives you repository-level IAM and scanning alongside your cluster workloads. See securing GKE clusters for how scanning and Binary Authorization work together in practice.
Private language packages. Internal npm, Maven, or PyPI packages that should not be published publicly belong in a private Artifact Registry repository. Developers configure their package manager to point at the registry URL and authenticate with their GCP credentials.
Upstream package caching. A remote repository proxies Docker Hub or other external registries and caches images locally. This prevents Docker Hub rate-limit errors in Cloud Build pipelines and reduces cross-internet pull latency.
Separating environments. Maintaining separate repositories for dev, staging, and production lets you apply different IAM policies to each. Production repositories can be restricted to deployment service accounts only, while dev repositories remain accessible to engineers. This maps directly to a sound dev, staging, and production strategy.
Artifact Registry vs Container Registry
Container Registry (gcr.io) was GCP’s original Docker registry.
Google announced its deprecation and Artifact Registry is the designated
replacement. As of 2025, Google has transitioned the underlying
infrastructure so that gcr.io requests are served through
Artifact Registry. For any new project, use Artifact Registry only.
Do not use gcr.io for new projects. Container Registry is
being discontinued. Use pkg.dev (Artifact Registry) for
all new repositories.
Format support: Container Registry supported Docker images only. Artifact Registry supports Docker, Helm, npm, Maven, PyPI, APT, RPM, and Go modules — one format per repository.
Better access control: Container Registry stored images
in a Cloud Storage bucket and used bucket-level ACLs. Artifact Registry
uses native IAM at the repository level, so you can grant a service
account reader on one repository and writer on
another within the same project without affecting anything else.
URI format: Container Registry used
gcr.io/PROJECT_ID/image. Artifact Registry uses
REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/image. The
repository name is explicit in the URI, so you choose how to organise
images: one repo per service, per team, or per environment, rather than
having everything land in a single shared bucket.
If you are migrating existing gcr.io images, Google provides
a migration tool that redirects gcr.io pulls to a
corresponding Artifact Registry repository. The
Artifact Registry best practices
guide covers migration patterns in detail.
Creating a repository
Each repository holds one format and lives in one region. Create separate repositories per environment or per team. This keeps IAM boundaries clean and makes it straightforward to apply different cleanup and scanning policies to each.
# Enable the API
gcloud services enable artifactregistry.googleapis.com
# Create a Docker repository
gcloud artifacts repositories create my-docker-repo \
--repository-format=docker \
--location=us-central1 \
--description="Production Docker images"
# Create an npm repository
gcloud artifacts repositories create my-npm-repo \
--repository-format=npm \
--location=us-central1
# List all repositories
gcloud artifacts repositories list --location=us-central1The repository region must match the region where your services run.
Pulling an image across regions adds latency and incurs cross-region
egress charges. For a
Cloud Run service in
us-central1, create the repository in
us-central1.
Pushing and pulling Docker images
Before you can push or pull, Docker needs credentials for Artifact
Registry. The gcloud auth configure-docker command writes a
credential helper into your Docker config so that Docker can obtain
short-lived tokens from GCP automatically.
When you push manually from a local machine, the image URI must include the full Artifact Registry path: region, project ID, repository name, image name, and tag. When Cloud Build builds and pushes images, it uses the same URI format but authenticates automatically with its service account, with no manual credential setup required.
# Configure Docker to authenticate with Artifact Registry
gcloud auth configure-docker us-central1-docker.pkg.dev
# Build, tag, and push
docker build -t us-central1-docker.pkg.dev/PROJECT_ID/my-docker-repo/my-app:v1 .
docker push us-central1-docker.pkg.dev/PROJECT_ID/my-docker-repo/my-app:v1
# Pull an image
docker pull us-central1-docker.pkg.dev/PROJECT_ID/my-docker-repo/my-app:v1
# List images in a repository
gcloud artifacts docker images list \
us-central1-docker.pkg.dev/PROJECT_ID/my-docker-repo
# List tags for a specific image
gcloud artifacts docker tags list \
us-central1-docker.pkg.dev/PROJECT_ID/my-docker-repo/my-appTag images with the Git commit SHA alongside a latest tag.
This makes it straightforward to map a running service back to the exact
commit that produced it, which is invaluable during incident
investigations.
The URI you push is the same URI you reference in Cloud Run deploy commands, Kubernetes pod specs, and Cloud Deploy release pipelines.
Managing access with IAM
Artifact Registry uses standard GCP IAM roles. The three you will use most often are:
roles/artifactregistry.reader: pull images and packagesroles/artifactregistry.writer: push images and packagesroles/artifactregistry.repoAdmin: manage repository settings, delete tags, and configure policies
Bind these at the repository level wherever possible.
This follows the
principle of least privilege:
your production deployment service account should get reader
on the production repository only, not on every repository in the project.
Cloud Build uses a dedicated service account
(PROJECT_NUMBER@cloudbuild.gserviceaccount.com) that needs
writer access to push images it builds.
# Grant a service account pull access (for Cloud Run or GKE)
gcloud artifacts repositories add-iam-policy-binding my-docker-repo \
--location=us-central1 \
--member=serviceAccount:my-sa@PROJECT_ID.iam.gserviceaccount.com \
--role=roles/artifactregistry.reader
# Grant a CI/CD service account push access
gcloud artifacts repositories add-iam-policy-binding my-docker-repo \
--location=us-central1 \
--member=serviceAccount:ci-cd-sa@PROJECT_ID.iam.gserviceaccount.com \
--role=roles/artifactregistry.writer
# Grant Cloud Build service account push access
gcloud artifacts repositories add-iam-policy-binding my-docker-repo \
--location=us-central1 \
--member=serviceAccount:PROJECT_NUMBER@cloudbuild.gserviceaccount.com \
--role=roles/artifactregistry.writerTo restrict registry access to specific VPCs or prevent images from being pulled outside your organisation, see VPC Service Controls applied to the Artifact Registry API.
Vulnerability scanning
Artifact Analysis scans Docker images for known CVEs when they are pushed and continues scanning as new vulnerability data is published. Each finding is a vulnerability occurrence with a CVE identifier, severity (Critical, High, Medium, Low), the affected package version, and whether a patched version exists.
You can query these findings via the Container Analysis API and use the results to block deployments in your secure CI/CD pipeline. Combined with Binary Authorization, you can enforce that only scanned and approved images ever reach production.
A clean scan result at push time does not guarantee the image stays clean. The CVE database is updated continuously. An image that passes today can be flagged tomorrow when a new vulnerability is published, without any change on your part. Treat scan results as a live signal, not a one-time gate.
# Enable the Container Analysis API
gcloud services enable containeranalysis.googleapis.com
# Enable scanning on a repository
gcloud artifacts repositories update my-docker-repo \
--location=us-central1 \
--enable-vulnerability-scanning
# List vulnerability findings for images in a repository
gcloud artifacts docker images list \
us-central1-docker.pkg.dev/PROJECT_ID/my-docker-repo \
--show-occurrences \
--occurrence-filter="kind=VULNERABILITY"Cleanup policies
Without a cleanup policy, every image push adds new layers to storage indefinitely. A busy Cloud Build pipeline pushing on every commit can accumulate hundreds of gigabytes within months. These costs grow quietly until someone notices the billing report.
Cleanup policies run on a schedule. The most common approach deletes untagged images older than a threshold. Untagged images are those superseded by a newer push but never explicitly removed. You can also combine conditions, for example keeping the most recent five tagged versions while deleting everything older.
# Delete untagged images older than 30 days
gcloud artifacts repositories set-cleanup-policies my-docker-repo \
--location=us-central1 \
--policy='[{
"name": "delete-untagged",
"action": {"type": "Delete"},
"condition": {
"tagState": "UNTAGGED",
"olderThan": "2592000s"
}
}]'Remote repositories
A remote repository acts as a caching proxy for an external registry such as Docker Hub, npm, or PyPI. On the first pull, Artifact Registry fetches the image or package from upstream and caches it. Subsequent pulls are served from the cache in your own project.
This solves two common problems in CI/CD. First, Docker Hub enforces rate limits on unauthenticated pulls. This issue surfaces quickly when many build workers pull popular base images at once. Second, caching images in the same region as your build infrastructure reduces pull latency and removes external internet dependency from your pipeline.
# Create a remote repository that proxies Docker Hub
gcloud artifacts repositories create dockerhub-proxy \
--repository-format=docker \
--location=us-central1 \
--mode=remote-repository \
--remote-repo-config-desc="Docker Hub proxy" \
--remote-docker-repo=DOCKER_HUB
# Pull through the proxy
docker pull us-central1-docker.pkg.dev/PROJECT_ID/dockerhub-proxy/nginx:latestRemote repositories also improve resilience. If Docker Hub becomes temporarily unavailable, cached images continue to be served from Artifact Registry. This is particularly useful in production build environments where reliability matters.
Common beginner mistakes
Using Container Registry (gcr.io) for new projects. Container Registry is being discontinued. New projects use Artifact Registry at
pkg.dev. The image naming format changes fromgcr.io/PROJECT_ID/imagetoREGION-docker.pkg.dev/PROJECT_ID/REPO/image.Creating the repository in the wrong region. Pulling an image across regions from a Cloud Run service or GKE cluster adds latency and incurs cross-region egress charges. Match the repository region to the region where your services run.
Granting project-level IAM instead of repository-level IAM. Binding
roles/artifactregistry.writerat the project level gives a service account push access to every repository in the project. Bind at the repository level so a CI account can only push to the repositories it owns. See least privilege in GCP for the reasoning.No cleanup policy, leading to growing storage costs. Every image push creates new layers. Without cleanup, storage costs accumulate quietly. Set a policy to delete untagged images older than 30 days at minimum.
Mixing dev, staging, and production images in one repository. Separate repositories per environment make it easier to audit what is deployed where and apply different IAM policies to each. Production repositories should have stricter access and more aggressive scanning requirements than dev repositories.
Not enabling vulnerability scanning for production repositories. Without scanning, you may deploy containers with known CVEs without realising it. Enable Artifact Analysis on any repository whose images reach production, and consider gating deployments on scan results.
Assuming a clean scan result is permanent. Artifact Analysis scans continuously. A clean result at push time does not mean the image stays clean. New CVEs are published regularly and previously passing images can become flagged. Treat scan results as a live signal.
Summary
- Artifact Registry is GCP’s managed private registry for Docker images, Helm charts, npm, Maven, PyPI, APT, RPM, and Go modules
- It replaces Container Registry (gcr.io): use
pkg.devfor all new projects; Container Registry is being discontinued - Each repository holds one format, lives in one region, and has its own IAM policy, cleanup settings, and scanning configuration
- Access is controlled with IAM at the repository level: reader for pull, writer for push, repoAdmin for management
- Enable Artifact Analysis to scan images for CVEs on push and continuously as new vulnerabilities are published
- Set cleanup policies to delete untagged or stale images and prevent storage cost creep over time
- Use remote repositories to proxy Docker Hub and other upstream registries and eliminate rate-limiting issues in CI
Frequently asked questions
What is Artifact Registry in Google Cloud?
Artifact Registry is GCP's managed private registry for storing container images and software packages. Your build pipeline pushes Docker images, Helm charts, npm packages, Maven JARs, or PyPI distributions into a repository, and your deployment tools (Cloud Run, GKE, Compute Engine, Cloud Build) pull from it at deploy time. It replaces the older Container Registry (gcr.io) service and is the recommended registry for all new GCP projects.
What is the difference between Artifact Registry and Container Registry?
Container Registry (gcr.io) is the older service and is being discontinued by Google. Artifact Registry (pkg.dev) is the replacement. Key differences: Artifact Registry supports multiple formats (Docker, Helm, npm, Maven, PyPI, APT, RPM, Go) while Container Registry only supported Docker. Artifact Registry offers repository-level IAM rather than bucket-level ACLs, so you can grant a service account pull access to one repository without access to everything else in the project. The URI format also changes from gcr.io/PROJECT_ID/image to REGION-docker.pkg.dev/PROJECT_ID/REPO/image. For any new project, use Artifact Registry.
What package formats does Artifact Registry support?
Docker container images, Helm charts, npm, Maven (Java), PyPI (Python), APT (Debian/Ubuntu), RPM (Red Hat/CentOS), and Go modules. Each repository holds a single format; you cannot mix Docker images and npm packages in the same repository. This means Artifact Registry can serve as a private registry for all build artefacts across your organisation, with consistent IAM access control and Cloud Audit Logs across every format.
How do Cloud Run and GKE use Artifact Registry?
Both services pull container images from Artifact Registry at deploy time. When you deploy to Cloud Run, you specify an image URI in the format REGION-docker.pkg.dev/PROJECT_ID/REPO/image:tag. GKE uses the same URI in pod specs. Services in the same GCP project as the registry can typically pull without extra IAM configuration when using default service accounts. For cross-project access or custom service accounts, grant roles/artifactregistry.reader to the relevant service account on the specific repository.
Should I enable vulnerability scanning in Artifact Registry?
Yes, for any repository whose images are deployed to production. Artifact Analysis scans images for known CVEs on push and continues scanning as new vulnerabilities are published. A clean image today may flag tomorrow when a new CVE is discovered. Each finding includes a CVE identifier, severity level, affected package, and whether a fix is available. You can query results via the Container Analysis API to block CI/CD pipelines from deploying images with Critical vulnerabilities.