Skip to content

Google Cloud Platform Deployment

Deploy FraiseQL on Google Cloud Platform using serverless Cloud Run or Kubernetes Engine (GKE).

Quick Start with Cloud Run (Fastest - 10 minutes)

Section titled “Quick Start with Cloud Run (Fastest - 10 minutes)”

1. Build and Push Docker Image to Artifact Registry

Section titled “1. Build and Push Docker Image to Artifact Registry”
Terminal window
# Set project
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
# Create Artifact Registry
gcloud artifacts repositories create fraiseql \
--repository-format=docker \
--location=$REGION
# Configure Docker auth
gcloud auth configure-docker $REGION-docker.pkg.dev
# Build and push
docker build -t fraiseql:latest .
docker tag fraiseql:latest \
$REGION-docker.pkg.dev/$PROJECT_ID/fraiseql/fraiseql:latest
docker push $REGION-docker.pkg.dev/$PROJECT_ID/fraiseql/fraiseql:latest
Terminal window
# Create database instance
gcloud sql instances create fraiseql-prod \
--database-version=POSTGRES_16 \
--tier=db-f1-micro \
--region=$REGION \
--availability-type=REGIONAL \
--backup-start-time=02:00 \
--retained-backups-count=30 \
--transaction-log-retention-days=7
# Create database
gcloud sql databases create fraiseql \
--instance=fraiseql-prod
# Create database user
gcloud sql users create fraiseql \
--instance=fraiseql-prod \
--password="$(openssl rand -base64 32)"
# Get connection string
gcloud sql instances describe fraiseql-prod \
--format='value(connectionName)'
Terminal window
# Create secrets
echo "postgresql://fraiseql:PASSWORD@/fraiseql?host=/cloudsql/PROJECT:REGION:fraiseql-prod" | \
gcloud secrets create fraiseql-database-url --data-file=-
echo "$(openssl rand -base64 32)" | \
gcloud secrets create fraiseql-jwt-secret --data-file=-
echo "https://app.example.com" | \
gcloud secrets create fraiseql-cors-origins --data-file=-
Terminal window
# Deploy service
gcloud run deploy fraiseql \
--image=$REGION-docker.pkg.dev/$PROJECT_ID/fraiseql/fraiseql:latest \
--region=$REGION \
--platform=managed \
--memory=1Gi \
--cpu=1 \
--timeout=60 \
--max-instances=100 \
--min-instances=1 \
--set-env-vars="ENVIRONMENT=production,LOG_LEVEL=info,LOG_FORMAT=json" \
--set-secrets="DATABASE_URL=fraiseql-database-url:latest,JWT_SECRET=fraiseql-jwt-secret:latest,CORS_ORIGINS=fraiseql-cors-origins:latest" \
--add-cloudsql-instances=$PROJECT_ID:$REGION:fraiseql-prod \
--allow-unauthenticated
# Get service URL
gcloud run services describe fraiseql \
--region=$REGION \
--format='value(status.url)'
Terminal window
# Map custom domain
gcloud run domain-mappings create \
--service=fraiseql \
--domain=api.example.com \
--region=$REGION
# Update DNS records (output from above command)
# Add CNAME record pointing to ghs.googleusercontent.com
Cloud Load Balancer (optional, for multiple regions)
|
Cloud Run (Serverless, auto-scaling)
|
Cloud SQL PostgreSQL (Managed database)

1. Create VPC Network (for Cloud SQL private IP)

