Google Cloud Deployment
Deploy InstaCRUD to Google Cloud using Cloud Run for serverless containers and Firestore with MongoDB compatibility for the database.
Architecture Overview
┌─────────────────────────────────────────────────────────┐
│ Google Cloud │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Cloud Run │ │ Cloud Run │ │ Firestore │ │
│ │ Frontend │───>│ Backend │───>│ (MongoDB) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │
│ └───────┬───────────┘ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Secret Mgr │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
Components:
- Cloud Run — Serverless container hosting for backend and frontend
- Firestore — MongoDB-compatible database (Enterprise edition)
- Secret Manager — Secure storage for API keys and credentials
- Artifact Registry — Docker image storage
Prerequisites
- Google Cloud account with billing enabled
gcloudCLI installed and configured- Docker installed locally
# Install gcloud CLI
curl https://sdk.cloud.google.com | bash
# Authenticate
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
Step 1: Create Firestore Database
Firestore with MongoDB compatibility provides a fully managed database.
Create Database
- Go to Firestore Console
- Click Create Database
- Select Firestore with MongoDB compatibility (Enterprise edition)
- Choose a location (e.g.,
us-central1) - Set a database ID (e.g.,
instacrud-system)
Get Connection String
- Open your Firestore database
- Click Connect and copy the MongoDB connection string
- Format:
mongodb://USER:PASSWORD@HOST:443/DATABASE?...
Step 2: Set Up Artifact Registry
Create a Docker repository for your images:
# Enable API
gcloud services enable artifactregistry.googleapis.com
# Create repository
gcloud artifacts repositories create instacrud-images \
--repository-format=docker \
--location=us-central1 \
--description="InstaCRUD Docker images"
# Configure Docker authentication
gcloud auth configure-docker us-central1-docker.pkg.dev
Step 3: Build and Push Images
Backend Dockerfile
Ensure your backend Dockerfile exposes port 8080 (Cloud Run requirement):
FROM python:3.11-slim
WORKDIR /app
RUN pip install poetry
COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.create false && poetry install --no-dev
COPY . .
EXPOSE 8080
CMD ["python", "-m", "uvicorn", "instacrud.app:app", "--host", "0.0.0.0", "--port", "8080"]
Build and Push
# Backend
cd backend
docker build -t us-central1-docker.pkg.dev/YOUR_PROJECT/instacrud-images/backend:latest .
docker push us-central1-docker.pkg.dev/YOUR_PROJECT/instacrud-images/backend:latest
# Frontend
cd ../frontend
docker build -t us-central1-docker.pkg.dev/YOUR_PROJECT/instacrud-images/frontend:latest .
docker push us-central1-docker.pkg.dev/YOUR_PROJECT/instacrud-images/frontend:latest
Step 4: Create Secrets
Store sensitive values in Secret Manager:
# Enable API
gcloud services enable secretmanager.googleapis.com
# Create secrets
echo -n "your-brevo-api-key" | gcloud secrets create BREVO_API_KEY --data-file=-
echo -n "your-openai-key" | gcloud secrets create OPENAI_API_KEY --data-file=-
echo -n "your-anthropic-key" | gcloud secrets create ANTHROPIC_API_KEY --data-file=-
Step 5: Deploy Backend to Cloud Run
gcloud run deploy instacrud-backend \
--image=us-central1-docker.pkg.dev/YOUR_PROJECT/instacrud-images/backend:latest \
--region=us-central1 \
--platform=managed \
--allow-unauthenticated \
--port=8080 \
--cpu=1 \
--memory=1Gi \
--max-instances=20 \
--set-env-vars="
BASE_URL=https://instacrud-backend-HASH.us-central1.run.app,
FRONTEND_BASE_URL=https://instacrud-frontend-HASH.us-central1.run.app,
SECRET_KEY=your-jwt-secret,
ALGORITHM=HS256,
TOKEN_EXPIRATION_SECONDS=86400,
DB_ENGINE=firestore,
MONGO_URL=mongodb://USER:PASS@HOST:443/instacrud-system?loadBalanced=true&tls=true&authMechanism=SCRAM-SHA-256&retryWrites=false,
CORS_ALLOW_ORIGINS=*,
CORS_ALLOW_CREDENTIALS=True,
GOOGLE_CLIENT_ID=your-google-client-id,
GOOGLE_CLIENT_SECRET=your-google-client-secret,
MS_CLIENT_ID=your-ms-client-id,
MS_CLIENT_SECRET=your-ms-client-secret,
MS_TENANT_ID=common,
OPEN_REGISTRATION=False,
TURNSTILE_SITE_KEY=your-turnstile-site-key,
TURNSTILE_SECRET_KEY=your-turnstile-secret-key,
TURNSTILE_MODE=normal
" \
--set-secrets="
BREVO_API_KEY=BREVO_API_KEY:latest,
OPENAI_API_KEY=OPENAI_API_KEY:latest,
ANTHROPIC_API_KEY=ANTHROPIC_API_KEY:latest
"
Note the deployed URL (e.g., https://instacrud-backend-abc123-uc.a.run.app).
Step 6: Deploy Frontend to Cloud Run
gcloud run deploy instacrud-frontend \
--image=us-central1-docker.pkg.dev/YOUR_PROJECT/instacrud-images/frontend:latest \
--region=us-central1 \
--platform=managed \
--allow-unauthenticated \
--port=80 \
--cpu=1 \
--memory=512Mi \
--max-instances=3 \
--set-env-vars="
NEXT_PUBLIC_API_BASE_URL=https://instacrud-backend-abc123-uc.a.run.app
"
Step 7: Update Backend URL
After deploying frontend, update the backend with the correct frontend URL:
gcloud run services update instacrud-backend \
--region=us-central1 \
--update-env-vars="FRONTEND_BASE_URL=https://instacrud-frontend-xyz789-uc.a.run.app"
Step 8: Configure OAuth Redirects
Update your OAuth providers with the Cloud Run URLs:
Google Cloud Console
Add redirect URI:
https://instacrud-backend-abc123-uc.a.run.app/oauth/google/callback
Microsoft Azure Portal
Add redirect URI:
https://instacrud-backend-abc123-uc.a.run.app/oauth/microsoft/callback
Custom Domain (Optional)
Map a custom domain to Cloud Run:
gcloud run domain-mappings create \
--service=instacrud-frontend \
--domain=app.yourdomain.com \
--region=us-central1
Follow the DNS verification steps provided by Google.
CI/CD with GitHub Actions
Automate deployments with GitHub Actions:
# .github/workflows/deploy.yml
name: Deploy to Cloud Run
on:
push:
branches: [main]
env:
PROJECT_ID: your-project-id
REGION: us-central1
jobs:
deploy-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- uses: google-github-actions/setup-gcloud@v2
- name: Configure Docker
run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev
- name: Build and Push
run: |
docker build -t ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/instacrud-images/backend:latest ./backend
docker push ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/instacrud-images/backend:latest
- name: Deploy
run: |
gcloud run deploy instacrud-backend \
--image=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/instacrud-images/backend:latest \
--region=${{ env.REGION }}
Firestore Database Management
Create Organization Databases
For multi-tenant setups, create separate databases per organization:
# Create organization database
gcloud firestore databases create \
--database=instacrud-org-acme \
--location=us-central1 \
--type=firestore-native
Database Naming Convention
- System database:
instacrud-system - Organization databases:
instacrud-org-{org-code}
Cost Optimization
Cloud Run Billing Modes
Cloud Run offers two CPU allocation modes that significantly impact costs:
Request-based billing (default):
- You're charged only when processing requests, during startup, and shutdown
- CPU is allocated exclusively during active request processing
- Idle instances are billed at a lower rate
- Best for sporadic or bursty traffic patterns
Instance-based billing:
- Charged for the entire container lifecycle, even when idle
- Required for background async processes, goroutines, threads, or monitoring agents (e.g., OpenTelemetry)
- Better for steady, high-volume traffic
For the lowest cost, use request-based billing with --min-instances=0:
gcloud run deploy instacrud-backend \
--min-instances=0 \
--no-cpu-throttling=false \
# ... other flags
This scales your service to zero when inactive. The tradeoff is cold starts of approximately 30-60 seconds when the first request arrives after idle periods.
Once you have stable traffic, set --min-instances=1 to eliminate cold starts while still benefiting from request-based billing for cost savings.
Request-based billing does not allocate CPU outside of request processing. This means:
- Background tasks and async work after returning responses won't execute
- Long-running background processes are not supported
- Monitoring agents that run continuously won't work properly
If your application requires background processing, you have to use instance-based billing with --no-cpu-throttling, which increases costs significantly.
Cloud Run Instance Settings
--min-instances=0— Scale to zero when idle (default, most economical)--min-instances=1— Keep one instance warm to avoid cold starts--max-instances— Set limits to control maximum spend
Firestore
- Use Enterprise edition for MongoDB compatibility
- Monitor read/write operations
- Implement caching where appropriate
Estimated Monthly Costs (as of early 2026)
For a minimal configuration (1 CPU, 512MB–1GB memory per service, 1 system database + 2 organization databases):
| Configuration | Estimated Monthly Cost |
|---|---|
--min-instances=0 + request-based billing | < $50 |
--min-instances=1 + request-based billing | ~$70 |
--min-instances=1 + instance-based billing | ~$180 |
These are average costs observed by the authors for a minimal setup. Your actual costs may vary based on usage patterns, region, and resource configuration. Always monitor your spending and set up budget alerts in the Google Cloud Console.
Summary
Google Cloud deployment provides:
- Serverless scaling with Cloud Run
- Managed MongoDB-compatible database with Firestore
- Secure secret management
- Automatic HTTPS and SSL certificates
- Global CDN for static assets
- CI/CD integration with GitHub Actions