Google Container Registry stopped accepting new image pushes on March 18, 2025. Google’s own docs are explicit: “Effective March 18, 2025, Container Registry is shut down and writing images to Container Registry is unavailable.” If you’re still setting up gcr.io as a live, working registry, that guide is out of date — this one replaces it.
The nuance that matters: this isn’t a flat “GCR is 100% gone.” Existing gcr.io URLs that were already migrated onto Artifact Registry’s compatibility layer (auto-migration for new projects started May 15, 2024) still resolve for pulls — old images don’t vanish. What’s dead is writes: there is no way to push a new image to a gcr.io URL anymore, on any project, migrated or not. So the real task here isn’t “set up both registries side by side” — it’s “set up Artifact Registry properly, and migrate whatever still points at gcr.io.”
Setting Up Artifact Registry
Enable the API and create a repository with the Google Cloud CLI:
gcloud services enable artifactregistry.googleapis.com
gcloud artifacts repositories create my-repo \
--repository-format=docker \
--location=us-central1 \
--description="Docker images for my-app"
Authenticate Docker to push to it:
gcloud auth configure-docker us-central1-docker.pkg.dev
Then tag and push using Artifact Registry’s URL format, which is the single biggest practical difference from GCR:
docker tag my-app:latest us-central1-docker.pkg.dev/my-project/my-repo/my-app:latest
docker push us-central1-docker.pkg.dev/my-project/my-repo/my-app:latest
Setting IAM Permissions for a Service Account
To let a service account push images, grant it the Artifact Registry Writer role on the repository (or the project, if it needs access to every repo):
gcloud artifacts repositories add-iam-policy-binding my-repo \
--location=us-central1 \
--member="serviceAccount:ci-deployer@my-project.iam.gserviceaccount.com" \
--role="roles/artifactregistry.writer"
The same thing in Terraform, using the real current resource names:
resource "google_artifact_registry_repository" "my_repo" {
location = "us-central1"
repository_id = "my-repo"
format = "DOCKER"
}
resource "google_artifact_registry_repository_iam_member" "ci_writer" {
location = google_artifact_registry_repository.my_repo.location
repository = google_artifact_registry_repository.my_repo.name
role = "roles/artifactregistry.writer"
member = "serviceAccount:ci-deployer@my-project.iam.gserviceaccount.com"
}
For read-only pulls (a deploy target that only needs to fetch images, never push), use roles/artifactregistry.reader instead — don’t hand out writer access to anything that doesn’t actually need to push.
Migrating From GCR: What Actually Breaks
The URL format change is the whole story here. GCR used gcr.io/PROJECT/IMAGE. Artifact Registry uses LOCATION-docker.pkg.dev/PROJECT/REPOSITORY/IMAGE — a region prefix and an explicit repository name that didn’t exist as a concept in GCR. Every hardcoded reference to the old format needs updating:
- Dockerfiles that use a
gcr.iobase image in aFROMline - CI/CD pipeline YAML (GitHub Actions, Cloud Build, GitLab CI) that builds and pushes to a hardcoded
gcr.iotag - Kubernetes manifests and Helm charts referencing
gcr.ioimages inimage:fields - Terraform/IaC that provisions deployments pointing at old image URLs
- docker-compose files for local dev environments still pulling from
gcr.io
For moving existing images rather than rebuilding them from scratch, Google’s docs point to two real, currently-documented tools:
For bulk/automated migration, gcloud artifacts docker upgrade migrate handles copying images across projects:
gcloud artifacts docker upgrade migrate \
--projects=my-project \
--from-gcr \
--to-pkg-dev \
--copy-only
For manual or more granular copies, Google’s docs instruct using gcrane (from the go-containerregistry project) to copy specific images one at a time:
gcrane cp gcr.io/my-project/my-app:latest \
us-central1-docker.pkg.dev/my-project/my-repo/my-app:latest
Whichever tool you use, the sequencing that avoids an outage is: copy the images to Artifact Registry first, update every CI/CD pipeline and manifest to reference the new URL, verify a full deploy cycle works end to end, and only then stop worrying about the old gcr.io references. If you have GitHub Actions workflows building and pushing images, that pipeline config is usually the first place a stale gcr.io reference hides.
Frequently Asked Questions
Q: Is Google Container Registry shutting down?
A: It already has, for writes. As of March 18, 2025, you cannot push a new image to any gcr.io URL, on any project. Pulls of images already migrated to Artifact Registry’s compatibility layer still work.
Q: How do I migrate from gcr.io to Artifact Registry?
A: Create an Artifact Registry repository, then copy existing images over with gcloud artifacts docker upgrade migrate (bulk) or gcrane cp (per-image), then update every Dockerfile, CI/CD pipeline, and manifest that references the old gcr.io URL format to the new LOCATION-docker.pkg.dev/... format.
Q: Can I still pull images from gcr.io URLs?
A: Yes, for images that were already hosted there before the shutdown — Google auto-migrated existing gcr.io URLs onto Artifact Registry’s infrastructure starting May 15, 2024, and those continue to resolve for pulls. You just can’t push anything new to them.
Q: What IAM role do I need to push images to Artifact Registry?
A: roles/artifactregistry.writer on the repository (or project, for broader access). Use roles/artifactregistry.reader for anything that only needs to pull.
Quick Summary
- Google Container Registry stopped accepting new image pushes on March 18, 2025 — existing migrated
gcr.ioURLs still work for pulls, but nothing new can be pushed to them - Artifact Registry is the replacement: enable the API, create a repository with
gcloud artifacts repositories create, authenticate Docker withgcloud auth configure-docker - Grant
roles/artifactregistry.writer(or.readerfor pull-only) to service accounts viagcloudor thegoogle_artifact_registry_repository_iam_memberTerraform resource - The URL format change (
gcr.io/PROJECT→LOCATION-docker.pkg.dev/PROJECT/REPO) is what actually breaks — audit Dockerfiles, CI/CD YAML, Kubernetes manifests, and IaC for hardcoded references - Use
gcloud artifacts docker upgrade migratefor bulk image migration orgcrane cpfor one-off copies, and update every pipeline reference before you stop watching for stalegcr.ioURLs