Choorai
Cycle 3

Backend Deployment

Package a FastAPI app into a Docker container and deploy it to Google Cloud Run.

What you'll learn in this Cycle

  • Why backend deployment differs from frontend
  • Docker basics (writing a Dockerfile)
  • Cloud Run deployment
  • Health checks and logging

🎯 Framework Maturity Levels

Backend frameworks are categorized by complexity level. Choose a framework that matches your skill level and project scale.

Which framework should you choose?

Hono (Lv.1): Only need JavaScript, fastest to get started

FastAPI (Lv.2): Prefer Python, need auto documentation

Go (Lv.3): Performance/cost optimization, serverless environments

.NET (Lv.3): Enterprise/finance, C# ecosystem, strong typing

NestJS (Lv.4): Gaining real-world experience, large-scale projects

Why Backend Deployment is Different

Frontend

  • Static files (HTML/CSS/JS)
  • Just upload to a CDN
  • No server process needed

Backend

  • Requires a running server process
  • Executes code for each request
  • State management, DB connections

By packaging the backend into a container, it can run identically on any server.

Docker Basics

Writing a Dockerfile

Dockerfile
# Use Python image
FROM python:3.11-slim

# Set working directory
WORKDIR /app

# Install dependencies first (leverage caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy source code
COPY . .

# Expose port
EXPOSE 8080

# Run the app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

Test Locally

Terminal
# Build image
docker build -t my-api .

# Run container
docker run -p 8080:8080 my-api

# Check at http://localhost:8080

Multi-stage Build (Optional)

Use multi-stage builds to reduce image size.

Dockerfile (Multi-stage)
# Build stage
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Runtime stage
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 8080
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

Cloud Run Deployment

1 Install gcloud CLI

Terminal
# macOS
brew install google-cloud-sdk

# Login
gcloud auth login

# Set project
gcloud config set project YOUR_PROJECT_ID

2 Build and Push Image

Terminal
# Build with Cloud Build + push to Artifact Registry
gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/my-api

3 Deploy to Cloud Run

Terminal
gcloud run deploy my-api \
  --image gcr.io/YOUR_PROJECT_ID/my-api \
  --platform managed \
  --region asia-northeast3 \
  --allow-unauthenticated

--allow-unauthenticated: Allow access without authentication (for public APIs)

4 Set Environment Variables

Terminal
gcloud run deploy my-api \
  --image gcr.io/YOUR_PROJECT_ID/my-api \
  --set-env-vars="DATABASE_URL=postgresql://...,API_KEY=secret"

Secret Management

For sensitive information, use Secret Manager instead of environment variables.

Operations Basics

Health Check Endpoint

Cloud Run performs health checks on the / path. It is best practice to create a dedicated health check endpoint.

main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/health")
def health_check():
    return {"status": "healthy"}

@app.get("/")
def root():
    return {"message": "Hello, World!"}

Checking Logs

Terminal
# View Cloud Run logs
gcloud run services logs read my-api --region asia-northeast3

# Stream logs in real-time
gcloud run services logs tail my-api --region asia-northeast3

CORS Configuration

If your frontend and backend are on different domains, you need to configure CORS.

main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://myapp.com", "http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Next Steps

If backend deployment is complete, let's connect a database next. In Cycle 4: Database, you will learn how to connect PostgreSQL.

Last updated: February 22, 2026 · Version: v0.0.1

Send Feedback

Opens a new issue page with your message.

Open GitHub Issue