How to create *.cloud.run URLs for Cloud Run services

Every Cloud Run service gets a default .run.app URL that embeds your project number and region, such as https://web-123456789012.europe-west1.run.app. That works great, but it's a bit of a mouthful. Cloud Run now lets you claim a unique https://<subdomain>.cloud.run URL directly from the gcloud CLI (in preview as of November 2026).

A .cloud.run service URL goes live within a few seconds. Below, I walk through deploying a service with a .cloud.run URL, disabling the default .run.app address, handling taken subdomains, and updating or deleting a service with a .cloud.run subdomain.

Deploy with a .cloud.run URL

To try this out, set your Google Cloud project ID, target region, and desired subdomain in your shell. Because all .cloud.run subdomains share a single global namespace across Google Cloud, the tr and head pipeline below reads /dev/urandom to generate six random lowercase letters and digits (such as mv72a9). That gives you a unique name you can copy and paste right away without hitting a collision, though you can set WEB_SUBDOMAIN to any unclaimed name you like:

export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="europe-west1"
export WEB_SUBDOMAIN="web-$(LC_ALL=C tr -dc 'a-z0-9' </dev/urandom | head -c 6)"
echo "https://$WEB_SUBDOMAIN.cloud.run"
https://web-mv72a9.cloud.run

If you pick your own name, use between 6 and 63 characters with lowercase letters, numbers, and hyphens, without starting or ending with a hyphen. When I tested different names, I found that Cloud Run also blocks some terms, such as google, api, auth, admin, and test (for example, my-api-mv72a9.cloud.run fails with contains the prohibited term 'api').

Before deploying, enable the Cloud Run Admin API in your project:

gcloud services enable run.googleapis.com \
  --project="$PROJECT_ID"
Operation "operations/acf.p2-123456789012-396be3b7-4aee-40ad-8fc6-ef05544f67bf" finished successfully.

With the API enabled and your variables set, you can deploy a sample NGINX container and claim $WEB_SUBDOMAIN.cloud.run in a single command. Pass --domain to gcloud beta run deploy to register the subdomain and route it to a new web service:

gcloud beta run deploy web \
  --image=nginx \
  --port=80 \
  --no-invoker-iam-check \
  --domain="$WEB_SUBDOMAIN.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID"
Deploying container to Cloud Run service [web] in project [YOUR_PROJECT_ID] region [europe-west1]
✓ Deploying new service... Done.
  ✓ Creating Revision...
  ✓ Creating domain mapping...
  ✓ Routing traffic...
Done.
Service [web] revision [web-00001-p5d] has been deployed and is serving 100 percent of traffic.
Service URL: https://web-123456789012.europe-west1.run.app
  • --image=nginx and --port=80 deploy the official NGINX container image from Docker Hub and route traffic to container port 80, where NGINX listens by default, instead of Cloud Run's default port 8080.
  • --no-invoker-iam-check disables Cloud Run's IAM invoker check so the service is publicly available.
  • --domain="$WEB_SUBDOMAIN.cloud.run" creates a domain mapping that routes your .cloud.run subdomain to the service web.

Even though the CLI output only prints the default Service URL, Cloud Run provisions your .cloud.run route over HTTPS in the background. Routing takes a few seconds to propagate after the command finishes (requests during that window return 404). Send a HEAD request with curl to verify (Cloud Run's front-end proxy overwrites NGINX's server header with Google Frontend):

curl -I "https://$WEB_SUBDOMAIN.cloud.run"
HTTP/2 200
content-type: text/html
content-length: 896
date: Fri, 06 Nov 2026 10:05:14 GMT
server: Google Frontend

During the deployment, gcloud beta run deploy printed Creating domain mapping.... Passing --domain creates a separate domain mapping resource in the region that routes $WEB_SUBDOMAIN.cloud.run to the Cloud Run service. Run gcloud beta run domain-mappings list to see the mapping:

gcloud beta run domain-mappings list \
  --region="$REGION" \
  --project="$PROJECT_ID"
   DOMAIN                SERVICE  REGION
✔  web-mv72a9.cloud.run  web      europe-west1

Disable the default .run.app URL

