Deploying a FastAPI + PostgreSQL App
Using Google Cloud Run and Cloud SQL
As a associate system administrator I worked on Redhat Linux servers, including user management, permissions, services, and performance monitoring Automated routine administrative tasks using Bash scripting and cron jobs, reducing manual effort by ~30% I am aws certified sysops administrator and Google Certified Cloud Engineer. Determined to transition my career into cloud architect /Cloud Support role
This guide outlines the process of deploying a high-performance, serverless backend using FastAPI and PostgreSQL on Google Cloud Platform (GCP). By leveraging Cloud Run for containerized execution and Cloud SQL for managed database operations, you achieve a scalable architecture with minimal operational overhead.
Architecture Overview In this setup, Cloud Run scales your FastAPI containers automatically based on incoming traffic. Connection to Cloud SQL is handled via the Cloud SQL Auth Proxy, which provides secure encryption and IAM-based authorization without requiring you to manage SSL certificates manually.
Step 1: Provision the Managed Database Create Instance: Navigate to the SQL section in the Google Cloud Console and create a PostgreSQL instance.
Configuration: Define your Instance ID, set a strong password for the postgres user, and select a region (e.g., us-central1) that matches your intended Cloud Run location to minimize latency.
Database & User: Create a specific database (e.g., app_db) and a dedicated application user. Avoid using the default root user for production applications.
Enable API: Ensure the Cloud SQL Admin API is enabled in your project to allow Cloud Run to "see" the instance.
Step 2: Develop the FastAPI Application The application uses environment variables to remain portable and secure. We utilize psycopg2-binary for the database connection.
from fastapi import FastAPI, HTTPException import psycopg2 import os
app = FastAPI()
def get_db_connection(): try: # Cloud SQL Unix socket path: /cloudsql/PROJECT_ID:REGION:INSTANCE_ID conn = psycopg2.connect( dbname=os.getenv("DB_NAME"), user=os.getenv("DB_USER"), password=os.getenv("DB_PASS"), host=os.getenv("DB_HOST"), ) return conn except Exception as e: print(f"Error connecting to DB: {e}") return None
@app.get("/health") def check_health(): conn = get_db_connection() if not conn: raise HTTPException(status_code=500, detail="Database connection failed")
cur = conn.cursor()
cur.execute("SELECT NOW();")
db_time = cur.fetchone()
cur.close()
conn.close()
return {"status": "online", "server_time": db_time[0]}
Step 3: Containerization To deploy to Cloud Run, the application must be packaged into a Docker image.
Use a lightweight Python base image
FROM python:3.11-slim
Prevent Python from writing .pyc files and enable unbuffered logging
ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1
WORKDIR /app
Install system dependencies for psycopg2
RUN apt-get update && apt-get install -y libpq-dev gcc && rm -rf /var/lib/apt/lists/*
COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt
COPY . .
Cloud Run listens on port 8080 by default
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"] Step 4: Deployment Workflow
- Build and Push to Artifact Registry First, submit your code to Cloud Build to create the container image and store it in your registry.
Bash gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/fastapi-sql-app . 2. Deploy to Cloud Run When deploying, you must link the Cloud SQL instance. This creates a Unix socket at /cloudsql/INSTANCE_CONNECTION_NAME inside the container.
gcloud run deploy fastapi-sql-app
--image gcr.io/YOUR_PROJECT_ID/fastapi-sql-app
--add-cloudsql-instances=YOUR_PROJECT_ID:region:instance-name
--set-env-vars DB_NAME=app_db,DB_USER=app_user,DB_PASS=your_password,DB_HOST=/cloudsql/YOUR_PROJECT_ID:region:instance-name
--region us-central1
--allow-unauthenticated
Best Practices for Production:
Security & Secrets Hardcoding passwords in environment variables is risky. Use Google Secret Manager to store your DB_PASS. You can then mount these secrets directly into Cloud Run environment variables.
Connection Pooling:
Creating a new connection for every request is expensive. For production, switch to an Async driver like asyncpg or use a pooling library like SQLAlchemy to maintain a pool of warm connections.
Networking For maximum security:
configure your Cloud SQL instance with a Private IP and use a Serverless VPC Access connector. This ensures your database traffic never touches the public internet.
Feature Standard (Public IP) Enhanced (Private IP) Connectivity Via Cloud SQL Proxy Via VPC Connector Internet Exposure Limited by IAM None Latency Low Lowest Would you like me to help you write a Terraform script to automate this entire infrastructure setup?
Python Lambda:
Automate the deployment of a FastAPI application on Google Cloud Run with a Cloud SQL (PostgreSQL) backend, using a Python script (via the Google Cloud Python SDK) is an excellent way to ensure reproducibility.
The following script uses the google-cloud-resource-manager, google-cloud-storage, and google-cloud-run libraries to orchestrate the setup.
Prerequisites Before running the script, ensure you have:
GCP SDK installed and authenticated: gcloud auth application-default login.
Required Libraries: pip install google-api-python-client google-cloud-run google-cloud-sql google-cloud-build.
Project ID: Replace YOUR_PROJECT_ID in the script.
Infrastructure Automation Script Python import time from googleapiclient import discovery from google.cloud import run_v2 from google.cloud import developerconnect_v1
Configuration
PROJECT_ID = "YOUR_PROJECT_ID" REGION = "us-central1" INSTANCE_NAME = "fastapi-sql-instance" DB_NAME = "app_db" DB_USER = "app_user" DB_PASS = "SuperSecretPass123" # In production, use Secret Manager IMAGE_URI = f"gcr.io/{PROJECT_ID}/fastapi-sql-app:latest"
def setup_infrastructure(): # 1. Initialize Service Clients sql_client = discovery.build('sqladmin', 'v1beta4')
print(f"--- Starting Infrastructure Setup for {PROJECT_ID} ---")
# 2. Create Cloud SQL Instance
print(f"Creating Cloud SQL Instance: {INSTANCE_NAME}...")
instance_body = {
"name": INSTANCE_NAME,
"region": REGION,
"settings": {
"tier": "db-f1-micro", # Cost-effective for dev
"databaseFlags": [],
"ipConfiguration": {"ipv4Enabled": True}
},
"databaseVersion": "POSTGRES_15"
}
try:
operation = sql_client.instances().insert(
project=PROJECT_ID, body=instance_body).execute()
print("Waiting for SQL instance to provision (this may take 5-10 mins)...")
# Polling for completion (simplified for brevity)
time.sleep(30)
except Exception as e:
print(f"SQL Instance might already exist or error: {e}")
# 3. Create Database and User
print("Configuring Database and User...")
sql_client.databases().insert(
project=PROJECT_ID, instance=INSTANCE_NAME,
body={"name": DB_NAME}).execute()
sql_client.users().insert(
project=PROJECT_ID, instance=INSTANCE_NAME,
body={"name": DB_USER, "password": DB_PASS}).execute()
# 4. Deploy to Cloud Run
print(f"Deploying Cloud Run Service from {IMAGE_URI}...")
client = run_v2.ServicesClient()
# Define the connection string for the Unix Socket
instance_connection_name = f"{PROJECT_ID}:{REGION}:{INSTANCE_NAME}"
db_host = f"/cloudsql/{instance_connection_name}"
service_request = run_v2.CreateServiceRequest(
parent=f"projects/{PROJECT_ID}/locations/{REGION}",
service_id="fastapi-sql-service",
service=run_v2.Service(
template=run_v2.RevisionTemplate(
containers=[
run_v2.Container(
image=IMAGE_URI,
env=[
{"name": "DB_NAME", "value": DB_NAME},
{"name": "DB_USER", "value": DB_USER},
{"name": "DB_PASS", "value": DB_PASS},
{"name": "DB_HOST", "value": db_host},
],
)
],
annotations={
"run.googleapis.com/cloudsql-instances": instance_connection_name
}
)
)
)
operation = client.create_service(request=service_request)
response = operation.result()
print(f"--- Deployment Complete! ---")
print(f"Service URL: {response.uri}")
if name == "main": setup_infrastructure() What This Script Handles Managed PostgreSQL: It provisions a db-f1-micro instance, which is ideal for testing and lightweight applications.
Database Scoping: It creates a fresh database and a restricted user, following the principle of least privilege.
Cloud SQL Auth Proxy Integration: By adding the run.googleapis.com/cloudsql-instances annotation, it tells Cloud Run to mount the SQL socket into the container automatically.
Environment Injection: It passes the connection metadata into your FastAPI app via environment variables so psycopg2 knows where to look.
Critical Security Note In the script above, the password is a string variable. For a professional deployment:
Store the password in Google Secret Manager.
Update the Cloud Run container definition to reference the secret directly:
Python
Example of secret reference in the container env list
{"name": "DB_PASS", "value_source": {"secret_key_ref": {"secret": "DB_PASSWORD_SECRET", "version": "latest"}}}