GCP Cloud Run Configuration
This guide covers deploying ForgeX microservices to Google Cloud Platform using Cloud Run, Cloud SQL, and Cloud Storage.
Prerequisites
Ensure these tools are installed and authenticated:
# Verify GCP authentication
gcloud auth list
# Set project
gcloud config set project forge-475221
# Verify Node.js and npm
node --version # Should be 18+
npm --version
Frontend Deployment
Frontend applications are built as static sites and deployed to Google Cloud Storage, served via Cloud CDN.
Portal Frontend
cd services/portal/frontend
# Build with production environment variables
VITE_API_URL=https://bids.precisionsiteservices.com/api \
VITE_BIDS_URL=https://bids.precisionsiteservices.com \
VITE_PROJECTS_URL=https://projects.precisionsiteservices.com \
VITE_FIELD_URL=https://field.precisionsiteservices.com \
VITE_GOOGLE_CLIENT_ID=45561947981-6ils22m4mcjlr9q43cn11q8kq2vl6mod.apps.googleusercontent.com \
VITE_COOKIE_DOMAIN=.precisionsiteservices.com \
npm run build
# Upload to Cloud Storage
cd ../../..
gcloud storage rsync -r services/portal/frontend/dist gs://forge-475221-portal --delete-unmatched-destination-objects
# Invalidate CDN cache
gcloud compute url-maps invalidate-cdn-cache forge-lb --path "/*" --async
Bids Frontend
CRITICAL: Environment variables must be production URLs. Localhost references will break production!
cd services/bids/frontend
# Build with production URLs
VITE_API_URL=https://bids.precisionsiteservices.com/api \
VITE_PORTAL_URL=https://forge.precisionsiteservices.com \
VITE_GOOGLE_CLIENT_ID=45561947981-6ils22m4mcjlr9q43cn11q8kq2vl6mod.apps.googleusercontent.com \
VITE_COOKIE_DOMAIN=.precisionsiteservices.com \
npm run build
# Verify no localhost references
grep -r "localhost" dist/ 2>/dev/null | grep -v "node_modules" || echo "✓ No localhost references found"
# Upload to Cloud Storage
cd ../../..
gcloud storage rsync -r services/bids/frontend/dist gs://forge-475221-bids --delete-unmatched-destination-objects
# Invalidate CDN cache
gcloud compute url-maps invalidate-cdn-cache forge-lb --path "/*" --async
CDN propagation takes 3-5 minutes. Test in an incognito window after this time.
Backend Deployment
Backend services are containerized and deployed to Cloud Run with Cloud SQL integration.
Step 1: Build Docker Image
Cloud Build is used to build Docker images from the repository:
# MUST run from repo root!
gcloud builds submit --config services/bids/backend/cloudbuild.yaml --timeout=20m
This creates gcr.io/forge-475221/bids-backend:latest.
Step 2: Deploy to Cloud Run
- Bash (Linux/Mac)
- PowerShell (Windows)
# Set secrets first
export DB_PASSWORD='your-database-password'
export GOOGLE_CLIENT_SECRET='your-google-client-secret'
export RESEND_API_KEY='re_xxxxx'
export SUPERTOKENS_URI='https://forge-supertokens-xxxxx.run.app'
# Deploy
gcloud run deploy forge-bids-backend \
--image gcr.io/forge-475221/bids-backend:latest \
--platform managed \
--region us-south1 \
--allow-unauthenticated \
--memory 512Mi \
--cpu 1 \
--min-instances 1 \
--max-instances 10 \
--add-cloudsql-instances forge-475221:us-south1:forge-postgres-prod \
--set-env-vars "DATABASE_URL=postgresql://forgeapp:${DB_PASSWORD}@localhost/bids_db?host=/cloudsql/forge-475221:us-south1:forge-postgres-prod" \
--set-env-vars "SUPERTOKENS_CONNECTION_URI=${SUPERTOKENS_URI}" \
--set-env-vars "API_DOMAIN=https://bids.precisionsiteservices.com" \
--set-env-vars "WEBSITE_DOMAIN=https://forge.precisionsiteservices.com" \
--set-env-vars "GOOGLE_CLIENT_ID=45561947981-6ils22m4mcjlr9q43cn11q8kq2vl6mod.apps.googleusercontent.com" \
--set-env-vars "GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}" \
--set-env-vars "RESEND_API_KEY=${RESEND_API_KEY}" \
--set-env-vars "EMAIL_FROM=Precision Platform <noreply@precisionsiteservices.com>" \
--set-env-vars "NODE_ENV=production"
# Set secrets
$env:DB_PASSWORD='your-database-password'
$env:GOOGLE_CLIENT_SECRET='your-google-client-secret'
$env:RESEND_API_KEY='re_xxxxx'
$env:SUPERTOKENS_URI='https://forge-supertokens-xxxxx.run.app'
# Deploy via Git Bash (handles commas correctly)
& "D:\DevTools\Git\bin\bash.exe" -c "gcloud run deploy forge-bids-backend --image gcr.io/forge-475221/bids-backend:latest --platform managed --region us-south1 --allow-unauthenticated --memory 512Mi --cpu 1 --min-instances 1 --max-instances 10 --add-cloudsql-instances forge-475221:us-south1:forge-postgres-prod --set-env-vars 'DATABASE_URL=postgresql://forgeapp:${env:DB_PASSWORD}@localhost/bids_db?host=/cloudsql/forge-475221:us-south1:forge-postgres-prod' --set-env-vars 'SUPERTOKENS_CONNECTION_URI=${env:SUPERTOKENS_URI}' --set-env-vars 'API_DOMAIN=https://bids.precisionsiteservices.com' --set-env-vars 'WEBSITE_DOMAIN=https://forge.precisionsiteservices.com' --set-env-vars 'GOOGLE_CLIENT_ID=45561947981-6ils22m4mcjlr9q43cn11q8kq2vl6mod.apps.googleusercontent.com' --set-env-vars 'GOOGLE_CLIENT_SECRET=${env:GOOGLE_CLIENT_SECRET}' --set-env-vars 'RESEND_API_KEY=${env:RESEND_API_KEY}' --set-env-vars 'EMAIL_FROM=Precision Platform <noreply@precisionsiteservices.com>' --set-env-vars 'NODE_ENV=production'"
Cloud Run automatically sets PORT=8080. Do not include it in your deployment command.
Step 3: Verify Deployment
# Check health endpoint
curl https://bids.precisionsiteservices.com/api/health
# Expected output: {"status":"ok","timestamp":"...","service":"bids"}
# View Cloud Run logs
gcloud run services logs read forge-bids-backend --region us-south1 --limit 50
SuperTokens Deployment
SuperTokens Core handles authentication sessions. This is a one-time setup.
SuperTokens (Java) cannot use Cloud SQL Auth Proxy Unix sockets. It requires TCP connection to Cloud SQL public IP.
Step 1: Create SuperTokens Database
# Start Cloud SQL Proxy
.\cloud-sql-proxy.exe forge-475221:us-south1:forge-postgres-prod --port=5432
# In another terminal, create database
psql -h 127.0.0.1 -U forgeapp -d postgres -c "CREATE DATABASE supertokens_db;"
Step 2: Get Cloud SQL Public IP
gcloud sql instances describe forge-postgres-prod --format="value(ipAddresses[0].ipAddress)"
# Example output: 34.174.7.92
Step 3: Deploy SuperTokens to Cloud Run
export DB_PASSWORD='your-database-password'
export CLOUD_SQL_IP='34.174.7.92' # From step 2
gcloud run deploy forge-supertokens \
--image docker.io/supertokens/supertokens-postgresql:9.3 \
--platform managed \
--region us-south1 \
--allow-unauthenticated \
--memory 512Mi \
--cpu 1 \
--min-instances 1 \
--port 3567 \
--set-env-vars "POSTGRESQL_HOST=${CLOUD_SQL_IP}" \
--set-env-vars "POSTGRESQL_PORT=5432" \
--set-env-vars "POSTGRESQL_DATABASE_NAME=supertokens_db" \
--set-env-vars "POSTGRESQL_USER=forgeapp" \
--set-env-vars "POSTGRESQL_PASSWORD=${DB_PASSWORD}"
Step 4: Verify SuperTokens
# Note the service URL from deployment output
export SUPERTOKENS_URL='https://forge-supertokens-xxxxx.us-south1.run.app'
# Health check
curl $SUPERTOKENS_URL/
# Expected: Hello
# API version
curl $SUPERTOKENS_URL/apiversion
# Expected: {"versions":["2.0","3.0",...]}
Step 5: Update Backend Environment
Use the SuperTokens URL as SUPERTOKENS_CONNECTION_URI in backend deployments.
Current Production URL: https://forge-supertokens-45561947981.us-south1.run.app
Database Schema Updates
When Prisma schema changes, push to production database:
# 1. Start Cloud SQL Proxy
.\cloud-sql-proxy.exe forge-475221:us-south1:forge-postgres-prod --port=5432
# 2. Set DATABASE_URL (separate terminal)
cd services/bids/backend
export DATABASE_URL='postgresql://forgeapp:YOUR_PASSWORD@127.0.0.1:5432/bids_db'
# 3. Push schema
npx prisma db push
# 4. Redeploy backend (see Backend Deployment section)
Always backup the database before schema changes:
gcloud sql backups create --instance=forge-postgres-prod
Google OAuth Configuration
Configure OAuth redirect URIs in GCP Console → APIs & Services → Credentials:
Production URIs:
https://forge-bids-backend-45561947981.us-south1.run.app/auth/callback/google
https://bids.precisionsiteservices.com/api/auth/callback/google
Local Development:
http://localhost:5001/api/auth/callback/google
OAuth Client ID: 45561947981-6ils22m4mcjlr9q43cn11q8kq2vl6mod.apps.googleusercontent.com
Automated Deployment Script
For convenience, use the automated deployment script:
./deploy-to-production.sh
This script:
- Verifies GCP authentication
- Builds portal and bids frontends
- Uploads to Cloud Storage
- Builds backend Docker image
- Deploys to Cloud Run
- Invalidates CDN cache
- Runs health checks
Troubleshooting
CDN shows old frontend
Wait 3-5 minutes after cache invalidation. Test in incognito window.
Force refresh: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)
Manual invalidation:
gcloud compute url-maps invalidate-cdn-cache forge-lb --path "/*" --async
Backend changes not appearing
Verify you deployed to the correct service and region:
gcloud run services describe forge-bids-backend --region us-south1
Check the image tag matches:
gcloud run services describe forge-bids-backend --region us-south1 --format="value(spec.template.spec.containers[0].image)"
Database connection failed
- Check Cloud SQL Proxy is running
- Verify DATABASE_URL format includes
/cloudsql/socket path - Ensure Cloud SQL instance is running:
gcloud sql instances list
OAuth login fails
- Verify redirect URIs in GCP Console
- Check
GOOGLE_CLIENT_IDmatches across frontend and backend - Ensure
GOOGLE_CLIENT_SECRETis set in Cloud Run - Check
API_DOMAINandWEBSITE_DOMAINmatch production URLs
SuperTokens connection fails
- Verify
SUPERTOKENS_CONNECTION_URIis set correctly - Check SuperTokens service is running:
curl https://forge-supertokens-xxxxx.run.app/
- Verify Cloud SQL allows connections from SuperTokens
- Check SuperTokens can reach Cloud SQL public IP
Email not sending
- Check
RESEND_API_KEYis set in Cloud Run - Verify domain is verified in Resend Dashboard
- Check Cloud Run logs for Resend API errors
- Ensure
EMAIL_FROMuses verified domain
Cost Optimization
Cloud Run
Min instances: 0 (scale to zero)
Max instances: 10 (prevent runaway costs)
Memory: 512Mi (adequate for most workloads)
Cloud SQL
Machine type: db-f1-micro (shared-core)
Storage: Start at 10GB, auto-increase
Backups: Automated daily, 7-day retention
Cloud Storage
Standard class for active files
Nearline class for backups
CDN enabled for fast delivery
Cloud Build
120 free build-minutes/day
Use --timeout to prevent hung builds
Cache Docker layers for faster builds