Complete Hands-On Guide to Deploying a FastAPI API to Production with Docker and Kubernetes
"It worked perfectly locally, but as soon as I pushed it to staging I got a 500."
If you're a backend developer, you've almost certainly lived through that painful moment. We pour countless hours into business logic, but the moment we try to drop that code into the vast ecosystem of a real production environment, deployment often feels like a black box.
Even after you've built a polished API with a modern framework like FastAPI, getting that code to handle traffic and run reliably 24/7 means climbing two big mountains: containerization and orchestration.
This guide is designed so you can fully understand Docker and Kubernetes—topics you've probably only seen in theory—by actually deploying FastAPI code you wrote yourself. From junior developers to DevOps engineers, this one post should take you through the entire deployment lifecycle.
🚀 Step 1: Containerizing Your FastAPI Application (Dockerizing)
Containerization is the process of turning your application into a self-contained package. Docker is the most powerful tool for standardizing that packaging.
1. Prepare a Basic FastAPI App
First, prepare a simple FastAPI app to deploy. Put the following code in main.py.
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
"""
ID .
"""
return {"item_id": item_id, "q": q, "message": "Successfully deployed via Docker!"}
# : when uvicorn .Then create a requirements.txt dependency file.
# requirements.txt
fastapi
uvicorn[standard]2. Principles for Writing an Optimized Dockerfile
Simply using FROM python:3.10 is not an optimized approach. In production, minimizing image size and hardening security are what matter. That's why we use a multi-stage build.
Example Dockerfile:
# --------------------------------------
# STAGE 1: Builder Stage
# --------------------------------------
FROM python:3.11-slim AS builder
WORKDIR /app
# when for requirements.txt
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
#
COPY . .
# --------------------------------------
# STAGE 2: Final Stage ( run )
# --------------------------------------
#
FROM python:3.11-slim
WORKDIR /app
# ( tool )
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /app /app
#
EXPOSE 8000
# when when run
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]3. Local Build and Test
Now run the following commands in your terminal to build the image and verify it locally.
# 1. (my-fastapi-app:latest )
docker build -t my-fastapi-app:latest .
# 2. 8080 run
docker run -d -p 8080:8000 --name fastapi-test my-fastapi-app:latest✅ Verify the test: Open http://localhost:8080/items/1?q=test in a browser or with curl. You should see FastAPI respond successfully.
🌐 Step 2: Understanding Kubernetes and Writing Manifests (Orchestration)
If Docker packages a single container, Kubernetes (K8s) is like an operating system that manages those containers at scale and automatically heals them.
To deploy an application on K8s, you need at least these three concepts:
- Deployment: Defines the desired state of your application. It maintains a declarative state such as "there should always be 3 Pods running this image."
- Service: Pod IPs keep changing. A Service gives those changing Pods a stable network address (Cluster IP).
- Ingress: Acts as the gateway for external (internet) traffic into the cluster. (For example, routing
api.mycompany.com/itemsto a specific Service.)
💡 Example K8s Manifest YAML
For the actual deployment, write deployment.yaml and service.yaml. (Here my-fastapi-app:latest is the image name from Step 1.)
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-deployment
labels:
app: fastapi
spec:
replicas: 3 # 3 Pod settings
selector:
matchLabels:
app: fastapi
template:
metadata:
labels:
app: fastapi
spec:
containers:
- name: fastapi-container
image: your-docker-registry/my-fastapi-app:latest # path required
ports:
- containerPort: 8000
# settings example
env:
- name: ENVIRONMENT
value: productionservice.yaml:
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
selector:
app: fastapi # Deployment
ports:
- protocol: TCP
port: 80 #
targetPort: 8000 #
type: ClusterIP #🛠️ Step 3: Deploy to a Real Cluster and Verify (Hands-On)
Now it's time to apply the prepared manifests to the cluster.
1. Deployment Commands
After connecting to the cluster, run the following commands in order.
# 1. Deployment Service
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# 2. check (Pod Running whento )
kubectl get pods -l app=fastapi💡 Troubleshooting: Pod Stuck in Pending?
If a Pod is stuck in Pending, inspect the details with:
kubectl describe pod <pod-name>
🚀 Exposing the Service (Ingress / Changing Service Type)
To make it reachable from outside, set type: LoadBalancer on the Service, or—in real production—use an Ingress Controller, which is the standard approach.
📚 Summary and Key Takeaways
| Step | Tool / Concept | Purpose | Key Commands |
|---|---|---|---|
| Containerization | Dockerfile | Package the app into an isolated environment | docker build, docker push |
| Orchestration | Kubernetes (K8s) | Automate container deployment, scaling, and management | kubectl apply -f, kubectl get pods |
| Service exposure | Service / Ingress | Route external traffic to internal containers | kubectl expose, Ingress Controller |
By going through this process, you move beyond just writing code and fully experience the engineering cycle of deploying and operating a service in production.
References: Official Docs
The primary source for the behavior, configuration, and errors covered in this post is the official documentation below. Check it for version-specific options and exact behavior.
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.