Simple FastAPI App with Docker and Minikube
Let's start with the simplest one. Which we can develop and test in our local system or laptop, or Mac.
✅ Simple FastAPI App with Docker and Minikube (Kubernetes)
๐ Folder Structure
fastapi-k8s-demo/
├── app/
│ └── main.py
├── Dockerfile
├── requirements.txt
├── k8s/
│ ├── deployment.yaml
│ └── service.yaml
๐ app/main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello from FastAPI on Kubernetes!"}
๐ requirements.txt
fastapi
uvicorn
๐ Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
๐ k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-deployment
spec:
replicas: 1
selector:
matchLabels:
app: fastapi
template:
metadata:
labels:
app: fastapi
spec:
containers:
- name: fastapi
image: fastapi-demo:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
๐ k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
type: NodePort
selector:
app: fastapi
ports:
- port: 8000
targetPort: 8000
nodePort: 30036
๐งช Run Locally with Minikube
# Start minikube
minikube start
# Build docker image inside minikube
eval $(minikube docker-env)
docker build -t fastapi-demo .
# Apply Kubernetes configs
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
# Access app in browser
minikube service fastapi-service
✅ FastAPI on Minikube with Ingress, Live Reload, and Persistent Volume
๐ Updated Structure
fastapi-k8s-demo/
├── app/
│ └── main.py
├── Dockerfile
├── requirements.txt
├── k8s/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ └── pv-claim.yaml
๐ Dockerfile (with live reload)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
๐ requirements.txt
fastapi
uvicorn[standard]
๐ k8s/deployment.yaml (with volume)
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-deployment
spec:
replicas: 1
selector:
matchLabels:
app: fastapi
template:
metadata:
labels:
app: fastapi
spec:
containers:
- name: fastapi
image: fastapi-demo:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
volumeMounts:
- mountPath: /app
name: app-volume
volumes:
- name: app-volume
persistentVolumeClaim:
claimName: fastapi-pvc
๐ k8s/pv-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fastapi-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
๐ k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
selector:
app: fastapi
ports:
- protocol: TCP
port: 80
targetPort: 8000
๐ k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fastapi-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: fastapi.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fastapi-service
port:
number: 80
๐ง Enable Ingress and Mount App
minikube start --addons=ingress
# Use Minikube's Docker
eval $(minikube docker-env)
docker build -t fastapi-demo .
# Apply all configs
kubectl apply -f k8s/pv-claim.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
# Add /etc/hosts entry (Linux/macOS)
echo "$(minikube ip) fastapi.local" | sudo tee -a /etc/hosts
# Open in browser
http://fastapi.local/
✅ FastAPI + PostgreSQL on Minikube with Secrets & ConfigMaps
๐ Project Structure
fastapi-k8s-demo/
├── app/
│ └── main.py
├── Dockerfile
├── requirements.txt
├── k8s/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── postgres-deployment.yaml
│ ├── postgres-service.yaml
│ ├── secret.yaml
│ └── configmap.yaml
๐ requirements.txt
fastapi
uvicorn[standard]
psycopg2-binary
๐ app/main.py
import os
import psycopg2
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"message": "Hello from FastAPI + Postgres!"}
@app.get("/db-check")
def db_check():
conn = psycopg2.connect(
host=os.getenv("DB_HOST"),
dbname=os.getenv("DB_NAME"),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASSWORD"),
)
cur = conn.cursor()
cur.execute("SELECT version();")
version = cur.fetchone()
conn.close()
return {"postgres_version": version}
๐ Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
๐ k8s/postgres-deployment.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
selector:
matchLabels:
app: postgres
replicas: 1
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:13
env:
- name: POSTGRES_DB
valueFrom:
configMapKeyRef:
name: pg-config
key: POSTGRES_DB
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: pg-secret
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: pg-secret
key: POSTGRES_PASSWORD
ports:
- containerPort: 5432
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: pgdata
volumes:
- name: pgdata
persistentVolumeClaim:
claimName: postgres-pvc
๐ k8s/postgres-service.yaml
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
ports:
- port: 5432
selector:
app: postgres
๐ k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: pg-secret
type: Opaque
stringData:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
๐ k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: pg-config
data:
POSTGRES_DB: mydb
๐ k8s/deployment.yaml (FastAPI updated)
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-deployment
spec:
replicas: 1
selector:
matchLabels:
app: fastapi
template:
metadata:
labels:
app: fastapi
spec:
containers:
- name: fastapi
image: fastapi-demo:latest
ports:
- containerPort: 8000
env:
- name: DB_HOST
value: postgres
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: pg-config
key: POSTGRES_DB
- name: DB_USER
valueFrom:
secretKeyRef:
name: pg-secret
key: POSTGRES_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: pg-secret
key: POSTGRES_PASSWORD
๐ k8s/service.yaml (FastAPI)
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
selector:
app: fastapi
ports:
- protocol: TCP
port: 80
targetPort: 8000
๐ k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fastapi-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: fastapi.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fastapi-service
port:
number: 80
๐ง Deployment Commands
minikube start --addons ingress
eval $(minikube docker-env)
# Build image
docker build -t fastapi-demo .
# Apply K8s resources
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/postgres-deployment.yaml
kubectl apply -f k8s/postgres-service.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
# Add to /etc/hosts
echo "$(minikube ip) fastapi.local" | sudo tee -a /etc/hosts
# Open
http://fastapi.local/db-check
Hope now you have a solid understanding of how to develop a Python FastAPI application in Docker and deploy it in Kubernetes clusters.
The whole application code is here with different branches https://github.com/dhirajpatra/fastapi-k8s-demo
Comments