Because the .cloud.run domain mapping is separate from the service's default .run.app endpoint, you can turn off the .run.app address while keeping https://<subdomain>.cloud.run live. Save the default URL in DEFAULT_URL so you can test it in a moment, then pass --no-default-url to gcloud run services update (or include it during gcloud beta run deploy):

DEFAULT_URL=$(gcloud run services describe web \
  --region="$REGION" \
  --project="$PROJECT_ID" \
  --format='value(status.url)')

gcloud run services update web \
  --no-default-url \
  --region="$REGION" \
  --project="$PROJECT_ID"
✓ Updating... Done.
Done.
Service [web] has been updated.

Disabling the default URL updates the service configuration without a new deployment. Verify with curl that the default .run.app endpoint now returns 404 while your .cloud.run URL continues returning 200:

curl -I "$DEFAULT_URL"
curl -I "https://$WEB_SUBDOMAIN.cloud.run"
HTTP/2 404
date: Fri, 06 Nov 2026 10:06:22 GMT
server: Google Frontend
content-type: text/html; charset=UTF-8

HTTP/2 200
content-type: text/html
content-length: 896
date: Fri, 06 Nov 2026 10:06:23 GMT
server: Google Frontend

What happens when a .cloud.run URL is already taken

Because all .cloud.run URLs share a global pool, someone else might already have claimed the subdomain you want. Try deploying a new Cloud Run service named wietse with --no-default-url and wietse.cloud.run, which I already claimed:

gcloud beta run deploy wietse \
  --image=nginx \
  --port=80 \
  --no-invoker-iam-check \
  --no-default-url \
  --domain="wietse.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID"
Deploying container to Cloud Run service [wietse] in project [YOUR_PROJECT_ID] region [europe-west1]
✓ Deploying new service...
  ✓ Creating Revision...
  X Creating domain mapping...failed
Deployment failed
ERROR: (gcloud.beta.run.deploy) Domain mapping to [wietse.cloud.run] already exists in this region.

Even though the error message says already exists in this region, .cloud.run subdomains are reserved globally across all regions, so switching regions returns the same error. Also notice that gcloud beta run deploy deploys the service before registering the domain mapping. Because the command included --no-default-url and the .cloud.run mapping failed, the wietse service is now running without any URL at all.

To check whether a .cloud.run subdomain is available and valid for an existing service without creating the mapping, pass --dry-run to gcloud beta run domain-mappings create:

export WIETSE_SUBDOMAIN="wietse-$(LC_ALL=C tr -dc 'a-z0-9' </dev/urandom | head -c 6)"

gcloud beta run domain-mappings create \
  --service=wietse \
  --domain="$WIETSE_SUBDOMAIN.cloud.run" \
  --dry-run \
  --region="$REGION" \
  --project="$PROJECT_ID"
Dry run: domain mapping [wietse-r4t9x1.cloud.run] to service [wietse] is valid and would be created.

Once the dry run succeeds, run the command without --dry-run to map the subdomain to wietse:

gcloud beta run domain-mappings create \
  --service=wietse \
  --domain="$WIETSE_SUBDOMAIN.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID"
✓ Creating......done.

The wietse service is now live at https://$WIETSE_SUBDOMAIN.cloud.run.

Clean up the domain mapping when deleting a service

Each domain mapping is a separate resource, and deleting a Cloud Run service doesn't automatically delete its .cloud.run mapping. Delete the wietse service to see what happens to $WIETSE_SUBDOMAIN.cloud.run:

gcloud run services delete wietse \
  --region="$REGION" \
  --project="$PROJECT_ID" \
  --quiet
✓ Deleting [wietse]...done.
Deleted service [wietse].

List your domain mappings again to see that $WIETSE_SUBDOMAIN.cloud.run is still registered in your project:

gcloud beta run domain-mappings list \
  --region="$REGION" \
  --project="$PROJECT_ID"
   DOMAIN                   SERVICE  REGION
✔  web-mv72a9.cloud.run     web      europe-west1
X  wietse-r4t9x1.cloud.run  wietse   europe-west1

domain-mappings list marks wietse-r4t9x1.cloud.run with X because its target service (wietse) no longer exists, and requests to https://$WIETSE_SUBDOMAIN.cloud.run return 404. Delete the dangling mapping with gcloud beta run domain-mappings delete so you release the subdomain (passing --quiet to skip the confirmation prompt):

