In today’s fast-paced digital landscape, building scalable and resilient web applications is paramount. FastAPI has emerged as a high-performance, easy-to-use web framework for Python, while Kubernetes has become the de facto standard for orchestrating containerized applications. When combined with Helm for package management and Ingress controllers for external access, you get a powerful stack capable of handling demanding workloads with grace.
This article will guide you through the process of deploying a FastAPI application to a Kubernetes cluster using Helm charts and exposing it via an Ingress controller. We’ll cover everything from containerizing your application to defining your Helm chart and finally, getting your service live.
The Power Trio: FastAPI, Kubernetes, and Helm
Before diving into the technical steps, let’s understand why this combination is so effective for modern application deployment.
FastAPI: High Performance and Developer Experience
FastAPI is a modern, fast (high-performance) web framework for building APIs with Python 3.7+ based on standard Python type hints. Its key advantages include:
- Exceptional Performance: Built on Starlette for the web parts and Pydantic for data validation, FastAPI delivers performance comparable to Node.js and Go.
- Automatic Interactive API Documentation: It automatically generates OpenAPI (formerly Swagger) and ReDoc documentation, making API consumption a breeze for front-end developers and other services.
- Developer Productivity: With features like dependency injection, type checking, and editor support, it significantly boosts developer productivity.
Kubernetes: Orchestrating Containerized Applications
Kubernetes (K8s) is an open-source system for automating deployment, scaling, and management of containerized applications. It provides:
- Scalability: Easily scale your application up or down based on demand.
- High Availability: Ensures your application remains available even if individual nodes or pods fail.
- Resource Management: Efficiently allocates resources across your cluster.
- Self-Healing: Automatically restarts failed containers, replaces and reschedules containers when nodes die.
Helm: The Kubernetes Package Manager
While Kubernetes is powerful, managing complex deployments with raw YAML files can become cumbersome. This is where Helm comes in. Helm is often referred to as the package manager for Kubernetes. It simplifies the deployment and management of applications on Kubernetes clusters by:
- Templating: Allows you to define your Kubernetes resources (Deployments, Services, Ingresses, etc.) using templates, making them configurable and reusable.
- Lifecycle Management: Provides commands to install, upgrade, rollback, and uninstall applications easily.
- Dependency Management: Helps manage dependencies between different parts of your application or other services.
Together, these tools create a robust pipeline for developing, deploying, and managing high-performance web services.

