Tenant Management Application - Deploy to GCP

12 minute read

After exploring 5 different AWS deployment approaches for our Tenant Management application, we turned to Google Cloud Platform (GCP) for the final chapter of our cloud deployment journey. What we discovered was remarkable: GCP’s serverless Cloud Run + Cloud SQL approach delivers the same functionality at $18-22/month—20% cheaper than even our most cost-optimized AWS setup. This post completes Evolution 6, showcasing how platform choice can dramatically impact both cost and operational simplicity.

Evolution Context: This post is part of Evolution 6: Cloud Deployment Strategies in the Tenant Management Evolutionary Project. This evolution focuses on cloud infrastructure and deployment strategies, building upon the conversational interface established in Evolution 5.

Prerequisites: This post builds on our 5 AWS Deployment Approaches exploration. Familiarity with that post will provide helpful context for the comparisons here.

The Multi-Cloud Challenge

Having deployed our full-stack application (Spring Boot + React + PostgreSQL) to AWS using 5 different approaches, a natural question emerged: How does Google Cloud Platform compare?

Our application stack:

  • Backend: Java 21 with Spring Boot 3.3.4
  • Frontend: React 18 with Material-UI
  • Database: PostgreSQL 16
  • AI Integration: Google Gemini 2.5 Flash

The AWS exploration revealed costs ranging from $10-13/month (Low-Cost EC2 with Spot instances) to $100+/month (Elastic Beanstalk). But each approach had trade-offs—the cheapest lacked high availability, while the expensive options over-provisioned for our needs.

Could GCP offer a better balance?

GCP’s Serverless Advantage: Cloud Run + Cloud SQL

GCP’s answer is elegantly simple: Cloud Run for containers + Cloud SQL for databases. No load balancers to configure, no NAT gateways to pay for, no auto-scaling groups to manage. Just pure serverless simplicity.

Architecture Overview

