home / skills / markus41 / claude / fastapi-k8s

This skill helps you containerize FastAPI and deploy to Kubernetes with Dockerfiles, Helm charts, health checks, and autoscaling.

npx playbooks add skill markus41/claude --skill fastapi-k8s

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
8.0 KB
---
name: FastAPI Kubernetes Deployment
description: This skill should be used when the user asks to "deploy FastAPI to Kubernetes", "create Dockerfile", "build Docker image", "write Helm chart", "configure K8s deployment", "add health checks", "scale FastAPI", or mentions Docker, Kubernetes, K8s, containers, Helm, or deployment. Provides containerization and orchestration patterns.
version: 0.1.0
---

# FastAPI Docker & Kubernetes Deployment

This skill provides production-ready patterns for containerizing FastAPI applications and deploying to Kubernetes.

## Dockerfile (Multi-Stage)

### Production Dockerfile

```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 --from=builder /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 --interval=30s --timeout=10s --start-period=5s --retries=3 \
    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

```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)

```yaml
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

```python
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

```yaml
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

```yaml
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

```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

```yaml
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 templates
- **`references/ci-cd.md`** - GitHub Actions deployment pipeline
- **`references/monitoring.md`** - Prometheus/Grafana setup

### Example Files

Working examples in `examples/`:
- **`examples/Dockerfile`** - Production Dockerfile
- **`examples/docker-compose.yml`** - Development compose
- **`examples/k8s/`** - Complete K8s manifests

Overview

This skill provides production-ready patterns to containerize FastAPI applications and deploy them to Kubernetes. It covers multi-stage Dockerfiles for production and development, Docker Compose for local workflows, health check endpoints, Kubernetes manifests, Helm chart structure, and autoscaling. Use it to generate reproducible, secure, and observable deployments for FastAPI services.

How this skill works

It supplies a multi-stage Dockerfile that builds wheels in a builder image and produces a minimal runtime image running uvicorn as a non-root user. It includes example Compose files for local development and health endpoints (liveness, readiness) that integrate with Kubernetes probes. Kubernetes manifests, a Helm chart scaffold, and an HPA example demonstrate resource requests/limits, probes, secrets, ingress TLS, and autoscaling.

When to use it

  • When you need to containerize a FastAPI app for production or development.
  • When you want Kubernetes manifests or a Helm chart to deploy FastAPI services.
  • When you need health endpoints compatible with K8s liveness/readiness probes.
  • When adding resource requests/limits, securityContext, and non-root runtime.
  • When you want an HPA and ingress/TLS examples integrated with cert-manager.

Best practices

  • Use a multi-stage Dockerfile to separate build dependencies and keep the runtime image small and immutable.
  • Run the app as a non-root user and set readOnlyRootFilesystem where possible for safer containers.
  • Expose explicit resource requests and limits and tune HPA targets based on real metrics.
  • Implement both liveness and readiness endpoints that check dependencies (DB, cache) before marking ready.
  • Store secrets (DB/Redis URLs) in Kubernetes Secrets and reference them via envFrom or secretKeyRef in manifests.

Example use cases

  • Generate a production Dockerfile that builds wheels and produces a minimal runtime image for container registry publishing.
  • Create a Docker Compose development setup with live reload and local MongoDB/Redis for feature development.
  • Deploy a 3-replica Kubernetes Deployment with Service and Ingress using TLS via cert-manager.
  • Add liveness (/health/live) and readiness (/health/ready) endpoints and wire them into deployment probes.
  • Enable autoscaling with an HPA driven by CPU and memory utilization, with min/max replicas configured in values.yaml.

FAQ

How do I test readiness locally before pushing to K8s?

Run the app with the development Dockerfile or uvicorn locally and call the /health/ready endpoint while running mocked or actual dependencies to verify checks pass.

Where should I store credentials and connection strings?

Keep credentials in Kubernetes Secrets and reference them in deployment env via secretKeyRef. Avoid baking secrets into images or plain values.yaml files.