Postgres on Google Cloud / Lesson 3 of 8
Two tools, two commands, a prompt. Then the four ways it fails, because you will meet at least two of them.
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:
| Placeholder | Looks like | What 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. |
# 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.
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.
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.
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:
| Flag | Effect |
|---|---|
--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. |
Three facts in that diagram earn their place:
create outgoing (or egress) connections to your Cloud SQL instance only on port 3307(Cloud SQL Auth Proxy). A firewall that allows 443 and 5432 fails at the last hop, and the symptom looks exactly like the database being down.
the connection between the client applications and the Cloud SQL Auth Proxy client on the client machine is not encrypted. That is fine — it is loopback — and it is why the proxy must run on the same machine as the client.
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:
| Field | Value |
|---|---|
| Host | 127.0.0.1 |
| Port | 5432, or whatever you passed to the proxy |
| Database | appdb |
| Username | your Google email |
| Password | leave empty |
| SSL mode | disable — see below |
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.
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.
| What you see | What 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. |
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.
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.
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.