Section titled “1. Create VPC Network (for Cloud SQL private IP)”
Terminal window
# Create VPC
gcloud compute networks create fraiseql-vpc \
--subnet-mode=custom \
--bgp-routing-mode=regional
# Create subnet
gcloud compute networks subnets create fraiseql-subnet \
--network=fraiseql-vpc \
--region=$REGION \
--range=10.0.0.0/20
# Create Private Service Connection
gcloud compute addresses create fraiseql-db-range \
--global \
--purpose=VPC_PEERING \
--prefix-length=16 \
--network=fraiseql-vpc
gcloud services vpc-peerings connect \
--service=servicenetworking.googleapis.com \
--ranges=fraiseql-db-range \
--network=fraiseql-vpc
Terminal window
gcloud sql instances create fraiseql-prod \
--database-version=POSTGRES_16 \
--tier=db-custom-2-8192 \
--region=$REGION \
--network=fraiseql-vpc \
--no-assign-ip \
--availability-type=REGIONAL \
--backup-start-time=02:00 \
--retained-backups-count=30 \
--transaction-log-retention-days=7 \
--database-flags=cloudsql_iam_authentication=on
Terminal window
# Add Cloud Run service to IAM policy
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$PROJECT_ID@appspot.gserviceaccount.com \
--role=roles/cloudsql.client
# In Cloud Run, use unix socket connection:
# DATABASE_URL=postgresql://fraiseql:pass@/fraiseql?host=/cloudsql/PROJECT:REGION:instance-name

Cloud Run auto-scales automatically based on request count.

To customize:

cloud-run-config.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: fraiseql
namespace: default
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/minScale: "1"
autoscaling.knative.dev/maxScale: "100"
autoscaling.knative.dev/targetUtilization: "0.7"
spec:
containerConcurrency: 80
containers:
- image: us-central1-docker.pkg.dev/PROJECT/fraiseql/fraiseql:latest

Apply:

Terminal window
gcloud run services replace cloud-run-config.yaml --region=$REGION

For more control and complex workloads:

Terminal window
# Create cluster
gcloud container clusters create fraiseql-cluster \
--region=$REGION \
--num-nodes=3 \
--machine-type=n2-standard-2 \
--enable-autoscaling \
--min-nodes=3 \
--max-nodes=10 \
--enable-autorepair \
--enable-autoupgrade \
--enable-stackdriver-kubernetes \
--addons=HttpLoadBalancing,HttpsLoadBalancing \
--workload-pool=$PROJECT_ID.svc.id.goog \
--enable-network-policy
# Get credentials
gcloud container clusters get-credentials fraiseql-cluster --region=$REGION

Follow the Kubernetes deployment guide with GCP-specific configuration:

fraiseql-gke-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: fraiseql
namespace: default
spec:
replicas: 3
template:
spec:
serviceAccountName: fraiseql
containers:
- name: fraiseql
image: us-central1-docker.pkg.dev/PROJECT/fraiseql/fraiseql:latest
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: fraiseql-secrets
key: database-url
---
apiVersion: v1
kind: Service
metadata:
name: fraiseql
spec:
type: LoadBalancer
selector:
app: fraiseql
ports:
- port: 80
targetPort: 8000

Deploy:

Terminal window
# Store secrets
kubectl create secret generic fraiseql-secrets \
--from-literal=database-url=$DATABASE_URL \
--from-literal=jwt-secret=$JWT_SECRET
# Deploy
kubectl apply -f fraiseql-gke-deployment.yaml
# Check status
kubectl get services fraiseql
kubectl get pods

Logs are automatically collected from Cloud Run and GKE.

View logs:

Terminal window
# Cloud Run logs
gcloud run logs read fraiseql --region=$REGION --limit=100
# GKE logs
kubectl logs deployment/fraiseql --all-containers --follow
Terminal window
# Create alert policy (high error rate)
gcloud alpha monitoring policies create \
--notification-channels=CHANNEL_ID \
--display-name="FraiseQL High Error Rate" \
--condition-display-name="Error rate > 5%" \
--condition-threshold-value=0.05 \
--condition-threshold-filter='resource.type="cloud_run_revision" AND metric.type="run.googleapis.com/request_count" AND resource.label.service_name="fraiseql"'

Enable distributed tracing:

Terminal window
# In your FraiseQL code, initialize tracer
from google.cloud import trace_v2
client = trace_v2.TraceServiceClient()
Terminal window
# Configure backups (already set in creation)
gcloud sql instances patch fraiseql-prod \
--backup-start-time=02:00 \
--retained-backups-count=30
# Create on-demand backup
gcloud sql backups create \
--instance=fraiseql-prod \
--description="Manual backup"
# List backups
gcloud sql backups list --instance=fraiseql-prod
# Restore from backup
gcloud sql backups restore BACKUP_ID \
--backup-instance=fraiseql-prod \
--backup-id=BACKUP_ID
Terminal window
# Create read replica
gcloud sql instances create fraiseql-replica \
--master-instance-name=fraiseql-prod \
--tier=db-f1-micro \
--region=us-west1 # Different region
# Promote replica to standalone
gcloud sql instances promote-replica fraiseql-replica

