Zum Hauptinhalt springen

FastAPI Kubernetes DevSecOps Platform

FastAPI DevSecOps Platform Banner

A practical Kubernetes project that extends an existing FastAPI DevSecOps platform into a container-orchestrated deployment using k3d, Kubernetes, Traefik Ingress, PostgreSQL, Helm, and basic Kubernetes security hardening.

The goal of this project is not to build an over-engineered enterprise cluster. The goal is to show that the application can be deployed, configured, exposed, and managed in a realistic Kubernetes environment while keeping the setup understandable and reproducible.


Table of Contents


Project Overview

This repository is based on an existing FastAPI DevSecOps platform and adds a Kubernetes-based deployment layer on top of it.

The application consists of three main components:

  • Frontend served through Nginx
  • Backend built with FastAPI
  • PostgreSQL database with persistent storage

Instead of running everything only with Docker Compose, the application is deployed into a local Kubernetes cluster using k3d. The project includes Kubernetes manifests and a Helm chart to make the deployment reusable and easier to manage.

The final result is a local Kubernetes environment where the frontend, backend, and database run as separate workloads and communicate through Kubernetes services.


What This Project Demonstrates

This project demonstrates practical knowledge of:

  • Containerized application deployment
  • Kubernetes workloads and networking
  • Local cluster management with k3d
  • Service discovery inside Kubernetes
  • Ingress routing with Traefik
  • ConfigMaps and Secrets
  • Persistent storage for PostgreSQL
  • Helm chart packaging
  • Basic security hardening for Kubernetes workloads
  • Health checks with readiness and liveness probes
  • Resource requests and limits

What Is Kubernetes?

Kubernetes is a system for managing containers.

With Docker Compose, containers are usually started directly on one machine:

Docker Compose
├── frontend
├── backend
└── postgres

This works well for local development. However, Docker Compose is limited when an application needs to scale, recover from failures, or run across multiple machines.

Kubernetes solves this by acting as a container orchestrator.

That means Kubernetes can:

  • Start containers
  • Restart failed containers
  • Expose applications through services
  • Store configuration separately from application code
  • Manage secrets
  • Scale workloads
  • Route traffic
  • Attach persistent storage
  • Monitor if applications are healthy

A simple way to understand Kubernetes:

Docker runs containers. Kubernetes manages containers.

In this project, Kubernetes is used to run the frontend, backend, and PostgreSQL database in a structured and reproducible way.


Why Kubernetes Was Added

The original project already had a strong DevSecOps foundation with containerization and security tooling. Kubernetes was added as the next infrastructure layer to make the project more realistic for modern DevOps, DevSecOps, and cloud-native environments.

The goal was to learn and demonstrate how an application can move from a Docker-based setup to a Kubernetes-based setup.

The Kubernetes setup adds:

  • Better separation between application components
  • Internal networking through Kubernetes services
  • Config and secret management
  • Persistent database storage
  • Ingress routing through Traefik
  • Health checks
  • Resource control
  • Helm-based deployment

Architecture

High-level architecture:

Browser


Traefik Ingress

├── frontend.localhost ──► frontend-service ──► frontend pod

└── api.localhost ──────► backend-service ───► backend pod


postgres-service


postgres pod


Persistent Volume

Inside the Kubernetes cluster:

Namespace: devsecops

├── Backend Deployment
│ └── FastAPI backend pod

├── Frontend Deployment
│ └── Nginx frontend pod

├── PostgreSQL Deployment
│ └── PostgreSQL pod

├── Services
│ ├── backend-service
│ ├── frontend-service
│ └── postgres-service

├── ConfigMap
│ └── non-sensitive backend configuration

├── Secret
│ └── sensitive backend values

├── PersistentVolumeClaim
│ └── PostgreSQL storage

└── Ingress
└── Traefik routing for local URLs

Tech Stack

TechnologyPurpose
FastAPIBackend API
PostgreSQLDatabase
NginxServes the frontend build
DockerBuilds application images
KubernetesContainer orchestration
k3dLocal Kubernetes cluster using Docker
k3sLightweight Kubernetes distribution used by k3d
kubectlCLI tool to interact with Kubernetes
TraefikIngress controller and reverse proxy
HelmKubernetes package manager
ConfigMapNon-sensitive configuration
SecretSensitive configuration
PersistentVolumeClaimPersistent database storage

Core Kubernetes Concepts Used

Cluster

A Kubernetes cluster is the environment where workloads run.

In this project, the cluster is created locally with k3d:

k3d cluster create devsecops-cluster \
-p "80:80@loadbalancer" \
-p "443:443@loadbalancer"

This creates a local Kubernetes cluster that runs inside Docker.


Node

A node is a machine that runs Kubernetes workloads.

In this local setup, k3d creates a Kubernetes node inside Docker.

Example:

