FastAPI Kubernetes Deployment
SKILL.md
FastAPI Docker & Kubernetes Deployment
This skill provides production-ready patterns for containerizing FastAPI applications and deploying to Kubernetes.
Dockerfile (Multi-Stage)
Production Dockerfile
# Build stage
FROM python:3.11-slim as builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
# Production stage
FROM python:3.11-slim
WORKDIR /app
# Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
# Copy wheels and install
COPY /app/wheels /wheels
RUN pip install --no-cache /wheels/*
# Copy application
COPY ./app /app/app
# Change ownership
RUN chown -R appuser:appuser /app
USER appuser
# Health check
HEALTHCHECK \
CMD python -c "import httpx; httpx.get('http://localhost:8000/health')" || exit 1
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Development Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt requirements-dev.txt ./
RUN pip install -r requirements.txt -r requirements-dev.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
Docker Compose (Development)
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "8000:8000"
volumes:
- ./app:/app/app
environment:
- MONGODB_URL=mongodb://mongo:27017
- REDIS_URL=redis://redis:6379
- KEYCLOAK_URL=http://keycloak:8080
depends_on:
- mongo
- redis
mongo:
image: mongo:7
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
redis:
image: redis:7-alpine
ports:
- "6379:6379"
keycloak:
image: quay.io/keycloak/keycloak:23.0
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
command: start-dev
ports:
- "8080:8080"
volumes:
mongo_data:
Health Check Endpoints
from fastapi import APIRouter, Response
from typing import Dict
import asyncio
router = APIRouter(tags=["Health"])
@router.get("/health")
async def health_check() -> Dict[str, str]:
"""Basic liveness probe."""
return {"status": "healthy"}
@router.get("/health/ready")
async def readiness_check(
db: Database = Depends(get_db),
cache: Redis = Depends(get_cache)
) -> Dict[str, any]:
"""Readiness probe - checks all dependencies."""
checks = {}
# Check MongoDB
try:
await db.command("ping")
checks["mongodb"] = "ok"
except Exception as e:
checks["mongodb"] = f"error: {str(e)}"
# Check Redis
try:
await cache.ping()
checks["redis"] = "ok"
except Exception as e:
checks["redis"] = f"error: {str(e)}"
# Overall status
all_ok = all(v == "ok" for v in checks.values())
if not all_ok:
return Response(
content=json.dumps({"status": "unhealthy", "checks": checks}),
status_code=503,
media_type="application/json"
)
return {"status": "healthy", "checks": checks}
@router.get("/health/live")
async def liveness_check() -> Dict[str, str]:
"""Kubernetes liveness probe."""
return {"status": "alive"}
Kubernetes Deployment
Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
labels:
app: fastapi-app
spec:
replicas: 3
selector:
matchLabels:
app: fastapi-app
template:
metadata:
labels:
app: fastapi-app
spec:
containers:
- name: api
image: registry.example.com/fastapi-app:latest
ports:
- containerPort: 8000
env:
- name: MONGODB_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: mongodb-url
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: redis-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health/live
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
Service & Ingress
apiVersion: v1
kind: Service
metadata:
name: fastapi-app
spec:
selector:
app: fastapi-app
ports:
- port: 80
targetPort: 8000
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fastapi-app
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fastapi-app
port:
number: 80
Helm Chart Structure
fastapi-chart/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── hpa.yaml
│ └── _helpers.tpl
values.yaml
replicaCount: 3
image:
repository: registry.example.com/fastapi-app
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: api.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: api-tls
hosts:
- api.example.com
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
mongodb:
url: "" # Set via secret
redis:
url: "" # Set via secret
Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: fastapi-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: fastapi-app
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Additional Resources
Reference Files
For detailed deployment patterns:
references/helm-chart.md- Complete Helm chart templatesreferences/ci-cd.md- GitHub Actions deployment pipelinereferences/monitoring.md- Prometheus/Grafana setup
Example Files
Working examples in examples/:
examples/Dockerfile- Production Dockerfileexamples/docker-compose.yml- Development composeexamples/k8s/- Complete K8s manifests