GCP project setup
Quayside’s recommended cloud deployment uses Google Cloud Run for the API, Firebase Hosting for the dashboard and docs, and an external Postgres (Neon, AWS RDS, Cloud SQL — whatever fits your shape). This page walks through the one-time GCP foundation: project, APIs, container registry, secrets, and the runtime service account.
You only run these steps once per environment (demo, staging, prod). After this, deployment is a single command — see Self-hosting.
Prerequisites
You need:
- A Google account with billing set up
gcloudCLI installed (brew install google-cloud-sdkon macOS, or download)- Authenticated locally:
gcloud auth login
Verify:
gcloud --versiongcloud auth list # shows your active accountgcloud billing accounts list # at least one entry, OPEN = TrueIf gcloud billing accounts list is empty, set up billing in the GCP console before continuing.
A. Project + billing
Project IDs are globally unique, lowercase, 6–30 chars, hyphens allowed. Pick something namespaced to your org or your name.
export PROJECT_ID="quayside-demo-yourname" # ← yoursexport BILLING_ACCOUNT="01XXXX-XXXXXX-XXXXXX" # from `gcloud billing accounts list`
gcloud projects create "$PROJECT_ID" --name="Quayside Demo"gcloud billing projects link "$PROJECT_ID" --billing-account="$BILLING_ACCOUNT"gcloud config set project "$PROJECT_ID"If reusing an existing project, skip the create and just gcloud config set project <existing>.
B. Enable required APIs
These are the GCP services Quayside touches. Enabling is free; you only pay for usage.
gcloud services enable \ run.googleapis.com \ artifactregistry.googleapis.com \ cloudbuild.googleapis.com \ secretmanager.googleapis.com \ cloudscheduler.googleapis.com \ iam.googleapis.com| API | Why |
|---|---|
run | Cloud Run service — where the API container runs |
artifactregistry | Where Cloud Run pulls the image from |
cloudbuild | Builds the Docker image (optional — you can build locally and push) |
secretmanager | Holds the DB URL, JWT secret, provider keys |
cloudscheduler | Triggers the nightly demo-reset job (optional) |
iam | Service-account bindings |
Takes about 30 seconds.
C. Artifact Registry repository
This is where your Quayside image lives.
export REGION="europe-west2" # pick a region close to your Postgres
gcloud artifacts repositories create quayside \ --repository-format=docker \ --location="$REGION" \ --description="Quayside container images"
# Configure local Docker to push to this repogcloud auth configure-docker "${REGION}-docker.pkg.dev"Region pairing rules of thumb:
| Your Postgres | GCP region |
|---|---|
Neon eu-west-2 (AWS London) | europe-west2 (London) |
Neon us-east-2 (AWS Ohio) | us-east4 (Virginia) — closest |
AWS RDS us-west-2 | us-west1 (Oregon) |
Cloud SQL in us-central1 | same — us-central1 |
Cross-region adds 30–100ms of round-trip per database query and the proxy makes ~6 queries per call. Picking the right region matters more than picking the right CPU shape.
D. Secret Manager — store secrets
Three secrets go in Secret Manager:
# 1. Postgres connection stringecho -n 'postgresql://USER:PASS@HOST.neon.tech/DB?sslmode=require' \ | gcloud secrets create quayside-database-url --data-file=-
# 2. JWT signing secret — 32 random bytesopenssl rand -base64 32 \ | gcloud secrets create quayside-jwt-secret --data-file=-
# 3. (Optional) Anthropic API key — for real LLM forwarding# Leave this unset and the proxy uses MockUpstream — fine for demo.# echo -n 'sk-ant-...' \# | gcloud secrets create quayside-anthropic-api-key --data-file=-Verify:
gcloud secrets listYou should see two (or three) secrets.
E. Runtime service account
Cloud Run runs as this identity. It needs to pull images from Artifact Registry and read secrets from Secret Manager.
gcloud iam service-accounts create quayside-runtime \ --display-name="Quayside Cloud Run runtime"
export SA_EMAIL="quayside-runtime@${PROJECT_ID}.iam.gserviceaccount.com"
# Grant Secret Manager read accessgcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:${SA_EMAIL}" \ --role="roles/secretmanager.secretAccessor"
# Grant Artifact Registry read access (same project is auto-granted; this is belt-and-braces)gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:${SA_EMAIL}" \ --role="roles/artifactregistry.reader"Verify:
gcloud iam service-accounts describe "$SA_EMAIL"You should see the display name and email.
F. Persist the config locally
The deploy script (deploy/deploy-api.sh in the repo) sources deploy/.env if it exists. Persist everything you just set up there so you don’t have to remember it next session:
cd <quayside-repo>cp deploy/.env.example deploy/.env# Edit deploy/.env and fill in:# PROJECT_ID, BILLING_ACCOUNT, REGION, SA_EMAIL,# NEON_DATABASE_URL (the actual DSN — for local migration runs)deploy/.env is gitignored; deploy/.env.example is a committed template.
You’re ready
After steps A–F you have:
- A project with billing
- Six APIs enabled
- An Artifact Registry repo
- Two or three secrets in Secret Manager
- A runtime service account with the right permissions
- Local
.envcarrying everything the deploy script needs
No containers yet, no Cloud Run service, no public URL — those come from running ./deploy/deploy-api.sh (see Self-hosting for the deploy step).
Cost expectation
At demo traffic with everything scaled to zero between calls:
| Service | Monthly |
|---|---|
| Cloud Run (API, scale-to-zero) | $0–3 |
| Artifact Registry storage | <$0.10 |
| Secret Manager (10k ops free) | $0 |
| Cloud Scheduler (3 jobs free) | $0 |
| GCP subtotal | ~$3 |
Plus whatever you pay for Postgres outside GCP (Neon free tier covers a demo) and any Anthropic spend you allow.
Tearing it down
If you want to throw away the demo:
gcloud projects delete "$PROJECT_ID"That deletes everything in one shot — Cloud Run service, Artifact Registry repo, Secret Manager entries, service account. The project enters a 30-day pending-deletion state where you can recover it; after 30 days it’s gone.