kubectl get nodes

Namespace

A namespace is used to logically separate Kubernetes resources.

This project uses:

devsecops

All application resources are deployed into this namespace.


Pod

A pod is the smallest deployable unit in Kubernetes.

Usually, one pod contains one application container.

In this project:

backend pod -> FastAPI container
frontend pod -> Nginx container
postgres pod -> PostgreSQL container

Deployment

A Deployment tells Kubernetes how to run an application.

It defines:

  • Which image to use
  • How many replicas to run
  • Which ports to expose inside the pod
  • Health checks
  • Resource limits
  • Security settings

If a pod crashes, the Deployment ensures that Kubernetes starts a new one.


Service

Pods can change their internal IP address. A Service gives them a stable network address.

In this project:

backend-service
frontend-service
postgres-service

The backend connects to PostgreSQL through:

postgres-service

This is important because localhost inside a pod means the pod itself, not another container.


ConfigMap

A ConfigMap stores non-sensitive configuration.

Examples:

PROJECT_NAME
ENVIRONMENT
POSTGRES_SERVER
POSTGRES_PORT
BACKEND_CORS_ORIGINS

This keeps configuration separate from the container image.


Secret

A Secret stores sensitive values.

Examples:

SECRET_KEY
POSTGRES_PASSWORD
FIRST_SUPERUSER_PASSWORD
SMTP_PASSWORD

For local development, placeholder values are used. In production, these values should be replaced with strong secrets and managed securely.


PersistentVolumeClaim

A PersistentVolumeClaim is used to request storage for a pod.

PostgreSQL needs persistent storage because database data should not disappear when the pod restarts.

This project uses a PVC for PostgreSQL data.


Ingress

Ingress exposes services through HTTP routes.

Instead of using port-forwarding, Traefik routes traffic to the correct service.

This project uses:

http://frontend.localhost
http://api.localhost/docs

Helm

Helm is the package manager for Kubernetes.

Instead of applying many YAML files manually, Helm packages them into a chart.

With Helm, the whole platform can be installed or upgraded with one command.


Repository Structure

Example structure:

.
├── backend/
├── frontend/
├── k8s/
│ ├── namespace.yaml
│ ├── backend-configmap.yaml
│ ├── backend-secret.yaml
│ ├── backend-deployment.yaml
│ ├── backend-service.yaml
│ ├── frontend-deployment.yaml
│ ├── frontend-service.yaml
│ ├── postgres-deployment.yaml
│ ├── postgres-service.yaml
│ ├── postgres-pvc.yaml
│ └── ingress.yaml

├── helm/
│ └── fastapi-kubernetes-platform/
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
│ ├── namespace.yaml
│ ├── backend-configmap.yaml
│ ├── backend-secret.yaml
│ ├── backend-deployment.yaml
│ ├── backend-service.yaml
│ ├── frontend-deployment.yaml
│ ├── frontend-service.yaml
│ ├── postgres-deployment.yaml
│ ├── postgres-service.yaml
│ ├── postgres-pvc.yaml
│ └── ingress.yaml

└── README.md

Prerequisites

Required tools:

  • Docker Desktop
  • kubectl
  • k3d
  • Helm
  • Git

Check versions:

docker --version
kubectl version --client
k3d version
helm version

Install k3d with Homebrew:

brew install k3d

Install Helm with Homebrew:

brew install helm

Local Kubernetes Cluster Setup

Create a local k3d cluster:

k3d cluster create devsecops-cluster \
-p "80:80@loadbalancer" \
-p "443:443@loadbalancer"

The port mapping is important because Traefik should be reachable directly through the browser without using port-forwarding.

Check if the cluster is running:

kubectl get nodes

Expected result:

NAME STATUS ROLES VERSION
k3d-devsecops-cluster-server-0 Ready control-plane,master ...

Docker Images

The Kubernetes cluster needs container images for the backend and frontend.

Build the backend image:

docker build -t backend:latest -f backend/Dockerfile .

Build the frontend image:

docker build -t frontend:latest -f frontend/Dockerfile .

Import the images into the k3d cluster:

k3d image import backend:latest -c devsecops-cluster
k3d image import frontend:latest -c devsecops-cluster

This is needed because the cluster runs inside Docker and does not automatically know every local image from the host system.


Kubernetes Manifests

The k8s/ directory contains raw Kubernetes YAML files.

These files can be applied manually:

kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/backend-configmap.yaml
kubectl apply -f k8s/backend-secret.yaml
kubectl apply -f k8s/postgres-pvc.yaml
kubectl apply -f k8s/postgres-deployment.yaml
kubectl apply -f k8s/postgres-service.yaml
kubectl apply -f k8s/backend-deployment.yaml
kubectl apply -f k8s/backend-service.yaml
kubectl apply -f k8s/frontend-deployment.yaml
kubectl apply -f k8s/frontend-service.yaml
kubectl apply -f k8s/ingress.yaml

