Create the Instance
Cloud SQL instances come in shared-core (cheap, slow, perfect for this) and dedicated-core (real workloads). We want shared-core.
gcloud sql instances create rag-db \
--database-version=POSTGRES_16 \
--tier=db-f1-micro \
--region=$REGION \
--storage-size=10 \
--storage-type=SSD \
--database-flags=cloudsql.iam_authentication=onThis takes 5–10 minutes. Go grab coffee.
What the flags mean:
| Flag | What it does |
|---|---|
--database-version=POSTGRES_16 | Latest stable Postgres on Cloud SQL — ships with a recent pgvector |
--tier=db-f1-micro | Shared-core machine. ~$8/month if always on. The cheapest tier that exists. |
--region=$REGION | Same region as Cloud Run will be in — keeps traffic free |
--storage-size=10 | 10 GB of disk. Way more than we need; this is the minimum. |
--database-flags=cloudsql.iam_authentication=on | Lets us connect using our Google identity instead of a password |
When it finishes you will see the connection name (e.g. my-rag-1716580000:us-central1:rag-db). Stash it:
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe rag-db \
--format='value(connectionName)')
echo $INSTANCE_CONNECTION_NAMEThat string — project:region:instance — is the address every connector library needs.
Create the Database
The instance is a Postgres server. We still need to create the actual database inside it.
gcloud sql databases create rag --instance=rag-dbCreate a Database User for Yourself
Because we turned on IAM auth, we can create a Postgres user that is your Google identity. No password to manage.
# Your Google account email
export USER_EMAIL=$(gcloud config get-value account)
gcloud sql users create $USER_EMAIL \
--instance=rag-db \
--type=cloud_iam_userNow when you connect, Cloud SQL checks "is this an IAM principal in the right project with the right role?" — no password is involved. We grant the role next.
Grant Yourself the IAM Role
To actually log in, your account needs roles/cloudsql.instanceUser on the project:
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="user:$USER_EMAIL" \
--role="roles/cloudsql.instanceUser"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="user:$USER_EMAIL" \
--role="roles/cloudsql.client"instanceUser = "you can log in via IAM auth." client = "you can connect via the connector."
Connect and Enable pgvector
The easiest way to run SQL against your instance from your laptop is gcloud sql connect. It tunnels through Google's network so you don't have to expose a public IP.
gcloud sql connect rag-db --user=$USER_EMAIL --database=ragIt will prompt for a password — for IAM users, type your current ADC access token. Easier: hit Ctrl-C and use the one-liner below instead.
gcloud sql connect rag-db --user=$USER_EMAIL --database=rag --quietOnce you're at the rag=> prompt, turn on the extension and create the schema:
-- Turn on pgvector
CREATE EXTENSION IF NOT EXISTS vector;
-- The single table that holds every chunk and its embedding
CREATE TABLE IF NOT EXISTS chunks (
id BIGSERIAL PRIMARY KEY,
source TEXT NOT NULL,
chunk_index INTEGER NOT NULL,
content TEXT NOT NULL,
embedding vector(768) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- An ANN index for fast similarity search
CREATE INDEX IF NOT EXISTS chunks_embedding_hnsw
ON chunks
USING hnsw (embedding vector_cosine_ops);
-- Confirm
\dx vector
\d chunksWhat you just did:
CREATE EXTENSION vectorturns onpgvector. Thevector(768)column type comes from this.embedding vector(768)— Vertex AI'stext-embedding-005returns 768-dimension vectors. The column has to match.USING hnsw (embedding vector_cosine_ops)builds a Hierarchical Navigable Small World index. It's the fast ANN index that ships with modernpgvector.vector_cosine_opsis the right choice when you'll query with<=>(cosine distance), which is what we'll do.
Type \q to exit the psql session.
Save the Connection Name as a Secret
The Cloud Run service will need the instance connection name at runtime. Drop it in Secret Manager so we never paste it into a Dockerfile:
echo -n "$INSTANCE_CONNECTION_NAME" | gcloud secrets create db-connection-name \
--replication-policy=automatic \
--data-file=-Connection names aren't really secret, but Secret Manager is also the right home for any env config that varies between dev and prod. We'll add the actual DB name and user the same way in Step 7.
Stop the Instance When You're Done
Cloud SQL bills you per second the instance is running, not just per query. If you walk away for a few days, stop it:
gcloud sql instances patch rag-db --activation-policy=NEVERTo start it back up:
gcloud sql instances patch rag-db --activation-policy=ALWAYSStorage still costs pennies per month while stopped. Compute drops to zero.
What You Have Now
- A Postgres 16 instance named
rag-dbin your region - A database named
raginside it - A user that is you (your Google account)
- The
vectorextension turned on - A
chunkstable with an HNSW index, ready to receive embeddings - The connection name stored in Secret Manager
Next: scaffold the Python project and prove your laptop can talk to the database.
Reference: Cloud SQL Postgres pricing · Cloud SQL IAM authentication · pgvector HNSW indexes · Cloud SQL admin commands