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

Popular posts from this blog

Self-contained Raspberry Pi surveillance System Without Continue Internet

COBOT with GenAI and Federated Learning

AI in Education: Embracing Change for Future-Ready Learning