DEV Community

Python-T Point
Python-T Point

Posted on • Originally published at pythontpoint.in

⚙️ Exposing FastAPI with NGINX Ingress on Kubernetes — a key tutorial

🚀 Architecture Overview — Why It Matters

fastapi nginx ingress kubernetes tutorial

A microservice behind an NGINX Ingress on Kubernetes can handle thousands of requests per second. A mis‑configured service, however, can add tens of milliseconds per hop, translating into hundreds of dollars of extra cloud spend each month. This overview maps the request flow from the client to a FastAPI pod and pinpoints where latency and cost are introduced.

📑 Table of Contents

  • 🚀 Architecture Overview — Why It Matters
  • 🔧 Ingress Controller Mechanics
  • 📦 Service and Endpoint Resolution
  • ⚙️ Containerizing FastAPI — How to Build
  • 🔍 Image Size Impact
  • 🌐 NGINX Ingress Configuration — What the Ingress Looks Like
  • 🔐 TLS Termination Details
  • 📈 Health Check Integration
  • 🟩 Final Thoughts
  • ❓ Frequently Asked Questions
  • How do I expose multiple FastAPI versions under the same domain?
  • Can I use a custom NGINX template with the Ingress controller?
  • What is the best way to handle large file uploads in FastAPI behind NGINX?
  • 📚 References & Further Reading

⚙️ Containerizing FastAPI — How to Build

A production‑ready FastAPI image must expose a port, run under a non‑root user, and stay small enough to keep pull latency low. The Dockerfile below satisfies those constraints; each instruction’s impact on the final image is noted. (Also read: 🧠 Building a semantic search with Pinecone and FastAPI — the right way)

# syntax=docker/dockerfile:1
FROM python:3.12-slim AS builder
WORKDIR /app
# Install build dependencies only for compilation
RUN apt-get update && \ apt-get install -y -no-install-recommends gcc && \ rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install -no-cache-dir -r requirements.txt FROM python:3.12-slim
WORKDIR /app
COPY -from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY . .
# Create a non‑root user
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Enter fullscreen mode Exit fullscreen mode

During the builder stage, gcc compiles any native wheels (e.g., uvloop) and is then discarded, keeping the final layer lightweight. The EXPOSE 8000 directive signals Kubernetes that the container listens on port 8000, which the pod manifest maps to the Service’s targetPort.

🔍 Image Size Impact

The two‑stage build reduces the final image from roughly 350 MB (including build tools) to about 120 MB. Smaller images lower node provisioning time because the runtime pulls less data. In a 30‑node cluster, a 200 MB reduction per node saves ~6 GB of bandwidth and cuts pod startup time by several seconds.


🌐 NGINX Ingress Configuration — What the Ingress Looks Like

The manifest below routes traffic to the FastAPI Service, enforces TLS, and configures path‑based routing. NGINX generates a location block that proxies to the Service’s ClusterIP while preserving the original host header for correct CORS handling. (More onPythonTPoint tutorials)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: name: fastapi-ingress namespace: prod annotations: nginx.ingress.kubernetes.io/rewrite-target: / nginx.ingress.kubernetes.io/proxy-body-size: "10m" nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec: ingressClassName: nginx tls: - hosts: - api.example.com secretName: fastapi-tls rules: - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: fastapi-svc port: number: 80
Enter fullscreen mode Exit fullscreen mode

NGINX parses this resource into a server block for api.example.com and a location / that proxies to the Service IP. The proxy-body-size annotation raises the allowed request payload, which is required for endpoints that accept JSON bodies larger than the default 1 MiB. (Also read: 🔐 Kubernetes RBAC Roles Tutorial — Secure Your Cluster Access the Right Way)

🔐 TLS Termination Details

NGINX terminates TLS using the secret referenced by secretName. The secret stores a PEM‑encoded certificate and private key. Because TLS ends at the ingress layer, the FastAPI pod receives plain HTTP, reducing CPU overhead inside the pod. (Also read: 🚀 Deploy Flask App AWS Free Tier — Easy EC2 & Nginx Setup)

$ kubectl get secret fastapi-tls -n prod -o yaml
apiVersion: v1
data: tls.crt: LS0tLS1CRUdJTiBDRV... tls.key: LS0tLS1CRUdJTiBSU0...
kind: Secret
type: kubernetes.io/tls
Enter fullscreen mode Exit fullscreen mode

📈 Health Check Integration

NGINX also exposes a /healthz endpoint that Kubernetes uses for readiness and liveness probes. The probe contacts the ingress, which forwards the request to the FastAPI pod's /health route. This indirect check validates both network path and application health.

readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 5 periodSeconds: 10
livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 30 periodSeconds: 30
Enter fullscreen mode Exit fullscreen mode

Properly layered ingress configuration eliminates latency spikes and hidden cloud costs.


🟩 Final Thoughts

Deploying FastAPI behind an NGINX Ingress on Kubernetes separates concerns cleanly: the ingress handles TLS, routing, and load balancing; the application container focuses exclusively on request processing. By tracing the data path—from the client’s TCP handshake through iptables DNAT to the pod’s Python runtime—inefficiencies become visible before they affect cost or performance.

Treat the Ingress manifest as part of the application code base. Store it in version control, validate it in a staging cluster, and iterate on annotations that affect payload size, timeout, or header forwarding. Automation reduces the risk of manual edits that introduce latency or security gaps.

❓ Frequently Asked Questions

How do I expose multiple FastAPI versions under the same domain?

Define separate Ingress rules with distinct path prefixes (e.g., /v1 and /v2) that each point to a different Service. NGINX routes based on the longest matching prefix, allowing versioned APIs to coexist.

Can I use a custom NGINX template with the Ingress controller?

Yes. Set the annotation nginx.ingress.kubernetes.io/template to reference a ConfigMap containing your custom nginx.conf. The controller merges the template with generated location blocks, letting you add modules or adjust buffer sizes.

What is the best way to handle large file uploads in FastAPI behind NGINX?

Increase the proxy-body-size annotation on the Ingress to match the maximum expected upload size. Additionally, configure FastAPI’s File and UploadFile parameters to stream directly to disk, preventing memory pressure inside the pod.

📚 References & Further Reading

  • Official FastAPI documentation — guides on async endpoints and deployment patterns: fastapi.tiangolo.com
  • NGINX Ingress Controller on Kubernetes — detailed guide on annotations and custom configurations: kubernetes.io
  • Docker best practices for Python images — recommendations for multi‑stage builds and security: docs.docker.com

Top comments (0)