Prerequisites for Deployment
Before we begin, ensure you have the following tools installed and configured on your development machine:
- Python 3.7+ and pip: For developing your FastAPI application.
- Docker: To containerize your FastAPI application.
- kubectl: The command-line tool for interacting with Kubernetes clusters.
- Helm 3: The Kubernetes package manager.
- A Kubernetes Cluster: This can be a local cluster (Minikube, Kind) or a cloud-managed cluster (GKE, AKS, EKS). For this tutorial, we’ll assume you have access to a functional cluster.
Building Your FastAPI Application
Let’s start with a very simple FastAPI application. Create a directory named fastapi-app and inside it, create a file named main.py:
# main.py
from fastapi import FastAPI
# Initialize the FastAPI application
app = FastAPI()
# Define a root endpoint
@app.get("/")
async def read_root():
return {"message": "Hello from FastAPI on Kubernetes!"}
# Define another endpoint with a path parameter
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
Next, create a requirements.txt file to list your application’s dependencies:
# requirements.txt
fastapi==0.111.0
uvicorn==0.30.1
Containerizing FastAPI with Docker
To deploy our FastAPI application to Kubernetes, we first need to package it into a Docker image. Create a Dockerfile in the same fastapi-app directory:
# Dockerfile
# Use an official Python runtime as a parent image
FROM python:3.9-slim-buster
# Set the working directory in the container
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY ./requirements.txt /app/requirements.txt
# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copy the application code into the container
COPY . /app
# Expose the port that Uvicorn will run on
EXPOSE 80
# Command to run the application using Uvicorn
# The --host 0.0.0.0 makes the server accessible from outside the container
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
Now, build your Docker image and push it to a container registry (e.g., Docker Hub, Google Container Registry). Replace your-dockerhub-username with your actual Docker Hub username.
docker build -t your-dockerhub-username/fastapi-app:v1.0.0 .
docker push your-dockerhub-username/fastapi-app:v1.0.0
Make sure your image is publicly accessible or your Kubernetes cluster has credentials to pull from a private registry.
Crafting Your Helm Chart for FastAPI
Now that our application is containerized, let’s create a Helm chart to deploy it. In a directory outside of your fastapi-app, run:
helm create fastapi-chart
This command generates a basic chart structure. Navigate into the fastapi-chart directory. We’ll focus on modifying files in the templates/ and values.yaml files.
values.yaml: Configuration for Your Chart
The values.yaml file holds the default configuration values for your Helm chart. We’ll define parameters for our Docker image, service, and ingress.
# fastapi-chart/values.yaml
replicaCount: 1
image:
repository: your-dockerhub-username/fastapi-app # Replace with your image
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "v1.0.0" # Replace with your image tag
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use. If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 80
targetPort: 80 # The port your FastAPI app listens on inside the container
ingress:
enabled: true # Enable or disable Ingress
className: "nginx" # Specify your Ingress Controller class, e.g., nginx, traefik
annotations: # Add any specific annotations for your Ingress controller
kubernetes.io/ingress.class: nginx
# cert-manager.io/cluster-issuer: letsencrypt-prod # Example for cert-manager
hosts:
- host: fastapi.example.com # Replace with your domain
paths:
- path: /
pathType: Prefix
serviceName: fastapi-chart
servicePort: 80
tls: []
# - secretName: fastapi-tls
# hosts:
# - fastapi.example.com
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
templates/deployment.yaml: Defining Your Pods
This file defines the Kubernetes Deployment for your FastAPI application, specifying the Docker image, replicas, and container port.
# fastapi-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "fastapi-chart.fullname" . }}
labels:
{{- include "fastapi-chart.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "fastapi-chart.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "fastapi-chart.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "fastapi-chart.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 5
periodSeconds: 10
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
templates/service.yaml: Exposing Your Deployment Internally
This file defines a Kubernetes Service, which provides a stable IP address and DNS name for your FastAPI pods within the cluster.
# fastapi-chart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "fastapi-chart.fullname" . }}
labels:
{{- include "fastapi-chart.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
name: http
selector:
{{- include "fastapi-chart.selectorLabels" . | nindent 4 }}
templates/ingress.yaml: Exposing Your Service Externally
This is crucial for making your FastAPI application accessible from outside the Kubernetes cluster. It leverages an Ingress controller to route external traffic to your service.
# fastapi-chart/templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "fastapi-chart.fullname" . }}
labels:
{{- include "fastapi-chart.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- toYaml .Values.ingress.tls | nindent 4 }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "fastapi-chart.fullname" $ }}
port:
number: {{ .servicePort }}
{{- end }}
{{- end }}
{{- end }}

Deploying to Kubernetes with Helm
Once your Helm chart is ready, deploying your FastAPI application is straightforward. Ensure your values.yaml points to your correct Docker image and tag, and your desired host for Ingress.
Install Your Helm Chart
From the parent directory of fastapi-chart, run the install command:
helm install fastapi-release ./fastapi-chart
Here, fastapi-release is the name of your Helm release. Helm will now create all the Kubernetes resources defined in your chart (Deployment, Service, Ingress).
Verify the Deployment
You can check the status of your deployment using kubectl commands:
- Check Pods:
kubectl get podsYou should see your FastAPI pod(s) running.
- Check Services:
kubectl get svcYou should see a service named
fastapi-release. - Check Ingress:
kubectl get ingYou should see an Ingress resource named
fastapi-releasewith an address. Note that it might take a few moments for the Ingress controller to provision an external IP address or DNS entry.
Updating Your Deployment
If you make changes to your application code, Docker image, or Helm chart values, you can upgrade your release:
helm upgrade fastapi-release ./fastapi-chart
Uninstalling Your Application
To remove your application and all associated Kubernetes resources:
helm uninstall fastapi-release
Understanding Ingress Controllers
An Ingress controller is a specialized load balancer for Kubernetes. It manages external access to the services in a cluster, typically HTTP and HTTPS, by providing routing configured by Ingress resources. In our setup, it’s responsible for:
- Traffic Routing: Directing incoming HTTP/HTTPS traffic to the correct backend service based on hostnames and paths.
- SSL/TLS Termination: Handling encrypted connections, often in conjunction with tools like Cert-Manager.
- Load Balancing: Distributing requests across multiple pods of your FastAPI application.
Popular Ingress controllers include NGINX Ingress Controller, Traefik, and cloud provider-specific controllers like GCE Ingress for Google Kubernetes Engine. Before your Ingress resource can function, you must have an Ingress controller deployed in your cluster. If you are using a managed Kubernetes service (like GKE, AKS, EKS), an Ingress controller might be pre-installed or easily installable via their marketplace.
For a local Minikube setup, you can enable the NGINX Ingress controller with:
minikube addons enable ingress
For other clusters, you typically install it via its own Helm chart or YAML manifests. For example, installing the NGINX Ingress Controller:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx --create-namespace

Accessing Your FastAPI Application
Once your Ingress is provisioned, you’ll get an external IP address or a DNS name. You’ll need to configure your DNS provider to point the host defined in your values.yaml (e.g., fastapi.example.com) to this Ingress IP address.
After DNS propagation, open your web browser and navigate to http://fastapi.example.com/ (or whatever host you configured). You should see the message: {"message": "Hello from FastAPI on Kubernetes!"}.
You can also test the /items endpoint:
http://fastapi.example.com/items/5?q=testshould return{"item_id": 5, "q": "test"}.
Advanced Considerations and Best Practices
While our basic deployment is functional, real-world applications require more robust configurations.
Resource Limits and Requests
It’s crucial to define resource limits and requests in your Deployment to ensure your application gets the necessary resources and doesn’t monopolize the cluster:
- Requests: The minimum resources guaranteed to the container.
- Limits: The maximum resources the container can consume.
You can add these to your values.yaml under the resources key and they will be picked up by the deployment.yaml.
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 200m
memory: 256Mi
Liveness and Readiness Probes
We’ve already included basic HTTP liveness and readiness probes in our deployment.yaml. These are vital:
- Liveness Probe: Determines if your application is still running. If it fails, Kubernetes restarts the container.
- Readiness Probe: Determines if your application is ready to serve traffic. If it fails, Kubernetes stops sending traffic to the pod until it’s ready again.
For FastAPI, a simple GET / endpoint is often sufficient, but for more complex applications, you might create a dedicated /health or /ready endpoint that checks database connections or other critical dependencies.
Horizontal Pod Autoscaler (HPA)
For true scalability, consider enabling the Horizontal Pod Autoscaler (HPA). HPA automatically scales the number of pods in your deployment based on observed CPU utilization or other select metrics. You can enable it in your values.yaml:
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
This will instruct Kubernetes to maintain CPU utilization around 70% by scaling between 2 and 10 pods.
Secrets Management
Never hardcode sensitive information (like API keys, database passwords) into your Docker images or Helm charts. Use Kubernetes Secrets to store them securely and inject them as environment variables or mounted files into your pods.
CI/CD Integration
Automate your deployment process by integrating Helm into your Continuous Integration/Continuous Delivery (CI/CD) pipeline. Tools like Jenkins, GitLab CI, GitHub Actions, or Azure DevOps can build your Docker image, push it to a registry, and then use Helm to deploy or upgrade your application on Kubernetes.
Conclusion
Deploying FastAPI applications to Kubernetes with Helm and Ingress provides a robust, scalable, and manageable solution for modern web services. By containerizing your application, defining its resources with Helm charts, and exposing it reliably through an Ingress controller, you gain the benefits of Kubernetes’ orchestration power without the complexity of manual YAML management.
This setup empowers developers to focus on building great applications, knowing that the underlying infrastructure can handle scaling and resilience automatically. Experiment with the configurations, explore advanced features like HPA and secrets, and integrate this workflow into your CI/CD pipeline to unlock the full potential of your cloud-native deployments.