← Back to Patterns

Internal-only Cloud Run: ingress, identity, and the caller’s network path

An internal Cloud Run service needs an allowed request path and an authorized caller. The correct setup differs for Cloud Tasks, another Cloud Run service, and human access.

By Ivan Richter LinkedIn

Last updated: Oct 2, 2026

5 min read

On this page

Setting a Cloud Run service to internal doesn’t tell you which of your callers will reach it. A Cloud Tasks queue and another Cloud Run service can be in the same project and still use different ingress paths. Giving both a service account with roles/run.invoker doesn’t make those paths equivalent.

We’d define the permitted callers first, then configure two separate checks: whether Cloud Run accepts the request’s origin and whether the caller is authorized to invoke the service. Keep application-level permissions as a third check when an authenticated caller shouldn’t be allowed to perform every operation.

Ingress restrictions don’t replace authentication

Cloud Run has three ingress settings. all permits requests from the internet to reach the service’s authentication check. internal accepts the origins that Google classifies as internal. internal-and-cloud-load-balancing also admits requests through supported external Application Load Balancers while restricting direct external access to the service. Google’s ingress guide defines the accepted origins; “anything running on Google Cloud” is not the rule.

Keep the Invoker IAM check enabled for a private service and grant access to the specific caller identities. Don’t grant allUsers merely because ingress is restricted. A network boundary can contain several applications with different responsibilities, and the ability to reach a worker shouldn’t automatically confer permission to use it.

A service with all ingress and correctly restricted IAM can reject unauthorized callers. It is authenticated, but it doesn’t meet a requirement that requests must arrive through an internal path. Name the requirement accurately rather than use “private” for both arrangements.

A same-project Cloud Tasks worker can be simple

For a queue and worker in the same project, Cloud Tasks can call the worker’s default run.app URL under internal ingress. You don’t need to add a VPC connector just to make that delivery internal. Google’s recognized managed-service path requires the default URL and the documented project or VPC Service Controls relationship; a custom domain isn’t interchangeable here.

A minimal setup has an internal-ingress worker with authentication required, a dedicated task invoker service account, and roles/run.invoker granted to that identity on the worker. Configure the task’s HTTP request to carry an OIDC token for that account. The Cloud Tasks and Cloud Run integration guide from Google sets out the required service-account and enqueuer permissions.

Keep the request URL and token audience separate. The request might go to a handler such as /process, while the audience identifies the receiving service URL, without the handler path. Use the service URL returned by Cloud Run. Google’s service-to-service authentication guide explains the audience requirement and configured custom-audience alternative.

Leave the default URL enabled for this integration. The same discipline applies to Pub/Sub push and Cloud Scheduler: verify the supported invocation path for the particular integration, not just the identity attached to it.

Another Cloud Run service needs a qualifying network path

A direct HTTP call from Cloud Run to Cloud Run isn’t automatically internal because both services share a project. The source needs to route the request through a path that the destination’s internal ingress recognizes.

One documented same-project arrangement uses Direct VPC egress or a connector on the caller, routes all outbound traffic through the VPC, and enables Private Google Access on the associated subnet. Other arrangements use an internal load balancer or Private Service Connect. Google’s private networking guide describes these options and the additional Shared VPC conditions.

Don’t assume private-ranges-only will route an ordinary run.app request through the VPC. With normal public DNS resolution, that destination may take the public outbound path instead. The configured egress mode, DNS answer, route, and destination all have to agree.

IAM still applies after the route works. Give the calling service its own runtime identity, grant that identity permission on the destination, and obtain a Google-signed ID token using the runtime’s credentials. An identity that passes on one route should still be rejected on a route that ingress doesn’t permit.

Human access is a separate design decision

An internal service called by a queue has a narrow machine-to-machine access pattern. An employee opening an application from a laptop has a different one. Don’t solve the second case by loosening the worker’s ingress until a browser happens to connect.

For a human-facing application, decide whether access belongs behind an authenticated frontend such as IAP, an internal load balancer reached through your private network, or another explicitly approved route. A load balancer alone isn’t user authentication. Shared VPC and cross-project access also need their own review; network peering by itself doesn’t make arbitrary traffic qualify as internal.

Keep the machine worker separate when that makes its permissions easier to understand. A frontend can authorize a user’s operation and enqueue work without giving every user direct invocation rights over the worker.

Prove both the allowed and rejected cases

Before calling the setup complete, test the intended caller with the intended identity, then vary one condition at a time: an unauthorized identity on the same allowed route, no token, the wrong audience, and a valid identity calling from an external route that should be blocked. Test the actual queue or subscription too; a successful manual request isn’t a substitute.

Record the destination, caller identity, audience, and network path in the deployment’s access notes. During troubleshooting, correlate request and delivery identifiers with application logs. Don’t assume a standard request log proves which identity was authenticated, and don’t treat a caller-supplied identity header as proof.

Once the tests pass, keep them close to the infrastructure configuration. A later change to DNS, egress, the default URL, or a service account can break an internal integration without changing the worker’s code at all.

More in this domain: Infrastructure

Browse all

Related patterns