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.
Cloud SQL language connectors, the Cloud SQL Auth Proxy, and private IP aren’t three mutually exclusive security options.
Private IP chooses the network route. A language connector or the Auth Proxy handles Cloud SQL connection authorization, certificate management, and optional automatic IAM database authentication. Either connector can itself use a private IP path when the runtime has VPC reachability.
The useful decision is therefore two decisions:
- How should traffic reach the instance?
- Where should Cloud SQL-specific connection behavior live?
Making those choices separately removes most of the vague “which one is more secure?” debate.
Choose the route first
For a private Cloud Run service in GCP, our default route is Cloud SQL private IP through Direct VPC egress. The database has no need for a public path, and reachability follows the VPC, subnet, firewall, DNS, and service egress design.
That route is easy to reason about when the platform already owns private networking. It also means network failures remain network failures: wrong subnet, exhausted addresses, missing route, firewall policy, DNS, or database-side private-services configuration.
A public IP path can still use a connector securely without authorized networks because the connector establishes the protected Cloud SQL connection. It may be the simpler route for local development or workloads that don’t belong on the VPC. Public IP isn’t automatically public database access. The connection method and instance policy still matter.
Private Service Connect can provide another private connection shape when its service-oriented network boundary is useful. Add it for that boundary, not because one more private acronym feels safer.
Language connectors keep the helper in process
The Go, Java, and Python Cloud SQL connectors integrate connection establishment into the application runtime. They’re a good fit when the language is supported, the service already uses provider-aware infrastructure, and automatic IAM database authentication or managed certificate refresh is useful.
The benefit is no companion process. The cost is Cloud SQL-specific code and library behavior inside the application. Startup, token refresh, connector metrics, and version compatibility become part of the runtime.
Use a language connector when that coupling makes the deployment simpler overall. Don’t wrap a generic application in provider-specific code merely to avoid one small process.
The Auth Proxy keeps the application generic
The Cloud SQL Auth Proxy is itself a Cloud SQL connector. It exposes a local TCP or Unix socket while handling the Cloud SQL side of authorization and encryption.
Use it when the application language lacks a connector, several runtimes need the same generic pattern, local tooling benefits from a localhost endpoint, or separating provider behavior from application code makes operations clearer.
The proxy is another process to start, observe, update, and restart. In Cloud Run it usually means a sidecar or another deployment integration. That cost is acceptable when it buys real compatibility.
The proxy doesn’t create network reachability. To use a private IP instance, the proxy still needs access to the VPC. It also doesn’t pool database sessions. Ten application connections through the proxy remain ten database connections.
Direct database connections keep every concern explicit
An application can connect directly to the private IP with a normal PostgreSQL driver. That keeps the runtime generic and removes the proxy, but the team now owns database credentials or IAM token handling, TLS configuration where required, certificate lifecycle, and connection-policy enforcement.
This can be the cleanest choice in a tightly controlled private network with mature secret and TLS handling. It can also be a false simplification that replaces a managed connector with bespoke credential scripts nobody wanted to maintain.
Count the whole operating path, not the number of containers in the manifest.
IAM database authentication is an identity choice
Automatic IAM database authentication through a language connector or Auth Proxy aligns the database login with the workload’s Google identity and refreshes short-lived tokens for the application.
Use it when service-account identity is the desired database principal and the connector lifecycle is acceptable. Keep conventional database users when portability, tooling, migration behavior, or connection scale makes them the more stable choice.
IAM authentication doesn’t replace database authorization. Roles and grants inside PostgreSQL still define what the principal may do.
Connection budgeting remains separate
None of these methods fixes pool sizing. A connector secures and authorizes the path. It doesn’t make backend sessions cheap.
Set per-instance pool limits, acquisition deadlines, max Cloud Run instances, and database headroom through the connection budget. Consider a pooler only when session multiplexing solves a measured problem.
Our default ladder
For Cloud Run services already using private networking:
- choose private IP through Direct VPC egress
- use a language connector when supported and IAM-oriented integration simplifies the service
- use the Auth Proxy when a generic local endpoint or unsupported runtime makes the sidecar worthwhile
- connect directly only when the team deliberately owns TLS and credential lifecycle
Break the route decision independently from the helper decision. A language connector over private IP and an Auth Proxy over private IP are both coherent designs.
The right combination is the one whose identity, network, startup, and incident paths the team can explain without collapsing them into the word “secure.”
More in this domain: Infrastructure
Browse allSafe 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.
IAM DB auth for Cloud SQL: when it simplifies security and when it complicates delivery
IAM DB auth can reduce password sprawl and make revocation cleaner, but it also turns database access into an identity operating model that depends on disciplined service-account boundaries.
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.
Cloud Run scaling from zero is a feature until it isn't
Scale to zero is a good default for request-driven services, until startup delay, warm-capacity needs, or instance caps turn it into user-visible reliability behavior instead of a pricing feature.
Direct VPC egress vs Serverless VPC Access for Cloud Run: our default
We default to Direct VPC egress for Cloud Run because it is the cleaner networking shape: fewer moving parts, no connector resource, and costs that scale with the service instead of beside it.
Related 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.
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.
Why Cloud Run + Postgres needs a connection budget
Cloud Run and Postgres get fragile when connection growth is left implicit. We treat connections as a finite runtime budget, not as plumbing the app can multiply without consequence.