gcloud beta run domain-mappings delete \
  --domain="$WIETSE_SUBDOMAIN.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID" \
  --quiet
✓ Deleting [wietse-r4t9x1.cloud.run]...done.
Mappings to [wietse-r4t9x1.cloud.run] now have been deleted.

Update the .cloud.run URL mapped to a service

Because domain mappings are separate from your service deployments, you can change the .cloud.run URL on a running service without redeploying its container. To replace $WEB_SUBDOMAIN.cloud.run on the Cloud Run service web you deployed earlier, create a new mapping with gcloud beta run domain-mappings create and then delete the old mapping with gcloud beta run domain-mappings delete:

export NEW_WEB_SUBDOMAIN="web-new-$(LC_ALL=C tr -dc 'a-z0-9' </dev/urandom | head -c 6)"

gcloud beta run domain-mappings create \
  --service=web \
  --domain="$NEW_WEB_SUBDOMAIN.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID"

gcloud beta run domain-mappings delete \
  --domain="$WEB_SUBDOMAIN.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID" \
  --quiet
✓ Creating......done.
✓ Deleting [web-mv72a9.cloud.run]...done.
Mappings to [web-mv72a9.cloud.run] now have been deleted.

You might wonder if redeploying the service with gcloud beta run deploy and --domain="$NEW_WEB_SUBDOMAIN.cloud.run" would replace the URL in one step. It doesn't. Instead, it redeploys the service and adds a second domain mapping alongside the first one, so you still have to run domain-mappings delete to remove the old subdomain.

Map a .cloud.run URL to a different service

You can't edit an existing domain mapping to change its target service. To point $NEW_WEB_SUBDOMAIN.cloud.run at a new web-v2 service instead of web, you have to delete the mapping and recreate it. First, deploy web-v2:

gcloud run deploy web-v2 \
  --image=nginx \
  --port=80 \
  --no-invoker-iam-check \
  --region="$REGION" \
  --project="$PROJECT_ID"
Deploying container to Cloud Run service [web-v2] in project [YOUR_PROJECT_ID] region [europe-west1]
✓ Deploying new service... Done.
  ✓ Creating Revision...
  ✓ Routing traffic...
Done.
Service [web-v2] revision [web-v2-00001-x9k] has been deployed and is serving 100 percent of traffic.
Service URL: https://web-v2-123456789012.europe-west1.run.app

Next, delete the mapping on web and create a new mapping that points $NEW_WEB_SUBDOMAIN.cloud.run to web-v2:

gcloud beta run domain-mappings delete \
  --domain="$NEW_WEB_SUBDOMAIN.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID" \
  --quiet

gcloud beta run domain-mappings create \
  --service=web-v2 \
  --domain="$NEW_WEB_SUBDOMAIN.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID"
✓ Deleting [web-new-k8w2p4.cloud.run]...done.
Mappings to [web-new-k8w2p4.cloud.run] now have been deleted.
✓ Creating......done.

Because you have to delete the existing mapping before creating the new one, the subdomain is momentarily unclaimed during the cutover, and requests can return 404 for a few seconds after domain-mappings create finishes while routing propagates.

Moving a .cloud.run mapping to another region works the same way: delete the mapping in europe-west1 and run domain-mappings create with --region=europe-west4 for your service in that region.

When you're done experimenting, delete web, web-v2, and the remaining domain mapping so you don't leave resources or reserved subdomains behind:

gcloud run services delete web \
  --region="$REGION" \
  --project="$PROJECT_ID" \
  --quiet

gcloud run services delete web-v2 \
  --region="$REGION" \
  --project="$PROJECT_ID" \
  --quiet

gcloud beta run domain-mappings delete \
  --domain="$NEW_WEB_SUBDOMAIN.cloud.run" \
  --region="$REGION" \
  --project="$PROJECT_ID" \
  --quiet
✓ Deleting [web]...done.
Deleted service [web].
✓ Deleting [web-v2]...done.
Deleted service [web-v2].
✓ Deleting [web-new-k8w2p4.cloud.run]...done.
Mappings to [web-new-k8w2p4.cloud.run] now have been deleted.
Signature