How to Connect to Cloud SQL Securely: Auth Proxy, Private IP, and IAM
Getting your application connected to Cloud SQL is not just about picking a hostname. It involves two separate decisions: how traffic reaches the instance (the network path) and how your application proves its identity (authentication). Get both right and your database stays protected. Get either one wrong and you create a gap that is easy to miss.
This guide explains all three connection methods: public IP with authorised networks, private IP, and the Cloud SQL Auth Proxy. It covers how they relate to each other and which pattern to use depending on whether your application runs on Cloud Run, GKE, Compute Engine, or your local machine.
What secure Cloud SQL connectivity actually means
Think of your database as a vault inside a building. Securing it properly means thinking about two separate things: who can get into the building at all, and who has the combination to the vault once they are inside. These are not the same problem, and solving one does not solve the other.
The network path controls who can reach your database port at all. A Cloud SQL instance on public IP is reachable from the internet by default. You restrict access with an authorised networks allowlist, but that list is only as good as your discipline in managing it. A Cloud SQL instance on private IP lives on your VPC. No internet host can even initiate a connection attempt, regardless of what credentials they have. That is the difference between locking the vault and also moving the building off the public street entirely.
Authentication is what happens once a connection attempt reaches the instance. The database requires a username and password, or an IAM token if you have enabled IAM database authentication. A strong password does not protect a database that is reachable from the public internet, and private network placement does not protect a database with a weak password. You need both layers.
The Cloud SQL Auth Proxy sits across both layers. Think of
it like a security desk in the lobby: it checks your IAM identity before
you get anywhere near the database port, then escorts the connection through
an encrypted tunnel. Your application connects to 127.0.0.1 on
a local port, as if talking to a local database. The proxy handles TLS
encryption and IAM authentication on your behalf. It works whether your
instance uses public or private IP.
Private IP is the preferred network model for production. Your Cloud SQL instance gets an IP address inside your VPC. Resources in that VPC (Compute Engine VMs, GKE pods, Cloud Run services with VPC access) reach the database without any traffic crossing the public internet. Database authentication is still required on top of this.
The recommended production defaults: private IP for the network path, and the Auth Proxy for connection and authentication management. Using both gives you defence in depth. They are not mutually exclusive.
Connection options at a glance
There are three network models for Cloud SQL. Each one describes how traffic reaches the instance. Authentication (database users, IAM tokens) is always a separate layer on top.
Public IP with authorised networks
The instance has a public IP address. The authorised networks allowlist controls which IP ranges can reach the database port. Traffic crosses the public internet from any IP not on your allowlist. This model requires careful allowlist management and is only appropriate for local development.
If you must use public IP, always connect via the Auth Proxy. It
adds IAM authentication and TLS encryption on top. Never open the allowlist
to 0.0.0.0/0 for any reason.
Private IP
The instance is assigned an IP address inside your VPC and is not reachable from the public internet. Only resources within the VPC or connected networks can initiate connections. This is the recommended network model for production. See Private vs Public IP Addresses for how GCP handles this distinction more broadly.
Private IP requires Private Service Connection (previously called Private Services Access). This creates a peering relationship between your VPC and Google’s managed services network. You configure it once per VPC before creating any private IP instances.
Cloud SQL Auth Proxy
The Auth Proxy runs alongside your application and manages the connection
on its behalf. It authenticates using a service account’s IAM identity
(or your personal credentials for local development), establishes an
encrypted TLS tunnel, and listens on a local port. Your application connects
to 127.0.0.1:PORT as if connecting to a local database. The
proxy works on top of either public or private IP.
Network path and authentication are two separate decisions. Private IP controls who can reach your instance over the network. Database credentials or IAM tokens control who can log in once a connection is open. The Auth Proxy sits across both: it uses IAM to reach the instance and provides a local port for your application to connect through. For production, use private IP and the Auth Proxy together.
| Approach | Suitable for | Typical use |
|---|---|---|
| Public IP + authorised networks | Development only | Laptop connecting to a dev instance, always with the Auth Proxy |
| Private IP | All production workloads | VMs, GKE pods, Cloud Run with Serverless VPC Access |
| Auth Proxy only | Development and temporary admin access | Local dev, short-term debugging sessions |
| Private IP + Auth Proxy | Production (recommended) | Network isolation plus IAM-managed authentication |
How a connection actually reaches Cloud SQL
Understanding the flow helps you debug problems and reason about what each layer is actually protecting.
Public IP path (without the proxy)
Your client initiates a TCP connection to the instance’s public IP on port 3306 (MySQL) or 5432 (PostgreSQL). Google’s network checks whether the source IP is on the authorised networks allowlist. If it is, the connection reaches the Cloud SQL instance. The database then requires a username and password (or IAM token) to complete the login. The database port is reachable from the internet; only the allowlist stands between an attacker and a login attempt.
Private IP path
Your client (a VM, a GKE pod, a Cloud Run container with Serverless VPC Access) sends a TCP connection to the instance’s private IP. The request stays within the VPC. Private Service Connection routes traffic through the VPC peering relationship to Google’s managed services network where Cloud SQL runs. The database still requires authentication at the application level. Firewall rules and VPC routing enforce network access; there is no instance-level IP allowlist to manage. See VPC Networks Explained for how the underlying network model works.
Auth Proxy path
The proxy starts and calls the Cloud SQL Admin API, authenticating via a
service account (or Application Default Credentials for local development).
It retrieves the instance’s connection details and an ephemeral TLS
certificate. The proxy then opens a local port at 127.0.0.1:PORT
and waits. Your application connects to that local port. From its
perspective, it is talking to a local process. The proxy forwards the
connection to Cloud SQL over an encrypted TLS tunnel using the ephemeral
certificate. Database authentication still happens inside that tunnel:
you still need a database user or IAM database token to log in. The proxy
does not bypass database-level access controls.
IAM controls two things separately. The roles/cloudsql.client
role allows a service account to reach the instance via the
proxy. Without it, the proxy cannot connect at all. IAM database
authentication (when enabled) allows a service account to log in
to a database using a short-lived IAM token instead of a native password.
You can have one without the other. Most production setups use both:
roles/cloudsql.client for proxy access, plus either a native
database user or IAM database authentication for the login itself. See
IAM in GCP for the broader model.
When to use each approach
The right pattern depends on where your application runs and what you are trying to do. Here are practical recommendations for common scenarios.
Local development from a laptop
Use the Auth Proxy with your personal credentials
(gcloud auth application-default login). Your Cloud SQL
instance can have a public IP for this purpose. The proxy connects via
the Cloud SQL API, so you do not need to add your laptop’s IP to the
authorised networks allowlist. If you have a VPN or tunnel to your VPC,
you can also reach a private IP instance directly.
Application on Compute Engine
If the VM is in the same VPC as the Cloud SQL instance, private IP is the
simplest and most secure network path. No public IP needed. Still run the
Auth Proxy if you want IAM-managed authentication rather than managing
database passwords manually. Assign a
service account to
the VM with roles/cloudsql.client and the proxy picks up
credentials automatically from the metadata server. No key files needed.
Application on GKE
Run the Auth Proxy as a sidecar container in the same pod as your
application. Use
Workload Identity for GKE
to bind the pod’s Kubernetes service account to a GCP service account with
roles/cloudsql.client. Connect your application to
127.0.0.1:PORT. Do not mount service account key files in the
pod. See
Why Service Account Keys Are Dangerous
for why this matters even inside Kubernetes Secrets. If your cluster is
private and your instance uses private IP, you get both network isolation
and IAM-authenticated access. See
Securing GKE Clusters
for the broader GKE security model.
Application on Cloud Run
Cloud Run has built-in Cloud SQL connector support managed by the platform.
Add the instance connection name to your Cloud Run service configuration
under “Cloud SQL connections.” Assign a service account with
roles/cloudsql.client to the Cloud Run service. Your
application connects via a Unix socket at
/cloudsql/PROJECT:REGION:INSTANCE. For a private IP instance,
enable Serverless VPC Access
so Cloud Run can reach your VPC. Cloud Run containers are not inside your
VPC by default. See
Cloud Run Security Model
for the full picture.
Private internal production system
Use private IP and the Auth Proxy. Create the instance with
—no-assign-ip so it has no public address. Your application
gets a dedicated service account with roles/cloudsql.client
and no more permissions than it needs. See
Principle of Least Privilege.
Store database credentials in
Secret Manager
rather than environment variables or config files. Use IAM database
authentication for service accounts to eliminate native database passwords
entirely.
Temporary admin access or debugging
Use the Auth Proxy from your local machine with personal ADC credentials.
Grant yourself roles/cloudsql.client temporarily, connect via
the proxy, and revoke the role when done. This is far safer than adding
your IP to authorised networks for a debugging session. If you need to
access a private IP instance without a VPN, use the proxy through Cloud
Shell or an IAP tunnel rather than converting the instance to public IP.
The Cloud SQL Auth Proxy in practice
The Auth Proxy requires the roles/cloudsql.client IAM role on
the project for the identity it runs as. This is separate from any
database-level permissions. Even if the database user exists and has the
right privileges, without this IAM role the proxy cannot open a connection
to the instance at all.
The instance connection name has the format
PROJECT_ID:REGION:INSTANCE_NAME. Retrieve yours with:
gcloud sql instances describe INSTANCE --format="value(connectionName)"Running the proxy locally for MySQL or PostgreSQL:
# Download the Cloud SQL Auth Proxy binary (Linux/macOS)
curl -o cloud-sql-proxy \
https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.14.0/cloud-sql-proxy.linux.amd64
chmod +x cloud-sql-proxy
# Connect to a MySQL instance (listens on 127.0.0.1:3306 by default)
./cloud-sql-proxy my-project:europe-west2:my-mysql-instance
# Connect to a PostgreSQL instance on a custom local port
./cloud-sql-proxy \
--port=5433 \
my-project:europe-west2:my-postgres-instance
# Connect to multiple instances simultaneously
./cloud-sql-proxy \
"my-project:europe-west2:my-mysql-instance?port=3306" \
"my-project:europe-west2:my-postgres-instance?port=5432"Once the proxy is running, connect your database client to
127.0.0.1:PORT with your normal database credentials. The proxy
is invisible to your application. It sees a local database connection.
The proxy requires roles/cloudsql.client on the project.
This controls whether the proxy can reach the instance at all. It is
entirely separate from the database username and password you use to log
in once connected. You need both. Granting one does not substitute for
the other. A common mistake is granting the IAM role and then wondering
why the database connection is still refused.
Auth Proxy as a Kubernetes sidecar
In Kubernetes, the Auth Proxy runs as a second container in the same pod
as your application. Because they share a network namespace, the application
connects to 127.0.0.1:PORT and the proxy handles the rest.
This is the standard GKE pattern for Cloud SQL connectivity.
Use Workload Identity to provide credentials. Workload Identity binds a Kubernetes service account to a GCP service account. The proxy picks up credentials automatically from the GKE metadata server. Never mount a service account key file in the pod. See Why Service Account Keys Are Dangerous for why key files are a risk even inside Kubernetes Secrets.
# Excerpt from a Kubernetes Deployment spec
containers:
- name: my-app
image: gcr.io/my-project/my-app:latest
env:
- name: DB_HOST
value: "127.0.0.1"
- name: DB_PORT
value: "5432"
- name: cloud-sql-proxy
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.0
args:
- "--structured-logs"
- "--port=5432"
- "my-project:europe-west2:my-postgres-instance"
securityContext:
runAsNonRoot: true
resources:
requests:
memory: "64Mi"
cpu: "50m"The —structured-logs flag outputs JSON logs that Cloud
Logging can parse automatically. The runAsNonRoot: true
security context prevents the proxy container from running as root.
Both are good defaults for production pods.
Private IP: network isolation for Cloud SQL
With private IP, your Cloud SQL instance gets an IP address within your
VPC rather than a public one. Think of it as moving your database off the
public internet entirely. A VM or pod in your VPC can reach it directly.
An attacker scanning the internet cannot even find the port to probe.
Using —no-assign-ip when creating the instance ensures no
public address is ever assigned.
Private IP requires Private Service Connection. This creates a VPC peering
relationship between your VPC and Google’s managed services network where
Cloud SQL instances live. You configure it once per VPC. Creating a Cloud SQL
instance with —network before completing this setup will fail.
# Step 1: allocate an IP range in your VPC for Google managed services
gcloud compute addresses create google-managed-services-my-vpc \
--global \
--purpose=VPC_PEERING \
--prefix-length=16 \
--network=my-vpc
# Step 2: create the private connection (run once per VPC)
gcloud services vpc-peerings connect \
--service=servicenetworking.googleapis.com \
--ranges=google-managed-services-my-vpc \
--network=my-vpc
# Step 3: create the Cloud SQL instance with private IP only
gcloud sql instances create my-private-db \
--database-version=POSTGRES_15 \
--region=europe-west2 \
--tier=db-n1-standard-2 \
--network=my-vpc \
--no-assign-ipOmit —no-assign-ip only if you have a specific reason to
need both private and public IP on the same instance. For production, use
it by default.
Serverless runtimes are not inside your VPC by default. If your Cloud SQL instance is on private IP, your Cloud Run service or Cloud Function also needs Serverless VPC Access configured. Without it, connection attempts will time out with no clear error. This is one of the most common causes of Cloud SQL connection failures in serverless environments.
See also PostgreSQL on Cloud SQL and Running MySQL on Cloud SQL for engine-specific setup steps.
IAM database authentication
IAM database authentication replaces native database passwords with short-lived IAM access tokens. Your application generates an access token, uses it as the database password, and Cloud SQL verifies the token against IAM. The token expires after a short time, so there is no long-lived credential to steal, rotate, or accidentally commit to a repository.
This is a good choice for service accounts running production applications because it removes the need to manage and rotate separate database passwords. Native database users still make sense in some cases: DBA tooling, legacy applications, migrations from other environments. But for GCP-native service accounts, IAM database authentication eliminates a whole category of credential risk. Database access also becomes visible in Cloud Audit Logs alongside other IAM activity.
# Enable IAM database authentication on an existing instance
gcloud sql instances patch my-db-instance \
--database-flags=cloudsql.iam_authentication=on
# Create an IAM-based database user for a service account
gcloud sql users create service-account@my-project.iam.gserviceaccount.com \
--instance=my-db-instance \
--type=CLOUD_IAM_SERVICE_ACCOUNT
# Generate a short-lived token and use it as the database password
ACCESS_TOKEN=$(gcloud auth print-access-token \
--impersonate-service-account=service-account@my-project.iam.gserviceaccount.com)
psql "host=127.0.0.1 \
user=service-account@my-project.iam \
dbname=my-app-db \
password=$ACCESS_TOKEN \
sslmode=disable"For PostgreSQL, the database username for a service account is the full
email with the .gserviceaccount.com suffix removed:
service-account@my-project.iam. Creating the IAM user in the
database does not automatically grant permissions inside it. You still need
to run GRANT statements. IAM controls who can log in; database
roles control what they can do once logged in.
IAM database authentication is separate from and complementary to the
Auth Proxy. The proxy controls how you reach the instance. IAM
database auth controls what credentials you use to log in once
connected. In most production setups, you use both: the proxy for
connection management, IAM database auth to avoid managing database
passwords for service accounts. See
IAM Roles in GCP for how
roles/cloudsql.client relates to other Cloud SQL roles.
Auth Proxy vs private IP: what each one actually controls
This comparison trips up many engineers new to Cloud SQL. The two approaches are often described as alternatives, but they operate at different layers. The best production setup uses both.
| Private IP | Auth Proxy | |
|---|---|---|
| What it controls | The network path: who can reach the instance at all | Authentication: how the client proves its identity to Cloud SQL |
| Prevents internet exposure | Yes. No public IP, no internet route. | No. Works over public IP too. |
| Handles TLS encryption | No. Encryption is a separate concern. | Yes. The proxy manages TLS automatically. |
| Requires IAM role | No direct IAM requirement on the network path itself | Yes. roles/cloudsql.client required. |
| Works on public IP | Not applicable | Yes |
| Cloud Run requirement | Needs Serverless VPC Access if instance is private | Built-in connector supported natively |
| Production recommendation | Always use for production | Always use for production |
When to use private IP alone
Private IP without the proxy is reasonable when your application runs on a Compute Engine VM inside the same VPC, your team manages TLS certificates and database passwords directly, and the operational simplicity of skipping the proxy outweighs the benefits of IAM-managed authentication. The trade-off: IAM is not in the connection path, so database access is harder to audit centrally.
When to use the proxy alone
The proxy on a public IP instance is acceptable for local development and temporary admin access when you do not have a VPN to the VPC. This is not a production pattern because the database is still reachable from the internet, even though the proxy requires IAM authentication before any connection reaches the database port.
When to use both (recommended for production)
Private IP removes the instance from internet exposure entirely. The Auth Proxy adds IAM-managed authentication and automatic TLS without requiring certificate management or allowlists. Together they give you defence in depth: a compromised database credential does not help an attacker who cannot reach the private IP from outside the VPC. This is the recommended default for production workloads on GKE, Cloud Run, and Compute Engine.
Common mistakes and how to avoid them
Assuming the Auth Proxy replaces IAM and database permissions. The proxy handles the network connection. It does not grant database-level access. You still need a database user with the right privileges inside the database. Having
roles/cloudsql.clientmeans the proxy can reach the instance. It says nothing about what the database user can do once connected.Thinking private IP alone is enough. Private IP isolates the network path, but database authentication still needs to be strong. A weak database password on a private IP instance can still be brute-forced by anyone with network access inside the VPC. Use IAM database authentication for service accounts, or store strong passwords in Secret Manager. Follow the principle of least privilege for database users. Do not use the
postgresorrootsuperuser for application connections.Using personal developer credentials in production. Credentials from
gcloud auth application-default loginare tied to a person. If that person leaves or their credentials are revoked, the application breaks. In production, always run the proxy with a dedicated service account. In GKE, use Workload Identity. In Cloud Run, assign a service account to the service and it is used automatically.Opening authorised networks to
0.0.0.0/0. This exposes the database port to every IP address on the internet and turns your Cloud SQL instance into a constant brute-force target. It sometimes happens during initial setup and gets forgotten. If you use public IP for any reason, keep the authorised networks list empty and connect via the Auth Proxy only. The proxy does not require any entry in the allowlist.Never do this in productionAuthorised networks set to
0.0.0.0/0means your database port is exposed to the entire internet. Automated scanners will find it within minutes. Even if your database password is strong, this is not an acceptable production configuration. Use private IP or the Auth Proxy instead.Skipping Private Service Connection before creating a private IP instance. Creating a Cloud SQL instance with
—networkwill fail if Private Service Connection is not set up in that VPC first. Complete the VPC peering setup once per VPC before creating any private IP instances. This step is easy to miss if you are following a tutorial that assumes it is already in place.Forgetting Serverless VPC Access for Cloud Run or Cloud Functions. Cloud Run and Cloud Functions are not inside your VPC by default. If your Cloud SQL instance is on private IP, your serverless service needs Serverless VPC Access to reach it. Without it, connection attempts will time out rather than produce a clear error message, making it hard to diagnose. See Cloud SQL Connection Refused for how to work through connection failures systematically.
Misunderstanding what GKE pods need for private access. Even if your GKE cluster is in the same VPC as the Cloud SQL instance, pod CIDR ranges are separate from node IPs. Firewall rules may need to explicitly allow egress from the pod CIDR to the Cloud SQL IP range. Using the Auth Proxy sidecar with Workload Identity is simpler than managing this manually and is the recommended pattern regardless.
Creating an IAM database user but forgetting database-level grants. Enabling IAM database authentication and creating the IAM user in the database does not automatically grant access to any schemas or tables. You still need to run
GRANTstatements inside the database. IAM controls who can log in; database roles control what they can do after login. Both are required.
Summary
- Network path and authentication are two separate decisions. Understand both before picking a connection method.
- For production: use private IP (
—no-assign-ip) so the instance is never reachable from the internet - Run the Auth Proxy alongside your application for IAM-managed, TLS-encrypted connections. It works in Cloud Run, GKE sidecars, and on VMs.
- The proxy requires
roles/cloudsql.clienton the service account. This is separate from any database-level permissions. - In GKE: use the proxy as a sidecar and Workload Identity for credentials. Never mount service account key files.
- In Cloud Run: use the built-in Cloud SQL connector. For private IP instances, also configure Serverless VPC Access.
- IAM database authentication replaces native passwords with short-lived tokens. Recommended for production service accounts.
- Store database credentials in Secret Manager, not in environment variables or config files.
- Never open authorised networks to
0.0.0.0/0, not even temporarily.
Frequently asked questions
What is the safest way to connect to Cloud SQL in production?
Use private IP so your instance is never reachable from the public internet, and run the Cloud SQL Auth Proxy for authenticated, encrypted connections. In Cloud Run and GKE, the proxy is the standard pattern. Grant your application service account the roles/cloudsql.client role, use IAM database authentication where possible, and store database credentials in Secret Manager rather than hardcoding them in environment variables.
Do I need both private IP and the Cloud SQL Auth Proxy?
They solve different problems, and using both is the recommended production pattern. Private IP controls the network path — your database is not reachable from the public internet at all. The Auth Proxy controls authentication — it verifies IAM identity and encrypts the connection. You can use private IP without the proxy, and you can use the proxy with a public IP instance. For production, using both gives you network isolation and strong IAM-managed authentication. They are complementary, not alternatives.
Is public IP ever acceptable for Cloud SQL?
Public IP is acceptable for local development when connecting from a laptop and you do not want to set up a VPN. If you use public IP, always connect via the Auth Proxy — never open authorised networks to 0.0.0.0/0. In production, prefer private IP so the instance is not exposed on the internet at all, even with a restricted allowlist.
How do Cloud Run and GKE connect to Cloud SQL securely?
Cloud Run has built-in Cloud SQL connector support. Add the instance connection name to your Cloud Run service configuration, assign a service account with roles/cloudsql.client, and connect via a Unix socket at /cloudsql/PROJECT:REGION:INSTANCE. For a private IP instance, also configure Serverless VPC Access. For GKE, run the Auth Proxy as a sidecar container in the same pod, use Workload Identity to provide credentials, and connect your application to 127.0.0.1:PORT. Neither platform should use service account key files for credentials.
What is the difference between IAM database authentication and the Cloud SQL Auth Proxy?
They operate at different layers. The Auth Proxy handles the network connection — it authenticates to the Cloud SQL API via IAM and creates an encrypted tunnel so your application connects to a local port. IAM database authentication handles login inside the database — instead of a native database password, you use a short-lived IAM token. You can use the proxy without IAM database authentication (with a native database user), and you can use IAM database authentication with or without the proxy. Most production setups use both.