← Back to Patterns

"Internal-only" Cloud Run isn't just a checkbox

Making a Cloud Run service private is not one toggle. It is a decision about ingress, routing, caller path, and IAM working together as one access model.

By Ivan Richter LinkedIn

Last updated: Sep 1, 2026

4 min read

On this page

An internal Cloud Run service is the intersection of an allowed ingress path and an authorized caller.

Ingress doesn’t replace IAM. IAM doesn’t make every route private. VPC attachment on the calling service doesn’t automatically make the destination see the request as internal. All three assumptions are common sources of access incidents.

Design private reachability as one flow from caller to service.

Choose the intended front door

Cloud Run services have a stable run.app endpoint even when another frontend is used. Decide which path callers should use:

  • the default endpoint reached from an allowed internal source
  • an internal Application Load Balancer
  • Private Service Connect with private DNS for a VPC-facing address model
  • an external Application Load Balancer protected by controls such as IAP or Cloud Armor

Then choose the ingress setting that permits that path and rejects the others.

all allows direct ingress subject to IAM. internal-and-cloud-load-balancing permits recognized internal sources and traffic through an external Application Load Balancer while blocking direct internet ingress to the service URL. internal restricts ingress to sources Cloud Run recognizes as internal, including supported VPC and Google-managed paths.

The middle mode is often the correct public-edge architecture: users enter through the load balancer, while the default service endpoint isn’t another internet path around it.

“Internal” is a platform definition

Cloud Run doesn’t interpret internal as “same company,” “same organization,” or “the hostname looked private.” It evaluates documented source and routing conditions.

A VM using Private Google Access, a service in a suitable Shared VPC path, an internal Application Load Balancer, and specific Google-managed services can qualify through different mechanisms. Conditions such as project placement, VPC Service Controls, and use of the default URL may matter.

Keep the accepted source classes in infrastructure code and architecture documentation. Recheck the current product definition during access reviews because platform-supported callers evolve.

Design the caller path too

For service-to-service calls, identify how the source resolves and reaches the destination. Direct VPC egress controls outbound traffic from a Cloud Run service. It doesn’t create direct ingress to another Cloud Run service. The request still enters through Cloud Run’s supported ingress path.

Use the destination’s run.app URL with an internal-qualified source path, an internal load balancer, or Private Service Connect according to the network contract. Test from the real caller identity and network, not only from a developer machine with broader access.

DNS is part of the design when private endpoints or load balancers are involved. A private IP that resolves only in one VPC isn’t a complete cross-project access model.

Authenticate every machine caller

Keep Cloud Run Invoker authorization enabled unless anonymous access is explicitly required. Give each calling workload a service account, grant the narrow invoker role, and mint an identity token for the target audience.

Network position says where the request came from. IAM says which workload is making it. Use both.

For human access to internal tools, prefer a controlled identity-aware frontend rather than distributing network access and broad invoker roles. The exact choice depends on whether the users already have a private network path and where session policy belongs.

Remove unintended paths

Audit custom domains, load balancer backends, old revisions, traffic tags, service IAM, organization policies, and DNS records. A service can have a restrictive intended frontend while an older path remains callable.

Test the negative cases:

  • unauthenticated request through the intended route
  • authenticated request from an unapproved source
  • direct request to run.app from the internet
  • request through each load balancer or private endpoint
  • call from every Google-managed service expected to invoke it
  • cross-project and Shared VPC caller paths

An access model isn’t complete until the denied paths fail for the reason you intended.

Observe path and identity

Log the authenticated principal, target service and revision, route or frontend where available, and request trace. Alert on denied calls from expected integrations and successful calls through routes that should be unused.

Keep a small access matrix:

caller                 path                         ingress mode                  IAM
scheduler              default URL                 internal                     invoker SA
employees              external LB + IAP            internal-and-cloud-balancing user policy
private service        internal LB or qualified URL internal                     invoker SA
internet               none                         denied                       none

The values will differ. Writing them down forces the design to stop hiding inside one console dropdown.

Cloud Run supports private services well, but ingress, routing, DNS, caller identity, and IAM must agree. “Internal-only” describes the resulting access model, not one checkbox.

More in this domain: Infrastructure

Browse all

Related patterns