However, the preferred deployment method for this project is Helm.


Helm Deployment

The Helm chart is located here:

helm/fastapi-kubernetes-platform/

Validate the Chart

helm lint ./helm/fastapi-kubernetes-platform

Expected result:

1 chart(s) linted, 0 chart(s) failed

Install the Application

helm install fastapi-platform ./helm/fastapi-kubernetes-platform

Upgrade the Application

After changing templates or values:

helm upgrade fastapi-platform ./helm/fastapi-kubernetes-platform

List Helm Releases

helm list

Uninstall the Release

helm uninstall fastapi-platform

If the namespace should also be removed:

kubectl delete namespace devsecops

Ingress and Local URLs

Traefik is used as the Ingress controller.

The local URLs are:

http://frontend.localhost
http://api.localhost/docs

The Ingress routes traffic like this:

frontend.localhost -> frontend-service -> frontend pod
api.localhost -> backend-service -> backend pod

Check the Ingress:

kubectl get ingress -n devsecops

Security Hardening

This project includes basic Kubernetes security hardening.

Non-root Containers

The backend and frontend containers run as non-root users.

Backend example:

securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
allowPrivilegeEscalation: false

Frontend example:

securityContext:
runAsNonRoot: true
runAsUser: 101
runAsGroup: 101
allowPrivilegeEscalation: false

This reduces the risk if a container is compromised.

Resource Requests and Limits

Resource requests and limits are configured for backend, frontend, and PostgreSQL.

This prevents containers from consuming unlimited CPU or memory.

Example:

resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"

Readiness and Liveness Probes

Health checks are used to let Kubernetes know whether a container is ready and healthy.

  • Readiness probe: decides if the pod should receive traffic
  • Liveness probe: decides if the pod should be restarted

Backend uses HTTP probes. Frontend uses HTTP probes. PostgreSQL uses TCP probes.

ConfigMap and Secret Separation

Non-sensitive configuration is stored in a ConfigMap. Sensitive values are stored in a Secret.

This follows a cleaner DevSecOps approach than hardcoding everything inside application code.


Persistence

PostgreSQL uses a PersistentVolumeClaim.

This means the database data can survive pod restarts.

Check PVC status:

kubectl get pvc -n devsecops

Expected result:

postgres-pvc Bound

Useful Commands

Check all pods:

kubectl get pods -n devsecops

Check services:

kubectl get svc -n devsecops

Check ingress:

kubectl get ingress -n devsecops

Check Helm releases:

helm list

View backend logs:

kubectl logs -n devsecops deployment/backend

View frontend logs:

kubectl logs -n devsecops deployment/frontend

View PostgreSQL logs:

kubectl logs -n devsecops deployment/postgres

Restart a deployment:

kubectl rollout restart deployment/backend -n devsecops

Check rollout status:

kubectl rollout status deployment/backend -n devsecops

Describe a pod:

kubectl describe pod <pod-name> -n devsecops

Screenshots

Add screenshots of the running Kubernetes setup here.

Recommended screenshots:

1. Kubernetes Pods

Command:

kubectl get pods -n devsecops

Suggested image:

Kubernetes Pods

2. Kubernetes Services

Command:

kubectl get svc -n devsecops

Suggested image:

Kubernetes Services

3. Kubernetes Ingress

Command:

kubectl get ingress -n devsecops

Suggested image:

Kubernetes Ingress

4. Helm Release

Command:

helm list

Suggested image:

Helm Release

5. Frontend

URL:

http://frontend.localhost

Suggested image:

Frontend Application

6. Backend API Docs

URL:

http://api.localhost/docs

Suggested image:

FastAPI Swagger Docs


What I Learned

Through this project, I learned how Kubernetes changes the way containerized applications are deployed and managed.

The most important learning points were:

  • kubectl is the CLI used to communicate with Kubernetes
  • k3d can create a lightweight local Kubernetes cluster using Docker
  • Kubernetes does not run source code directly; it runs container images
  • Pods are the smallest running units in Kubernetes
  • Deployments keep pods running and replace failed pods automatically
  • Services provide stable internal networking
  • localhost inside a pod does not mean the host machine or another pod
  • ConfigMaps and Secrets separate configuration from application code
  • PostgreSQL needs persistent storage through a PVC
  • Ingress exposes services through clean local URLs
  • Helm packages Kubernetes resources into a reusable chart
  • Security hardening can be added through non-root users, resource limits, and health checks

The project helped me understand the difference between simply running containers and managing containerized infrastructure.


Disclaimer

This project is intended for learning and portfolio purposes.

The local secrets and placeholder passwords are used only for local development. In a real production environment, secrets must be replaced with strong values and managed using a secure secret management solution.