Postgres on Google Cloud / Lesson 3 of 8

Your first connection

Two tools, two commands, a prompt. Then the four ways it fails, because you will meet at least two of them.

≈ 20 minutes · hands on · you need an instance you already have access to

This is the lesson that produces a working psql prompt. Nothing here requires the infrastructure repository, and nothing here can change infrastructure — these are read-only client tools and one local process.

Three placeholders appear throughout. Get them from whoever runs the instance and write them down once:

PlaceholderLooks likeWhat it is
INSTANCE_URI PROJECT_ID:REGION:INSTANCE_NAME The instance connection name. Three colon-separated parts, always. Not a hostname, and there is no port in it.
DB_NAME appdb The database inside the instance. One instance holds many.
DB_USER you@example.com Your Google email address. Yes, really — that is the PostgreSQL username.

Step 1 — install two things

# macOS
brew install cloud-sql-proxy libpq

# Debian / Ubuntu: the proxy is a single static binary.
# Check github.com/GoogleCloudPlatform/cloud-sql-proxy/releases for VERSION.
curl -o cloud-sql-proxy \
  https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/VERSION/cloud-sql-proxy.linux.amd64
chmod +x cloud-sql-proxy
sudo apt install postgresql-client

You need the psql client only. Nothing runs a local PostgreSQL server, and installing a full server package is a common and harmless waste of time.

Check your psql version before anything else

Many systems ship an old psql ahead of the one you just installed. Run psql --version. If it is older than the server, you will hit strange errors much later and blame the wrong thing. On Homebrew, keg-only formulae need to be put on PATH explicitly.

Step 2 — authenticate once

gcloud auth application-default login

This opens a browser, and writes the credential the proxy will read. It is the application-default variant, as covered in lesson 1. You will re-run this whenever it expires — often about once a day under a corporate reauthentication policy.

Step 3 — start the proxy and leave it running

cloud-sql-proxy --port 5432 --auto-iam-authn \
  PROJECT_ID:REGION:INSTANCE_NAME

It prints that it is listening and then stays in the foreground. Leave it in its own terminal tab. Two flags are doing all the work:

FlagEffect
--port 5432 Where it listens on your machine. Pick anything free. If you run a local PostgreSQL, use 5433 and save yourself an hour of confusion about which database you are looking at.
--auto-iam-authn Turns on automatic IAM database authentication: the proxy mints your access token and supplies it as the password. Without this flag the proxy still tunnels, but expects a real database password.

What it is actually doing

your client cloud-sql-proxy Google ─────────── ─────────────── ────── psql ──────────────► 127.0.0.1:5432 (plaintext, │ loopback only) ├──► sqladmin.googleapis.com:443 │ instance metadata + │ ephemeral client certificate │ ├──► oauth2.googleapis.com:443 │ access token (1 hour) │ └──► instance IP :3307 mutual TLS

Three facts in that diagram earn their place:

Step 4 — connect

psql -h 127.0.0.1 -p 5432 -U you@example.com -d appdb

No -W, no password prompt, no password. If one is requested, hit Enter — an empty password is correct, and being asked usually means --auto-iam-authn is missing from the proxy command.

For a GUI client — DBeaver, TablePlus, DataGrip, pgAdmin — the same four values:

FieldValue
Host127.0.0.1
Port5432, or whatever you passed to the proxy
Databaseappdb
Usernameyour Google email
Passwordleave empty
SSL modedisable — see below
The most common self-inflicted failure

Turning SSL on in your client. It feels like the safe choice and it breaks the connection with no encryption or SSL is not enabled on the server. The encrypted leg is proxy → instance. Your client is talking to a socket on your own machine. Set SSL to disable.

Step 5 — prove it is you

SELECT current_user, current_database();

This is not ceremony. current_user must come back as your email address. If it comes back as anything else — postgres, an application role, a migration account — you are connected through a different route than you think, probably a second proxy on another port, using a shared password identity. Everything you do will be attributed to that account in the audit log.

The four ways this fails

What you seeWhat it means
403 on connectSettings, when the proxy starts Your identity lacks roles/cloudsql.client on the project. The proxy never got off the ground. → layer 1
password authentication failed for user "you@example.com" The proxy worked. You are not an IAM database user on that instance. → layer 2
permission denied for table … You are in. PostgreSQL grants are missing. → layer 3
server closed the connection unexpectedly Almost always the proxy's credentials expired, typically a proxy left running overnight. See below.

The one that wastes the most time

A proxy left running outlives its credentials, and it fails opaquely. It keeps listening. It accepts your TCP connection. Then it drops it. What psql reports is server closed the connection unexpectedly, which reads as a database problem, and people go and check whether the instance is up.

The habit that saves the hour

First thing each working day, kill and restart any proxy left from yesterday. Do it before investigating any connection problem at all. If the proxy complains on restart, re-run gcloud auth application-default login.

Check yourself

Your firewall permits outbound 443 and 5432. The proxy starts cleanly, then connections hang. Why?

The proxy reaches the Admin API on 443 fine — that is why it starts cleanly. The database connection itself goes to port 3307, which your rules do not cover. The giveaway is "starts cleanly, then hangs": an authentication problem fails fast, a blocked port times out.

Your GUI client reports SSL is not enabled on the server. What is wrong?

Your client is asking for TLS on the connection to the proxy, which is a plaintext loopback socket by design. Set SSL mode to disable. The encryption you care about is on the leg you cannot see.

From memory: three destinations the proxy needs to reach, with ports.

sqladmin.googleapis.com:443 for instance metadata and the ephemeral certificate; oauth2.googleapis.com:443 to exchange credentials for an access token; and the instance IP on 3307 for the database connection itself.