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=on

This takes 5–10 minutes. Go grab coffee.

What the flags mean:

FlagWhat it does
--database-version=POSTGRES_16Latest stable Postgres on Cloud SQL — ships with a recent pgvector
--tier=db-f1-microShared-core machine. ~$8/month if always on. The cheapest tier that exists.
--region=$REGIONSame region as Cloud Run will be in — keeps traffic free
--storage-size=1010 GB of disk. Way more than we need; this is the minimum.
--database-flags=cloudsql.iam_authentication=onLets 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_NAME

That 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-db

Create 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_user

Now 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=rag

It 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 --quiet

Once 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 chunks

What you just did:

  • CREATE EXTENSION vector turns on pgvector. The vector(768) column type comes from this.
  • embedding vector(768) — Vertex AI's text-embedding-005 returns 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 modern pgvector. vector_cosine_ops is 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=NEVER

To start it back up:

gcloud sql instances patch rag-db --activation-policy=ALWAYS

Storage still costs pennies per month while stopped. Compute drops to zero.

What You Have Now

  • A Postgres 16 instance named rag-db in your region
  • A database named rag inside it
  • A user that is you (your Google account)
  • The vector extension turned on
  • A chunks table 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