Cloud Run automatically scales based on request count.

For higher throughput:

Terminal window
# Increase maximum instances
gcloud run services update fraiseql \
--max-instances=500 \
--region=$REGION
# Set minimum instances (keeps instances warm)
gcloud run services update fraiseql \
--min-instances=10 \
--region=$REGION
Terminal window
# Enable insights
gcloud sql instances patch fraiseql-prod \
--enable-database-flags=cloudsql_insights_enabled=on
# View insights
gcloud sql operations list --instance=fraiseql-prod

Create build pipeline:

cloudbuild.yaml
steps:
# Build Docker image
- name: 'gcr.io/cloud-builders/docker'
args:
- 'build'
- '-t'
- '$_IMAGE_NAME'
- '.'
# Push to Artifact Registry
- name: 'gcr.io/cloud-builders/docker'
args:
- 'push'
- '$_IMAGE_NAME'
# Deploy to Cloud Run
- name: 'gcr.io/cloud-builders/gke-deploy'
args:
- 'run'
- '--service='
- 'fraiseql'
- '--region=$_REGION'
- '--image=$_IMAGE_NAME'
substitutions:
_IMAGE_NAME: 'us-central1-docker.pkg.dev/$PROJECT_ID/fraiseql/fraiseql:$SHORT_SHA'
_REGION: 'us-central1'
images:
- '$_IMAGE_NAME'

Trigger from GitHub:

Terminal window
gcloud builds connect --repository-name=fraiseql \
--repository-owner=your-github-org \
--region=$REGION
  • Invocations: $0.40 per 1 million requests
  • Compute time: $0.00001667 per CPU-second
  • Memory: $0.0000025 per GB-second

Example:

1 request at 1 CPU for 1 second = $0.00001667
100,000 requests/month = $0.04

Optimization tips:

  • Use min-instances=0 to save on idle time
  • Lower CPU allocation for I/O-bound apps
  • Cache responses at load balancer
  • Shared core: $8/month (dev only)
  • db-f1-micro: $28/month (small production)
  • db-custom-2-8192: $150+/month (medium workloads)

Optimization tips:

  • Use shared-core for development
  • Enable automated backups (cheaper than manual)
  • Use preemptible VMs if availability not critical
FeatureCloud RunGKE
Setup time5 minutes30 minutes
ScalingAutomaticManual/Automatic
Price (low traffic)$0.04/month$200+/month
Price (high traffic)$0.40/M requestsBetter for sustained load
Latency100-500ms cold startLow (warm)
CustomizationLimitedFull control
Multi-regionEasyMore complex
Terminal window
# Check service status
gcloud run services describe fraiseql --region=$REGION
# View recent deployments
gcloud run revisions list --service=fraiseql --region=$REGION
# View logs during deployment
gcloud builds log $(gcloud builds list --limit=1 --format='value(id)')
Terminal window
# Test connection from Cloud Run
gcloud run exec --service=fraiseql \
--region=$REGION \
-- psql $DATABASE_URL
Terminal window
# Increase memory
gcloud run services update fraiseql \
--memory=2Gi \
--region=$REGION
  • Cloud SQL automated backups enabled
  • Secrets stored in Secret Manager
  • IAM roles configured (least privilege)
  • Cloud Monitoring alerts configured
  • Cloud Logging retention set
  • Read replica in different region
  • VPC network configured (if using private SQL)
  • Cloud Armor security policies (if needed)
  • Cloud CDN enabled (for static content)
  • Load testing completed
  • Disaster recovery plan documented

Cloud Build CI/CD

Set up Cloud Build triggers for automatic deployments on every push to main. Deployment Overview

Cloud Monitoring

Configure Cloud Monitoring dashboards and alert policies. Scaling Guide

Troubleshooting

Debug Cloud Run deployment failures and Cloud SQL connection issues. Troubleshooting Guide