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.
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 allCloud Tasks vs. Pub/Sub for Cloud Run: routing, retries, and rate limits
Use Cloud Tasks for a specific operation that needs scheduling or controlled dispatch. Use Pub/Sub when independent consumers need the same event. Either HTTP handler must tolerate retries.
Cloud Run scale to zero: cold starts and minimum instances
Keep scale to zero when the first request can wait. Pay for minimum instances when measured startup latency hurts the service, and decide background CPU separately.
Direct VPC egress vs. Serverless VPC Access for Cloud Run
Use Direct VPC egress for most new Cloud Run connections to a VPC. Check address capacity, startup behavior, and outbound routing before retiring a working connector.
Pulumi stack config: values, code, and secrets
Stack config supplies values within an infrastructure design. Keep topology and operating policy explicit in code, validate the inputs, and manage secrets according to who owns their lifecycle.
How we decide between Cloud SQL connectors, Auth Proxy, and private IP
Cloud SQL connectors, the Auth Proxy, and private IP are not interchangeable secure connection options. They change identity, routing, deployment shape, and how much network plumbing the team actually owns.
Related patterns
Cloud Run request timeouts don't kill your code (so your architecture has to)
A Cloud Run request timeout ends the request, not necessarily the work. If the operation can outlive its caller, the system needs explicit job semantics instead of hope.
Safe scaling defaults for Cloud Run + Postgres
Cloud Run autoscaling is not a database strategy. Safe defaults keep the application from scaling itself into a Postgres incident before the team understands the workload.
GKE Autopilot as the escape hatch from Cloud Run
When Cloud Run stops fitting, the next move is usually GKE Autopilot: more Kubernetes-shaped control without immediately taking on the full burden of Standard clusters.
Why we default to Cloud Run for SME internal platforms
For SME internal platforms, Cloud Run is our default because it covers a large share of useful workload shapes without forcing teams to own cluster operations before they have earned that surface area.