Deploy and Scale a Flask App on Kubernetes
Deploy a Flask app to Kubernetes, scale replicas, and do a rolling update.
Introduction
30 Second Summary
Running a single Docker container works great for development. But what happens when you need three copies of your app, automatic recovery from crashes, and updates that don't take your site offline?
In this project, you will deploy a Flask application to a local Kubernetes cluster running inside Docker Desktop. You'll scale it up, then perform a rolling update that swaps your app to a new version with zero downtime.
What You'll Build
A Flask web application deployed to a local Kubernetes cluster, exposed to your browser via a NodePort Service, and scaled across multiple replicas.
By the end of this project, you'll have:
- A containerized Flask app built as a Docker image and deployed to Kubernetes using a YAML manifest.
- A Kubernetes Service that exposes your app to your browser on a specific port.
- A scaled deployment with multiple running pods and automatic load balancing, plus a completed rolling update from v1 to v2.
- Secret Mission: Add a Redis backend and configure inter-service communication using Kubernetes DNS.
Are there any prerequisites?
This project assumes basic Docker knowledge (building images, running containers) and familiarity with Flask as a Python web framework. You should also have Docker Desktop installed on Windows.
Enable Kubernetes and Verify Your Cluster
You already know how to package an app into a Docker container. Kubernetes takes that further by orchestrating multiple containers, automatically restarting failed ones, and distributing traffic across replicas.
In this step, you will enable Docker Desktop's built-in Kubernetes cluster and verify that kubectl (the command-line tool for talking to Kubernetes) is ready to go.
In this step, get ready to:
- Enable Kubernetes in Docker Desktop using the Kubeadm cluster type.
- Verify kubectl connects to your cluster.
- Create the project folder for all your files.
Enable Kubernetes in Docker Desktop
Docker Desktop ships with a built-in Kubernetes cluster you can spin up in a few clicks. No extra downloads or virtual machines required.
- Press the Windows key to open your search bar.
- Type Docker Desktop and press Enter to open it.
- In the Docker Desktop Dashboard, select the Kubernetes view from the left sidebar.
- Select Create cluster.
- Choose Kubeadm as the cluster type.
- Select Create and wait about 1 minute for the cluster to provision.
- Confirm the Kubernetes status shows as running in the Docker Desktop Dashboard footer.
Why Kubeadm over Kind?
Docker Desktop offers two cluster types. Kubeadm creates a single-node cluster that shares Docker Desktop's image store. This means locally-built Docker images are available to Kubernetes immediately without extra steps.
Kind uses a separate containerd image store, which means locally-built images need additional handling. Kubeadm is simpler for projects where you build images on the same machine.
What is Kubeadm doing behind the scenes?
Kubeadm provisions a single-node Kubernetes cluster running inside Docker Desktop. It also installs kubectl automatically, which is the command-line tool you will use to manage your cluster throughout this project.
Verify kubectl is working
Now that Kubernetes is running, let's confirm kubectl can communicate with your cluster.
- Press the Windows key to open your search bar.
- Type PowerShell and press Enter to open it.
- Check that kubectl is installed by running this command:
kubectl version
You should see version information for both the client and the server. If both display successfully, kubectl is connected to your cluster.
- Verify your cluster node is healthy by running:
kubectl get nodes
✔️ I see a node with Ready status
Your cluster is running and kubectl is connected. You are ready to move on.
ⓧ I see an error or no nodes appear
kubectl might be pointing to a different cluster context. Run this command to switch to the Docker Desktop context:
kubectl config use-context docker-desktop
- Run kubectl get nodes again to confirm you see a node with Ready status.
Still seeing errors?
Make sure the Kubernetes status in Docker Desktop Dashboard shows as running. If it shows as starting or stopped, wait for it to finish provisioning or restart it from the Kubernetes view.
Help me troubleshoot why kubectl cannot connect to my Docker Desktop Kubernetes cluster.
Create the project folder
All the files you create in this project will live in a single folder. Let's create it on your Desktop so it is easy to find.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir flask-k8s
cd flask-k8s
- Confirm the folder exists by running:
ls
You should see an empty directory listing. This confirms you are inside the flask-k8s folder and ready to start creating files.
Don't see the folder?
Make sure you ran cd ~/Desktop before mkdir flask-k8s. If you accidentally created the folder elsewhere, delete it and start again from the Desktop.
Help me find or create my flask-k8s project folder on my Desktop using PowerShell.
Your cluster is live and your project folder is ready. Next up, you will build the Flask app that Kubernetes will orchestrate.
Build Your Flask App Image
Your Kubernetes cluster is running and ready to orchestrate containers. Before deploying anything, you need a container image to deploy.
In this step, you'll create a simple Flask web app, write a Dockerfile to package it, and build a Docker image. This image is what Kubernetes will run inside pods.
In this step, get ready to:
- Create the Flask application files.
- Write a Dockerfile to containerize the app.
- Build and verify the Docker image.
Create the Flask application
Your Flask app needs two routes: a home route that identifies which pod is serving the request, and a health check route that Kubernetes can use to monitor the container.
- Create a new file called app.py inside your flask-k8s folder using any text editor, then paste in the following code:
from flask import Flask
import socket
app = Flask(__name__)
@app.route("/")
def home():
# Return a version-tagged response with the container hostname
hostname = socket.gethostname()
return f"Hello from Flask on Kubernetes! (v1) - Pod: {hostname}"
@app.route("/health")
def health():
# Simple health check endpoint for Kubernetes
return "OK", 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
What does this code do?
- socket.gethostname() returns the container's hostname. In Kubernetes, this is the pod name, making it easy to confirm which pod served each request.
- The /health route returns a simple OK response. Kubernetes can use this to check if your app is alive.
- host="0.0.0.0" tells Flask to listen on all network interfaces, which is required for traffic to reach the app inside a container.
- Create a second file called requirements.txt in the same flask-k8s folder with this content:
flask==3.1.3
Why pin the version?
Pinning Flask to 3.1.3 ensures your image builds consistently every time. Without a pin, pip might install a newer version that introduces breaking changes.
- Save both files.
- Confirm the files exist by running this in your PowerShell window:
ls
You should see app.py and requirements.txt listed in the output.
Don't see both files?
- Make sure you saved both files inside the flask-k8s folder, not in a parent directory.
- Check that you didn't accidentally add a .txt extension to app.py (some Windows editors add this by default).
- If your PowerShell isn't inside the flask-k8s folder, navigate there first with cd flask-k8s.
Why aren't my Flask app files showing up?
✔️ Awesome, I've got everything!
Great. Both files are ready for the next substep where you'll write the Dockerfile.
ⓧ I'd like to double check the full code
from flask import Flask
import socket
app = Flask(__name__)
@app.route("/")
def home():
hostname = socket.gethostname()
return f"Hello from Flask on Kubernetes! (v1) - Pod: {hostname}"
@app.route("/health")
def health():
return "OK", 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
flask==3.1.3
Create the Dockerfile
A Dockerfile tells Docker how to package your Flask app into a runnable image. It defines the base environment, installs dependencies, and specifies how to start the app.
- Create a file called Dockerfile (no extension) in your flask-k8s folder and paste the following:
# Start from a lightweight Python image
FROM python:3.11-slim
# Set the working directory inside the container
WORKDIR /app
# Install dependencies first (Docker caches this layer)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy the application code
COPY app.py .
# Document the port the app listens on
EXPOSE 5000
# Start the Flask app when the container runs
CMD ["python", "app.py"]
What does each layer do?
- FROM python:3.11-slim starts from a lightweight Python image that keeps your final image small.
- COPY requirements.txt then RUN pip install installs dependencies in a separate layer. Docker caches this layer, so rebuilds are fast unless requirements change.
- COPY app.py . adds your application code after dependencies are installed.
- EXPOSE 5000 documents which port the container listens on.
- CMD ["python", "app.py"] tells Docker how to start the app when the container runs.
- Save Dockerfile.
- Confirm it exists by running:
ls
You should now see app.py, Dockerfile, and requirements.txt in the output.
Don't see the Dockerfile?
- On Windows, some editors add a hidden .txt extension. Make sure the file is named exactly Dockerfile with no extension.
- In File Explorer, enable View > File name extensions to see hidden extensions and rename if needed.
Why isn't my Dockerfile showing up correctly?
✔️ Awesome, I've got everything!
Great. Your Dockerfile is ready to build.
ⓧ I'd like to double check the full code
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
Build the Docker image
With all three files in place, you can now build the Docker image. The docker build command reads your Dockerfile and produces a tagged image that Kubernetes can run.
- Build the image and tag it as version 1 by running this command:
docker build -t flask-k8s:v1 .
What do the flags mean?
- -t flask-k8s:v1 names the image flask-k8s and tags it v1. You'll reference this exact name in your Kubernetes manifest later.
- . tells Docker to use the current directory as the build context. This is where it looks for the Dockerfile and any files to copy.
- Verify the image was created by running:
docker images flask-k8s
You should see one entry with the repository flask-k8s, tag v1, and a size around 130-150 MB.
Build failed?
- If you see an error about failing to read the Dockerfile, make sure the file is named exactly Dockerfile (capital D, no extension) and you're running the command from inside the flask-k8s folder.
- If pip install fails inside the build, check that requirements.txt contains exactly flask==3.1.3 with no typos or extra spaces.
- If Docker itself isn't running, open Docker Desktop and wait for the engine to start before retrying.
Why is my docker build command failing?
Your Flask app is now packaged as a Docker image, ready for Kubernetes. Next up, you'll write a Deployment manifest to tell Kubernetes exactly how to run it.
Deploy to Kubernetes
Your Flask app is packaged as a Docker image, but it is just data sitting in your local image store.
Kubernetes needs explicit instructions to run your containers. You provide those instructions through a YAML manifest called a Deployment. In this step, you will write a Deployment manifest and use it to launch your Flask app on the cluster.
In this step, get ready to:
- Write a Kubernetes Deployment manifest for your Flask app.
- Apply the manifest to deploy your app onto the cluster.
- Inspect the running pod to confirm your app started successfully.
Write the Deployment manifest
A Deployment tells Kubernetes which container image to run, how many copies (replicas) to keep alive, and how to label the resulting pods.
- Create a new file called deployment.yaml inside your flask-k8s folder by running this command:
New-Item deployment.yaml
- Confirm the file exists by running:
ls
You should see deployment.yaml listed alongside app.py, requirements.txt, and Dockerfile.
- Open deployment.yaml in Notepad by running:
notepad deployment.yaml
- Paste the following content into the file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
labels:
app: flask-app
spec:
replicas: 1
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
spec:
containers:
- name: flask-app
image: flask-k8s:v1
ports:
- containerPort: 5000
imagePullPolicy: Never
What does this manifest do?
- apiVersion: apps/v1 and kind: Deployment tell Kubernetes you are creating a Deployment resource.
- replicas: 1 means Kubernetes will keep exactly one copy of your pod running at all times.
- selector and template.metadata.labels use the label app: flask-app to link the Deployment to its pods.
- image: flask-k8s:v1 points to the Docker image you built in the previous step.
- imagePullPolicy: Never tells Kubernetes to use the locally-built image instead of trying to pull it from a remote registry.
- Save and close deployment.yaml in Notepad.
File looks empty or Notepad did not open?
- Make sure you are still inside the flask-k8s directory. Run pwd to check your current location.
- If Notepad opened a different file, close it and re-run notepad deployment.yaml from PowerShell.
My deployment.yaml is empty or won't save properly.
✔️ Awesome, I've got everything!
Great. Double check you have saved deployment.yaml and it matches the YAML above.
ⓧ I'd like to double check the full code
Here is the complete deployment.yaml file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
labels:
app: flask-app
spec:
replicas: 1
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
spec:
containers:
- name: flask-app
image: flask-k8s:v1
ports:
- containerPort: 5000
imagePullPolicy: Never
Apply the Deployment
With the manifest written, you can hand it to Kubernetes. The kubectl apply command reads a YAML file and creates (or updates) the resources it defines.
- Create the Deployment on your cluster by running:
kubectl apply -f deployment.yaml
What does this command do?
kubectl apply -f deployment.yaml sends your manifest to the Kubernetes API server. The server creates a Deployment controller, which in turn creates a Pod running your flask-k8s:v1 container.
- Verify the Deployment is running by checking its status:
kubectl get deployments
You should see output similar to this, with 1/1 under the READY column:
NAME READY UP-TO-DATE AVAILABLE AGE
flask-app 1/1 1 1 10s
- View the individual pod that the Deployment created:
kubectl get pods
You should see one pod with a name starting with flask-app- and a STATUS of Running.
Pod status shows ImagePullBackOff or ErrImageNeverPull?
- This means Kubernetes cannot find the flask-k8s:v1 image locally. Confirm you built the image in the previous step by running docker images flask-k8s.
- Make sure imagePullPolicy: Never is set in your deployment.yaml. Without it, Kubernetes tries to pull from Docker Hub and fails.
- If you rebuilt the image after enabling Kubernetes, confirm the cluster type is kubeadm (which shares the Docker image store).
My pod is stuck in ImagePullBackOff. Help me fix it.
Inspect your running pod
Your app is running inside a pod, but you cannot see it in a browser yet. Before exposing it, take a moment to inspect what Kubernetes created behind the scenes.
- Copy the full pod name from the kubectl get pods output (it looks like flask-app-xxxxxxxxx-xxxxx).
- View detailed information about the pod by running:
kubectl describe pod [[POD_NAME="your pod name from kubectl get pods"]]
What does kubectl describe show?
The output includes the pod's IP address, the container image it is running, its current status, and an Events section at the bottom.
The Events section shows the lifecycle of your pod. You should see Normal events for Scheduled, Pulled, Created, and Started. These confirm Kubernetes successfully placed your pod on the node and started the container.
- View the Flask application logs by running:
kubectl logs [[POD_NAME="your pod name from kubectl get pods"]]
You should see Flask's startup output confirming the app is serving on port 5000.
Why check logs?
kubectl logs shows the standard output from your container. This is equivalent to what you would see if you ran python app.py directly. If the app crashes, the logs are the first place to look.
Seeing 'error: no such pod' or no logs?
- Double check you copied the full pod name exactly. Run kubectl get pods again to confirm the name.
- If the pod restarted, it may have a different name. Use the latest name from kubectl get pods.
My kubectl logs command is not returning output. Help me debug.
Your Flask app is alive inside the cluster. Next up, you will expose it to your browser with a Kubernetes Service so you can actually visit it.
Expose Your App with a Service
Your Flask app is running inside the Kubernetes cluster, but you have no way to reach it from your browser. Pods are isolated by default. They have internal IP addresses that only other pods inside the cluster can talk to.
To make your app accessible from the outside world, you need a Service. A Service is a stable networking endpoint that routes incoming traffic to the right pods. In this step, you will create a NodePort Service that maps a port on your machine directly to your Flask pods.
In this step, get ready to:
- Write a NodePort Service manifest that exposes your app on port 30080.
- Apply the Service and visit your running app in the browser.
- Explore how Kubernetes networking connects Services to pods automatically.
Write the Service manifest
Just like the Deployment, a Service is defined as a YAML manifest file. The manifest tells Kubernetes what type of Service to create and how to route traffic to your pods.
- Create a new file called service.yaml inside your flask-k8s folder by running this command:
New-Item service.yaml
- Open service.yaml in your text editor and paste the following content:
apiVersion: v1
kind: Service
metadata:
name: flask-service
spec:
type: NodePort
selector:
app: flask-app
ports:
- port: 5000
targetPort: 5000
nodePort: 30080
What does this manifest do?
- type: NodePort exposes the Service on a static port on each node in your cluster. This makes your app reachable from outside the cluster.
- selector: app: flask-app tells the Service to route traffic to any pod that has the label app: flask-app. This is how the Service finds your pods.
- port: 5000 is the port the Service listens on inside the cluster. targetPort: 5000 is the port your Flask container is listening on.
- nodePort: 30080 is the external port on your machine. You will use this to access the app from your browser.
- Save service.yaml.
- Confirm the file exists by running:
ls
You should see service.yaml listed alongside app.py, requirements.txt, Dockerfile, and deployment.yaml.
Don't see service.yaml?
Make sure your PowerShell is inside the flask-k8s folder. Run pwd to check your current directory.
If you created the file but it's empty, open it in your editor and confirm you pasted the YAML content and saved.
Help me find my service.yaml file
✔️ Awesome, I've got everything!
Great. Make sure you have saved service.yaml before moving on.
ⓧ I'd like to double check the full code
Here is the complete service.yaml for comparison:
apiVersion: v1
kind: Service
metadata:
name: flask-service
spec:
type: NodePort
selector:
app: flask-app
ports:
- port: 5000
targetPort: 5000
nodePort: 30080
Apply the Service and access your app
With the manifest written, you can now tell Kubernetes to create the Service. Once it is live, your Flask app becomes reachable in the browser.
- Apply the Service to your cluster by running this command:
kubectl apply -f service.yaml
What does this command do?
kubectl apply -f service.yaml sends your manifest to the Kubernetes API server. Kubernetes reads the definition and creates a Service resource that starts routing traffic to pods matching your label selector.
- Verify the Service was created by running:
kubectl get services
You should see flask-service listed with type NodePort and a PORT(S) column showing 5000:30080/TCP.
- Open your browser and navigate to:
http://localhost:30080
You should see your Flask app's response: Hello from Flask on Kubernetes! (v1) - Pod: followed by a pod name.
Browser showing an error or refusing to connect?
Make sure you used port 30080 in the URL, not 5000. Port 5000 is internal to the cluster.
Run kubectl get pods to confirm your pod shows Running status. If the pod is in CrashLoopBackOff, the app crashed. Check logs with kubectl logs followed by the pod name.
Help me troubleshoot why my Flask app isn't responding on localhost:30080
Understand the networking
Your app is running and reachable. Now take a closer look at how the Service discovers and connects to your pods. This is fundamental to understanding how Kubernetes handles networking.
- Inspect the Service details by running:
kubectl describe service flask-service
What should I see?
Look for the Selector field showing app=flask-app. This is the label the Service uses to find pods.
The Endpoints field shows the internal IP addresses of all pods currently receiving traffic from this Service. Right now you should see one IP (your single pod).
Kubernetes does not hardcode which pod a Service points to. It constantly watches for pods matching the selector. If a pod disappears, Kubernetes removes it from the Endpoints list. If a new pod appears with the right label, Kubernetes adds it automatically.
- Test this self-healing behavior. First, get your pod name by running:
kubectl get pods
- Copy the pod name from the output (it looks like flask-app-xxxxxxx-xxxxx).
- Delete that pod by running:
kubectl delete pod [[POD_NAME="your pod name from kubectl get pods"]]
- Immediately run kubectl get pods again:
kubectl get pods
You should see a new pod with a different name already starting up or running. The Deployment detected the missing pod and created a replacement.
- Refresh http://localhost:30080 in your browser.
The app still responds, but the pod name in the response has changed. The Service automatically routed to the new pod without any manual intervention.
Why does this matter?
In production, pods can crash, get evicted, or be replaced during updates. The Service layer ensures your users never notice because traffic is always routed to healthy pods. This is a core benefit of container orchestration.
App not responding after deleting the pod?
Give it a few seconds. The new pod needs time to start the Flask process. Run kubectl get pods and wait until the STATUS column shows Running and READY shows 1/1.
Help me understand why my pod isn't recovering after deletion
Your app is now accessible from the browser and resilient to pod failures. Next up, you will scale it to multiple replicas and perform a zero-downtime rolling update.
Scale and Perform a Rolling Update
Your Flask app is live and accessible in the browser through a NodePort Service. Right now it runs on a single pod, which means any crash takes the whole app offline.
In production, teams run multiple copies of an app to handle more traffic and survive failures. In this step, you will scale your deployment to 3 replicas, then perform a zero-downtime rolling update that swaps your app from v1 to v2 without a single dropped request.
In this step, get ready to:
- Scale the deployment to 3 replicas and verify load balancing.
- Perform a zero-downtime rolling update from v1 to v2.
- Practice rolling back to a previous version instantly.
Scale your deployment
A single pod cannot handle traffic spikes or survive a crash without downtime. Scaling tells Kubernetes to run multiple identical copies of your app, called replicas. The Service automatically distributes incoming requests across all of them.
- Scale your deployment to 3 replicas by running this command:
kubectl scale deployment/flask-app --replicas=3
- Verify all 3 pods are running by running:
kubectl get pods
You should see 3 pods listed, all with STATUS Running. It may take a few seconds for all pods to reach that state.
- Check how the Service routes traffic to all 3 pods by running:
kubectl describe service flask-service
What should I see?
Look at the Endpoints field in the output. It now lists 3 IP addresses separated by commas. Each IP belongs to one of your pods.
This is how Kubernetes load balancing works. The Service watches for pods matching its selector label and automatically adds or removes their IPs from the Endpoints list.
- Open your browser and refresh http://localhost:30080 several times.
Notice the pod hostname at the end of the response changes between refreshes. Each request is being handled by a different replica.
Pods stuck in Pending or ContainerCreating?
Make sure Docker Desktop is running and the Kubernetes cluster shows a green status in the Docker Desktop Dashboard footer. If pods are stuck, run kubectl describe pod followed by the pod name to check the Events section for details.
help me debug pods stuck in Pending after scaling
Perform a rolling update
A rolling update lets you deploy a new version of your app without downtime. Kubernetes gradually replaces old pods with new ones, keeping the Service available throughout the transition.
- In app.py, find this line inside the home() function:
return f"Hello from Flask on Kubernetes! (v1) - Pod: {hostname}"
- Replace (v1) with (v2), so the line now looks like this:
return f"Hello from Flask on Kubernetes! (v2) - Pod: {hostname}"
- Save app.py.
- Build a new Docker image tagged as v2 by running:
docker build -t flask-k8s:v2 .
- Verify the new image exists by running:
docker images flask-k8s
You should see both v1 and v2 tags listed.
- Tell Kubernetes to update the deployment to use the v2 image by running:
kubectl set image deployment/flask-app flask-app=flask-k8s:v2
- Watch the rolling update progress by running:
kubectl rollout status deployment/flask-app
You should see a message like deployment "flask-app" successfully rolled out once all pods are updated.
What just happened?
Kubernetes created new pods running the v2 image one at a time. As each new pod became healthy, it terminated one of the old v1 pods. The Service continued routing traffic to whichever pods were available throughout the process.
This means your users never saw an error page or a connection timeout during the update. That is zero-downtime deployment.
Rollout stuck or pods in ImagePullBackOff?
If pods show ImagePullBackOff or ErrImageNeverPull, Kubernetes cannot find the flask-k8s:v2 image locally. Make sure you ran docker build -t flask-k8s:v2 . successfully before the kubectl set image command. Confirm the image tag matches exactly.
help me fix a rolling update that is stuck
Verify the update and practice rolling back
- Open your browser and refresh http://localhost:30080.
You should see the response now includes (v2) instead of (v1). The pod hostname confirms a new pod is serving the request.
- Confirm all pods are running the new version by running:
kubectl get pods
All 3 pods should show STATUS Running with a recent AGE (they were just recreated during the rolling update).
Rolling back is just as simple as updating. Kubernetes keeps a history of your deployment revisions, so you can revert instantly.
- Roll back the deployment to v1 by running:
kubectl rollout undo deployment/flask-app
- Refresh http://localhost:30080 in your browser.
The response now shows (v1) again. Kubernetes replaced the v2 pods with v1 pods using the same rolling strategy.
Why are rollbacks so fast?
Kubernetes did not rebuild any images. The flask-k8s:v1 image already existed locally. The rollback simply told Kubernetes to spin up pods using the previous image tag, which makes it nearly instantaneous.
The rollback reverted your running pods back to v1. To keep the project in a clean state for the Secret Mission, revert your source file too.
- In app.py, find this line:
return f"Hello from Flask on Kubernetes! (v2) - Pod: {hostname}"
- Change (v2) back to (v1), so the line looks like this:
return f"Hello from Flask on Kubernetes! (v1) - Pod: {hostname}"
- Save app.py.
✔️ Awesome, I've got everything!
Great. Double check you have saved app.py with the v1 response string.
ⓧ I'd like to double check the full code
from flask import Flask
import socket
app = Flask(__name__)
@app.route("/")
def home():
hostname = socket.gethostname()
return f"Hello from Flask on Kubernetes! (v1) - Pod: {hostname}"
@app.route("/health")
def health():
return "OK", 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Browser still showing v2 after rollback?
The rollback only applies to the Kubernetes deployment (which image the pods run). Your browser may be caching the old response. Try a hard refresh with Ctrl+Shift+R. If it still shows v2, run kubectl get pods to confirm the new pods have started (look for a recent AGE).
help me verify my rollback worked
Secret mission
Add a Redis Backend
Your Flask app runs great on Kubernetes, but it's stateless. Every pod responds independently with no shared memory. In this extension, you'll deploy Redis as a second service in your cluster and wire your Flask app to use it as a shared visit counter. This mirrors how real microservice architectures communicate inside Kubernetes.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing monetary costs. The Kubernetes cluster does use system memory and CPU while running.
Resources you used:
- A Kubernetes cluster running inside Docker Desktop (uses system memory and CPU while active).
- Docker images stored locally (flask-k8s:v1, flask-k8s:v2, flask-k8s:v3).
- Kubernetes Deployments and Services running as containers (flask-app, redis, flask-service, redis service).
- Local project files in the flask-k8s folder on your Desktop.
Keep everything running
No action needed. Choose this if you're still actively building or experimenting with your Kubernetes cluster.
- Your Kubernetes cluster stays active and your app remains accessible at http://localhost:30080.
- The cluster uses system memory and CPU but costs nothing financially.
- All manifests and images remain available for further experiments.
Pause - I'll come back to this later
Stop the Kubernetes cluster to free up system resources. Your manifest files are preserved and you can recreate everything later.
- Open the Docker Desktop Dashboard.
- Select the Kubernetes view.
- Select Stop to stop the cluster. This stops and removes Kubernetes containers but keeps your configuration intact.
- When you're ready to come back, recreate the cluster and run kubectl apply on your manifest files to restore everything.
Delete - I don't want to use this again
Remove all Kubernetes resources, Docker images, and project files to start completely fresh.
- Delete all Kubernetes resources by running this command in PowerShell:
kubectl delete -f deployment.yaml -f service.yaml -f redis-deployment.yaml -f redis-service.yaml
- Remove the Docker images by running:
docker rmi flask-k8s:v1 flask-k8s:v2 flask-k8s:v3
- Delete the Kubernetes cluster from the Docker Desktop Dashboard. Select the Kubernetes view, then select Stop to remove the cluster containers.
- Optionally, disable Kubernetes entirely by opening Settings, selecting the Kubernetes tab, unchecking Enable Kubernetes, and clicking Apply & Restart.
- Delete the project folder. On Windows, run:
cd ~/Desktop
Remove-Item -Recurse -Force flask-k8s
Nice Work!
Nice Work!
You just deployed, scaled, and updated a Flask application on a local Kubernetes cluster. This is the same core workflow that engineering teams use every day to ship code to production.
Here's what you learned:
- Built a Flask app as a Docker image and deployed it to Kubernetes using YAML manifests for Deployments and Services.
- Exposed the app to your browser with a NodePort Service and scaled to 3 replicas with automatic load balancing.
- Performed a zero-downtime rolling update from v1 to v2, then instantly rolled back using kubectl rollout undo.
- Secret Mission: Added a Redis backend with its own Deployment and Service, demonstrating inter-service communication via Kubernetes DNS.
Ready to quiz yourself?