Articles Platform Engineering

High-Performance Python Microservices on Cloud Run v2 with FastAPI

🎧 Listen to Article Client-side voice reader powered by Web Speech API.
Ready to play
Speed:

High-Performance Python Microservices on Cloud Run v2 with FastAPI

Python is often viewed as slower than compiled languages like Go or Rust for microservices. However, when paired with an asynchronous ASGI framework like FastAPI, high-concurrency event loops, and proper container optimization, Python achieves enterprise-grade throughput and sub-15ms response times.


1. Multi-Stage Docker Build with Non-Root Security (UID 10001)

Cloud security begins at container build time. Running containers as root violates security baselines and exposes the host kernel to privilege escalation.

# Stage 1: Build & Dependency Wheel Cache
FROM python:3.12-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

# Stage 2: Hardened Unprivileged Runtime
FROM python:3.12-slim
WORKDIR /app

# Create unprivileged non-root user (UID 10001)
RUN groupadd -g 10001 appgroup && \
    useradd -u 10001 -g appgroup -s /sbin/nologin -d /app appuser

COPY --from=builder /root/.local /home/appuser/.local
COPY app/ app/
COPY static/ static/

RUN chown -R appuser:appgroup /app /home/appuser

ENV PATH="/home/appuser/.local/bin:${PATH}" \
    PYTHONUNBUFFERED="1" \
    PORT="8080"

USER 10001:10001
EXPOSE 8080

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080", "--workers", "1"]

2. Concurrency Tuning: 80 Concurrency per Container

Unlike Cloud Functions which handles 1 concurrent request per instance, Cloud Run defaults to 80 concurrent requests per container. Because FastAPI and Uvicorn use an asynchronous non-blocking event loop (asyncio), a single container instance easily handles 80 concurrent I/O-bound requests simultaneously without thread contention.


3. Defense-in-Depth Security Headers

Every HTTP response emitted by AustinSS Blogs includes comprehensive OWASP-recommended security headers via Starlette middleware:

response.headers["Content-Security-Policy"] = (
    "default-src 'self'; script-src 'self' 'unsafe-inline'; frame-ancestors 'none';"
)
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"