graph TB
    subgraph Internet
        User[User Browser]
    end
    
    subgraph GCP["Google Cloud Platform"]
        subgraph CloudRun["Cloud Run (Serverless)"]
            Frontend[Frontend Service<br/>React + Nginx<br/>Auto HTTPS]
            Backend[Backend Service<br/>Spring Boot API<br/>Auto HTTPS]
        end
        
        subgraph Database["Cloud SQL"]
            DB[(PostgreSQL 16<br/>db-f1-micro<br/>Shared-core)]
        end
        
        subgraph External["External APIs"]
            Gemini[Google Gemini API<br/>Native Integration]
        end
        
        subgraph CICD["CI/CD"]
            CloudBuild[Cloud Build<br/>Automated Deployment]
            ArtifactRegistry[Artifact Registry<br/>Container Images]
        end
    end
    
    User -->|HTTPS| Frontend
    Frontend -->|/api/*| Backend
    Backend -->|Cloud SQL Proxy| DB
    Backend -->|REST API| Gemini
    CloudBuild -->|Push Images| ArtifactRegistry
    ArtifactRegistry -->|Deploy| CloudRun
    
    style Frontend fill:#4285f4,stroke:#1a73e8,stroke-width:2px,color:#fff
    style Backend fill:#4285f4,stroke:#1a73e8,stroke-width:2px,color:#fff
    style DB fill:#34a853,stroke:#0d652d,stroke-width:2px,color:#fff
    style Gemini fill:#fbbc04,stroke:#f29900,stroke-width:2px,color:#000
    style CloudBuild fill:#ea4335,stroke:#a50e0e,stroke-width:2px,color:#fff

Key Components

Cloud Run Services (Serverless Containers):

  • Frontend: React app served by Nginx, automatically scales 0→production
  • Backend: Spring Boot API with built-in Cloud SQL connectivity
  • Auto-scaling: Handles 0 to 1000+ requests/second automatically
  • HTTPS: Automatic SSL certificates, no configuration needed
  • Pay-per-use: Billed only for actual request processing time

Cloud SQL (Managed PostgreSQL):

  • db-f1-micro: Shared-core instance (0.6 GB RAM, 10 GB SSD)
  • Perfect for: Development, testing, and low-traffic production
  • Automatic backups: Daily backups with 7-day retention
  • High availability: Option to upgrade to regional HA configuration

Cloud Build (CI/CD):

  • Automated deployments: Triggered by git push or manual submission
  • Multi-stage builds: Efficient Docker image creation
  • Free tier: 120 build-minutes per day (plenty for most projects)

Cost Breakdown: The Numbers That Matter

GCP Cloud Run + Cloud SQL: $18-22/month

Component Specification Monthly Cost
Cloud Run - Backend 1 vCPU, 512MB RAM, ~3000 requests/month $0-5 (often free tier)
Cloud Run - Frontend 1 vCPU, 512MB RAM, ~3000 requests/month $0-5 (often free tier)
Cloud SQL Instance db-f1-micro (Shared-core, 0.6GB RAM) $7.67
Cloud SQL Storage 10GB SSD $1.70
Networking Egress (minimal for low traffic) $1-3
Artifact Registry Container image storage $0.50
Cloud Build 120 minutes/day free tier $0
Total   $18-22/month

Cost Comparison: GCP vs All AWS Approaches

Rank Platform Approach Monthly Cost vs GCP
🥇 1 GCP Cloud Run + Shared SQL $18-22 Baseline
2 AWS Low-Cost EC2 (Single VM + Spot) $10-13 45% cheaper
3 AWS App Runner + RDS $50-80 155% more
4 AWS Elastic Beanstalk + RDS $100+ 355%+ more
5 AWS ECS Fargate + RDS $70-120 280% more
6 AWS Standard EC2 + RDS $80-100 345% more

Wait—the AWS Low-Cost EC2 is actually cheaper? Yes, but with critical caveats:

  • No high availability (single point of failure)
  • Self-managed database (you handle backups, scaling, security)
  • Spot instance risk (can be terminated, though rare for t3.small)
  • Manual scaling (no auto-scaling)
  • Manual HTTPS setup (SSL certificate management)

GCP Cloud Run provides enterprise-grade features at a similar price point, making it the clear winner for production-ready deployments.

Deployment Workflow: Terraform + Cloud Build

Infrastructure as Code (Terraform)

# Cloud SQL Instance
resource "google_sql_database_instance" "main" {
  name             = "tenant-db-${random_id.suffix.hex}"
  database_version = "POSTGRES_16"
  region           = var.region
  
  settings {
    tier = "db-f1-micro"  # Shared-core for cost optimization
    disk_size = 10
    disk_type = "PD_SSD"
    
    backup_configuration {
      enabled = true
      start_time = "03:00"  # Daily backups at 3 AM UTC
    }
  }
}

# Cloud Run - Backend Service
resource "google_cloud_run_service" "backend" {
  name     = "tenant-backend"
  location = var.region
  
  template {
    spec {
      containers {
        image = var.backend_image
        
        env {
          name  = "SPRING_PROFILES_ACTIVE"
          value = "prod"
        }
        
        env {
          name  = "POSTGRES_URL"
          value = "jdbc:postgresql:///tenantdb?cloudSqlInstance=${google_sql_database_instance.main.connection_name}&socketFactory=com.google.cloud.sql.postgres.SocketFactory"
        }
      }
    }
    
    metadata {
      annotations = {
        "run.googleapis.com/cloudsql-instances" = google_sql_database_instance.main.connection_name
        "autoscaling.knative.dev/minScale"      = "0"  # Scale to zero
        "autoscaling.knative.dev/maxScale"      = "10"
      }
    }
  }
}

Automated Deployment Pipeline

# cloudbuild.yaml - Automated CI/CD
steps:
  # Build Backend Image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'us-central1-docker.pkg.dev/$PROJECT_ID/tenant-repo/backend:$BUILD_ID', './backend']
  
  # Build Frontend Image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'us-central1-docker.pkg.dev/$PROJECT_ID/tenant-repo/frontend:$BUILD_ID', './frontend']
  
  # Push Images to Artifact Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/tenant-repo/backend:$BUILD_ID']
  
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/tenant-repo/frontend:$BUILD_ID']
  
  # Deploy Backend to Cloud Run
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - 'run'
      - 'deploy'
      - 'tenant-backend'
      - '--image=us-central1-docker.pkg.dev/$PROJECT_ID/tenant-repo/backend:$BUILD_ID'
      - '--region=us-central1'
  
  # Deploy Frontend to Cloud Run
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - 'run'
      - 'deploy'
      - 'tenant-frontend'
      - '--image=us-central1-docker.pkg.dev/$PROJECT_ID/tenant-repo/frontend:$BUILD_ID'
      - '--region=us-central1'

Deployment time: ~8-10 minutes from code commit to production

GCP vs AWS: Comprehensive Comparison

Feature-by-Feature Analysis

Feature AWS Best Option GCP Cloud Run Winner
Lowest Cost EC2 Spot: $10-13 Cloud Run: $18-22 🏆 AWS*
Production-Ready Cost App Runner: $50-80 Cloud Run: $18-22 🏆 GCP
Serverless App Runner, ECS Fargate Cloud Run 🏆 Tie
Auto-Scaling ECS Fargate, App Runner Cloud Run (0→production) 🏆 GCP
Setup Complexity Elastic Beanstalk Cloud Run 🏆 GCP
HTTPS/SSL ALB required ($$) Built-in automatic 🏆 GCP
Cold Starts Fargate: 10-30s Cloud Run: 1-3s 🏆 GCP
Managed Database RDS: $18-25+ Cloud SQL: $9.37 🏆 GCP
CI/CD Integration CodePipeline ($$) Cloud Build (free) 🏆 GCP
Gemini AI Integration External API Native GCP service 🏆 GCP
Multi-Region HA ECS Fargate + ALB Cloud Run + Traffic Manager 🏆 Tie
Control & Flexibility EC2 Compute Engine 🏆 Tie

AWS Spot wins on pure cost, but GCP wins on production-ready value.

Pros & Cons: GCP Cloud Run

✅ Advantages:

  1. True Pay-Per-Use: Billed per 100ms of request processing, not per hour
  2. Zero-Configuration Scaling: Automatically handles 0 to 1000+ req/sec
  3. No Infrastructure Management: No servers, no clusters, no node groups
  4. Built-in Security: HTTPS by default, IAM integration, VPC connectivity
  5. Native Gemini Integration: Same GCP account, simplified billing
  6. Fast Cold Starts: 1-3 seconds vs 10-30 seconds for AWS Fargate
  7. Simple Deployment: One command to deploy (gcloud run deploy)
  8. Free SSL Certificates: Automatic HTTPS with Google-managed certificates

❌ Trade-offs:

  1. Cold Starts Exist: 1-3 second delay after idle periods (mitigated by min instances)
  2. Less Control: Can’t SSH into containers or customize OS
  3. GCP Lock-in: Migrations to other platforms require refactoring
  4. Regional Availability: Not available in all GCP regions
  5. Request Timeout: 60-minute max (though this is generous)
  6. Learning Curve: Different from traditional VM-based deployments

Real-World Performance: What We Measured

Load Testing Results

Using Apache Bench, we tested API response times under various loads:

Scenario Cloud Run (GCP) ECS Fargate (AWS) EC2 (AWS)
Cold Start 1.2s 12.5s 0s (always-on)
Warm Request (p50) 45ms 52ms 48ms
Warm Request (p95) 120ms 180ms 135ms
100 req/sec sustained 58ms avg 65ms avg 61ms avg
Scale 0→100 requests 3.2s 25s N/A (manual)

Key Insights:

  • Cold starts are 10x faster on Cloud Run vs ECS Fargate
  • Warm performance is comparable across all platforms
  • Auto-scaling is dramatically faster on Cloud Run

Cost Under Load

Monthly Requests Cloud Run App Runner EC2 (Always-On)
5,000 $8-12 $45 $22.50
50,000 $15-22 $55 $22.50
500,000 $35-45 $80 $22.50*
5,000,000 $150-200 $250+ $50+**

* Requires scaling to multiple instances
** Requires auto-scaling group and load balancer

Observation: Cloud Run is cost-effective up to ~500K requests/month. Beyond that, consider GKE or Compute Engine with committed use discounts.

Migration Journey: AWS to GCP

We migrated from AWS App Runner to GCP Cloud Run in 6 hours. Here’s how:

Phase 1: Database Migration (2 hours)

# Export from AWS RDS
pg_dump -h aws-rds-endpoint.rds.amazonaws.com \
  -U tenant \
  -d tenantdb \
  > backup.sql

# Import to Cloud SQL
gcloud sql import sql tenant-db-instance \
  gs://migration-bucket/backup.sql \
  --database=tenantdb

Phase 2: Application Configuration (1 hour)

Update application-prod.properties:

# Before (AWS RDS)
spring.datasource.url=jdbc:postgresql://aws-rds.rds.amazonaws.com:5432/tenantdb

# After (GCP Cloud SQL)
spring.datasource.url=jdbc:postgresql:///tenantdb?cloudSqlInstance=PROJECT:REGION:INSTANCE&socketFactory=com.google.cloud.sql.postgres.SocketFactory

Add Cloud SQL Socket Factory dependency:

<dependency>
    <groupId>com.google.cloud.sql</groupId>
    <artifactId>postgres-socket-factory</artifactId>
    <version>1.15.0</version>
</dependency>

Phase 3: Deployment (2 hours)

# Build and deploy backend
gcloud builds submit --config=cloudbuild.yaml .

# Verify deployment
curl https://tenant-backend-PROJECT_ID.us-central1.run.app/actuator/health

Phase 4: DNS & Testing (1 hour)

  • Update DNS to point to new Cloud Run URLs
  • Run integration tests
  • Verify AI chat functionality
  • Load test to confirm performance

Total migration time: 6 hours
Downtime: 15 minutes (DNS propagation)

When to Choose GCP Cloud Run

✅ Choose GCP Cloud Run When:

  1. Cost is critical and you need production-ready features
  2. Serverless is preferred (no server management)
  3. Variable traffic patterns (daily/weekly spikes)
  4. Fast iteration is important (deploy in minutes)
  5. Using Google services (Gemini, BigQuery, etc.)
  6. Small to medium scale (up to 500K requests/month)

❌ Consider AWS Instead When:

  1. Absolute lowest cost is the only factor ($10-13 EC2 Spot)
  2. Already invested in AWS ecosystem (Lambda, DynamoDB, etc.)
  3. Need specific AWS services (SageMaker, Redshift, etc.)
  4. Ultra-high scale with predictable traffic (Reserved Instances save $$$)
  5. Cold starts are unacceptable (use always-on EC2)

⚖️ Either Platform Works When:

  1. Enterprise applications (both have robust enterprise features)
  2. Compliance requirements (both are SOC 2, HIPAA, etc. compliant)
  3. Global distribution (both have worldwide regions)
  4. Kubernetes needed (EKS on AWS, GKE on GCP)

Key Learnings from Evolution 6

Having now deployed to 5 AWS approaches + 1 GCP approach, here are the critical insights:

1. Serverless Isn’t Always More Expensive

Conventional wisdom says serverless costs more. Our data shows otherwise:

  • GCP Cloud Run: $18-22/month (serverless)
  • AWS App Runner: $50-80/month (serverless)
  • AWS ECS Fargate: $70-120/month (serverless)
  • AWS EC2 (Always-On): $80-100/month (traditional)

GCP Cloud Run proves serverless can be the cheapest production-ready option.

2. Platform Choice Impacts Total Cost of Ownership

It’s not just infrastructure costs:

Factor AWS Complex Setup GCP Cloud Run
Infrastructure $70-120/month $18-22/month
Engineering time 20-40 hrs/month 5-10 hrs/month
Effective cost $120-180/month $25-35/month

Operational simplicity is a cost multiplier.

3. Auto-Scaling Architecture Pays Off

Traffic patterns for our app:

  • Weekdays 9 AM - 5 PM: 100-200 requests/hour
  • Nights & Weekends: 0-10 requests/hour

With Cloud Run’s scale-to-zero:

  • Active time: ~40 hours/week (24% of the week)
  • Idle time: 128 hours/week (76% of the week)
  • Cost savings: ~70% vs always-on instances

4. Cold Starts Are Overrated as a Problem

Our measurements:

  • Cloud Run cold start: 1.2 seconds
  • User tolerance: 2-3 seconds for initial load
  • Frequency: Only after 15+ minutes of zero traffic

Mitigation: Set minInstances: 1 for $5-8/month → zero cold starts

5. Infrastructure as Code is Non-Negotiable

Having Terraform configurations for all approaches enabled:

  • Quick experiments: Spin up/down environments in minutes
  • Cost control: Destroy unused environments easily
  • Reproducibility: Deploy to multiple projects/regions identically
  • Version control: Track infrastructure changes in Git

Terraform paid for itself within the first week.

6. Multi-Cloud Competency is a Superpower

Understanding both AWS and GCP:

  • Negotiation leverage: Can genuinely choose either platform
  • Cost optimization: Pick the right service for each workload
  • Risk mitigation: Not locked into one vendor’s pricing changes
  • Career growth: Multi-cloud skills are highly valued

Evolution 6 Complete: The Full Journey

From a single Python file to a multi-cloud deployable application, this evolution has been transformative:

The Complete Evolution Timeline

graph LR
    E1[Evolution 1<br/>Single-File Python<br/>Flask + SQLite]
    E2[Evolution 2<br/>Modular Architecture<br/>Blueprints + React]
    E3[Evolution 3<br/>Enterprise Stack<br/>Spring Boot + PostgreSQL]
    E4[Evolution 4<br/>AI Integration<br/>Gemini API + MCP]
    E5[Evolution 5<br/>Chat Interface<br/>Conversational UX]
    E6[Evolution 6<br/>Multi-Cloud Deploy<br/>5 AWS + 1 GCP]
    
    E1 -->|Refactor| E2
    E2 -->|Rewrite| E3
    E3 -->|Enhance| E4
    E4 -->|Improve UX| E5
    E5 -->|Deploy| E6
    
    style E1 fill:#9e9e9e,stroke:#616161,stroke-width:2px,color:#fff
    style E2 fill:#9e9e9e,stroke:#616161,stroke-width:2px,color:#fff
    style E3 fill:#9e9e9e,stroke:#616161,stroke-width:2px,color:#fff
    style E4 fill:#9e9e9e,stroke:#616161,stroke-width:2px,color:#fff
    style E5 fill:#9e9e9e,stroke:#616161,stroke-width:2px,color:#fff
    style E6 fill:#4caf50,stroke:#2e7d32,stroke-width:4px,color:#fff

What We’ve Accomplished

Technology Mastery:

  • ✅ Python → Java → React → Docker → Kubernetes concepts
  • ✅ Flask → Spring Boot → Enterprise patterns
  • ✅ SQLite → PostgreSQL → Cloud databases
  • ✅ Monolith → Microservices architecture
  • ✅ Traditional → AI-enhanced applications
  • ✅ Single-cloud → Multi-cloud deployments

Deployment Expertise:

  • ✅ 6 complete deployment approaches implemented
  • ✅ Cost analysis across $10-$150/month spectrum
  • ✅ Trade-off understanding for real-world decisions
  • ✅ Infrastructure as Code mastery (Terraform)
  • ✅ CI/CD pipeline automation (CodePipeline, Cloud Build)

Practical Decision Framework:

Use Case Recommended Approach Cost Reason
Prototype/Demo AWS EC2 Spot $10-13 Lowest absolute cost
Dev/Test Environment GCP Cloud Run $18-22 Best feature/cost balance
Low-Traffic Production GCP Cloud Run $18-22 Production-ready at minimal cost
Growing Startup GCP Cloud Run + upgrade to Standard SQL $60-70 Scales seamlessly
Enterprise App AWS ECS Fargate or GCP GKE $100-150 Proven scalability

Conclusion: The Multi-Cloud Future

After exploring 6 deployment approaches across AWS and GCP, the conclusion is clear: there is no one-size-fits-all solution. The “best” deployment depends entirely on your requirements:

For this project (Tenant Management System):

  • Development: GCP Cloud Run ($18-22/month) ← Winner for value
  • Production: GCP Cloud Run with Standard SQL ($60-70/month)
  • Budget-Constrained: AWS EC2 Spot ($10-13/month) ← Winner for absolute cost
  • Enterprise Scale: AWS ECS Fargate or GKE Autopilot ($100-150/month)

The Real Lesson: Understanding the trade-offs empowers informed decisions. Cost isn’t everything—operational simplicity, team expertise, existing infrastructure, and business requirements all play crucial roles.

What’s Next?

While Evolution 6 is complete, the learning continues:

Future Explorations:

  • ☐ Kubernetes deep dive (EKS vs GKE)
  • ☐ Azure deployment comparison
  • ☐ Performance optimization at scale
  • ☐ Multi-region deployment strategies
  • ☐ Cost optimization advanced techniques

Resources

GitHub Repository:

Related Posts:

GCP Documentation:


Thank you for following this evolutionary journey! From a single Python file to a production-ready, multi-cloud, AI-enhanced application—we’ve covered the full spectrum of modern full-stack development. The skills, patterns, and decision-making frameworks developed through this evolution will serve as a foundation for every future architectural choice.

Evolution 6: Complete
The Tenant Management Evolutionary Project: Complete 🚀

Posts in Evolution 6: Cloud Deployment Strategies

Explore other posts in this evolution to understand the complete learning journey.

Tenant Management Application: 5 AWS Deployment Approaches Compared

Evolution 6: Exploring and comparing 5 different AWS deployment strategies for a full-stack application—from $10/mont...

Tenant Management Application - Deploy to GCP

Evolution 6 (Final): Deploying our full-stack application to Google Cloud Platform using Cloud Run and Cloud SQL. At ...

Evolution Summary

Total Posts: 2

Focus: AWS deployment approaches and cost optimization

Technologies: AWS, Terraform, Docker, CI/CD

Evolution Context

Project: Tenant Management: An Evolutionary Project

Evolution: Cloud Deployment Strategies

Focus: AWS deployment approaches and cost optimization

Status: ✅ Complete