Deploy a RAG API to Kubernetes

Learn how production systems orchestrate containers at scale with Kubernetes!

Introduction

This project has been archived

We're no longer updating this project - we recommend checking out our active projects instead!

If you've already completed this project, don't worry! Your documentation is still available for you to check out and share.

If you have any questions, leave a question in our Discord and we'll get back to you ASAP!

⚡️ 30 second Summary

Picture this: you've just learned how to package your app into a container with Docker, so it can run the same in any system. Nice!

But what if you need to run several containers - maybe one for your API, another for your database, and more to handle extra users or features? What if a container crashes? How do you scale your containers to meet a traffic spike, or make updates without downtime?

Once you've mastered containerization, managing containers at scale becomes the real challenge - and why Kubernetes exists.

Kubernetes automates deployment, recovery, scaling, and networking for containerized apps. It solves a big and common problem in production systems, and has become the second fastest-growing project in the history of open-source software (with 96% of enterprises using it)!

What you'll build

Welcome to Project #3 of a 4-part DevOps × AI series! In this project, you'll deploy your containerized RAG API to Kubernetes using Minikube.

I haven't done Projects #1 or #2 yet

No worries! You can either complete Project #1: Build a RAG API with FastAPI and Project #2: Containerize a RAG API with Docker first (which we highly recommend!), or we'll guide you through a quick setup at the start of this project to catch up. Either way works.

By the end, you'll have:

  • ☸️ A local Kubernetes cluster running on your computer
  • 🚀 A Kubernetes Deployment orchestrating your containerized app
  • 🌐 A Kubernetes Service providing stable networking

This is the same workflow many professional DevOps engineers use to ship AI workloads into production at scale 🛳️ The best part? Minikube runs locally on your computer with no cloud costs or fees.

I don't have experience in Kubernetes!

You don't need any prior experience in Kubernetes to do this project. We'll explain all the concepts along the way, and you can always ask questions as they come up.

Want a complete demo of how to do this project, from start to finish? Check out our 🎬 walkthrough with Natasha 🎬

If you're up for a bit of a challenge, quiz yourself on the key concepts up ahead in this project.

This project is part of a series:

  1. Part 1: Build a RAG API with FastAPI
  2. Part 2: Containerize a RAG API with Docker
  3. Part 3: You are here!
  4. Part 4: Automate with GitHub Actions

Before we start Step #1...

Welcome! Before we dive into deploying your containerized RAG API to Kubernetes, let's take a moment to set our intentions and understand what we're about to build.

Set Up Your Docker Image

Before we deploy anything to Kubernetes, let's make sure you have a Docker image ready!

What is a Docker image?

A Docker image is a portable package that contains your app's code, dependencies, and settings. Think of it like a blueprint or a recipe - it has everything your application needs to run, but it's not running yet.

💡 Why do we start with Docker before Kubernetes?

Kubernetes can only run things that are already packaged.

Docker comes first because it packages your app and everything it needs into a container image - a single, portable unit that runs the same on any machine.

Kubernetes then uses that image to create and run containers. It decides where they run, how many to run, and keeps the containers alive - but it never builds the image itself.

In this step, you're going to:

  • Build your Docker image

Open Cursor

Let's open Cursor on your computer:

🍎 macOS

  • Press Cmd+Space to open Spotlight.
  • Type Cursor and press Enter.

🖼️ Windows

  • Press the Win key.
  • Type Cursor and press Enter.

I don't have Cursor!

We'll use Cursor for this step-by-step guide, but you can pick another IDE like VS Code if you prefer.

If you don't have another IDE to use, check out Cursor's download page if you'd like to have a go at doing this on your own, or try Step #1 of our Build a Mini Spotify with Cursor project for step-by-step guidance!

Cursor will open on your screen.

  • Select Open project from the welcome screen.

Have you done Projects #1 and #2 in this series?

What are Projects #1 and #2 in this series?

Here's what we did in first two projects of our DevOps x AI series:

  • Project #1: A RAG API that answers questions using AI and your own knowledge base
  • Project #2: Containerized that API with Docker

✅ Yes, I completed Projects #1 and #2

Perfect! Welcome back to the DevOps × AI series!

Let's make sure your setup is ready for Kubernetes. We'll check that you have your project folder and Docker is running.

Open your project folder

  • Select Desktop.
  • Select your project folder called nextwork-rag-api
  • Select Open.

Where can I find the folder?

Your API code should be in a project folder on your computer. Common locations are your Desktop or Documents folder.

Check your Docker image

Let's make sure Docker Desktop is running.

  • In Cursor, select Terminal → New Terminal from the menu bar at the top.
  • In your terminal, run this command and press Enter:
docker ps

What does docker ps do?

docker ps lists all running Docker containers. We're using it here to verify Docker Desktop is up and running properly. An empty table or a list of containers both mean Docker is working correctly!

✅ I see a list or empty table

Perfect! Docker Desktop is running.

❌ Cannot connect to Docker daemon

No worries! That just means Docker Desktop isn't running yet.

General error

You might see an error like:

Cannot connect to the Docker daemon

This means Docker Desktop isn't running.

Windows pipe error

On Windows, you might see:

error during connect: Get "http://%2F%2F.%2Fpipe%2FdockerDesktopLinuxEngine/v1.51/containers/json": 
open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.

This means Docker Desktop isn't running or hasn't fully started yet.

Start Docker Desktop

🍎 macOS
  • Press Cmd+Space, type Docker Desktop, and press Enter.
🖼️ Windows
  • Press Win key, type Docker Desktop, and press Enter.

Wait for Docker Desktop to fully start (you'll see the Docker whale icon in your menu bar or system tray), then run docker ps again.

Still seeing the error after starting Docker Desktop?

Docker Desktop can take 30-60 seconds to fully start. Wait until you see the whale icon stop animating in your system tray, then try docker ps again.

Next, we'll run a command to check what Docker images are in your computer.

  • Run this command and press Enter:

🍎 macOS

docker images | grep rag-app
What does this command do?

docker images lists all Docker images on your system, and grep rag-app filters the list to show only images with "rag-app" in the name.

🖼️ Windows

docker images | Select-String "rag-app"
What does this command do?

docker images lists all Docker images on your system, and Select-String "rag-app" filters the list to show only images with "rag-app" in the name.

You should see your Docker image listed!

I see a different Docker image name!

If you did the secret mission from the previous project in this series (where we pushed our image to Docker Hub), you might find that your image is named [your-nickname]/rag-app!

That's totally okay! We recommend storing your image's name here now, so the commands later in this project match up perfectly with what you've got:

[[DOCKER_IMAGE_NAME="rag-app"]]

✅ I see rag-app

Awesome! You have your Docker image from Project #2.

Note

You won't need to set up your Docker image again! Jump to 🏁 the end of this step!

❌ I don't see rag-app

No worries! Maybe you deleted it or it's on a different machine.

Let's rebuild the Docker image - it'll take 10-15 minutes

➡️ Jump here for the quick setup guide to build your Docker image!

⚠️ I completed Project #1 but not Project #2

Tip

We 100% recommend doing Project #2 first! It teaches critical Docker concepts that help you understand what Kubernetes is managing. But if you want to dive into Kubernetes now, we've got you covered!

Project #2: Containerize a RAG API with Docker taught you to containerize your RAG API. Let's get up to speed! We'll be building 2 things:

  1. A Dockerfile for your app, which is set of instructions that tells Docker how to create a template of your app
  2. A built Docker image (rag-app), which is the actual template that Docker creates once it follows your Dockerfile

First, let's open the project folder where you've built your RAG API.

Open your project folder

  • Select Desktop.
  • Select your project folder called nextwork-rag-api

Where can I find the folder?

Your API code should be in a project folder on your computer. Common locations are your Desktop or Documents folder.

  • Select Open.

Here are the steps for containerizing your RAG API - it'll take 25-30 minutes!

➡️ Jump here for the quick setup guide you're all set up to deploy your app to Kubernetes.

❌ No, I haven't done Projects #1-#2

Tip

We strongly recommend doing Project #1: Build a RAG API with FastAPI and Project #2: Containerize a RAG API with Docker first! They teach critical RAG, API and Docker concepts that help you understand what Kubernetes is managing.

But if you want to dive into Kubernetes now, let's build everything below! ⬇️

Let's get up to speed! We'll build 3 things:

  • Complete code for the RAG API (app.py, embed.py, k8s.txt)
  • A Dockerfile to containerize it
  • A Docker image ready for Kubernetes

Why do we need these 3 components?

Kubernetes works with containers - it can't run your application code directly. That's why we need to containerize our RAG API first!

We'll write the application code, create a Dockerfile to package it into a container, and build a Docker image that Kubernetes can deploy. Without all three, Kubernetes wouldn't know how to run your application!

Create the project folder

  • From Cursor's folder selection window, click into Desktop.
  • Select New folder.
  • Name your folder nextwork-rag-api
  • Select this folder as your repository.

Your project folder should now be open in Cursor, and you'll see the file explorer in the left sidebar.

Create the knowledge document file

What is k8s.txt?

This file contains the knowledge base that your API will search through to answer questions. In a real application, this could be documentation, FAQs, or any text content. We're using a simple Kubernetes description for this project!

  • In Cursor's file explorer (left sidebar), select New File.
  • Name the file k8s.txt
  • Press Enter.
  • Click inside the newly created k8s.txt file in the editor.
  • Copy and paste in the following content:
Kubernetes is a container orchestration platform used to manage containers at scale.
  • Press Cmd + S (macOS) or Ctrl + S (Windows/Linux) to save the file.

You should see a white circle disappear next to the file name once it's saved.

Create embedding script

What does embed.py do?

This script reads your knowledge base (k8s.txt) and converts it into embeddings (numerical representations) that can be efficiently searched. ChromaDB stores these embeddings in a database (./db/) so your API can quickly find relevant information when answering questions.

  • In Cursor's file explorer (left sidebar), select New File again.
  • Name the file embed.py
  • Press Enter.
  • Click inside the newly created embed.py file in the editor.
  • Paste the following code:
import chromadb

client = chromadb.PersistentClient(path="./db")
collection = client.get_or_create_collection("docs")

with open("k8s.txt", "r") as f:
    text = f.read()

collection.add(documents=[text], ids=["k8s"])

print("Embedding stored in Chroma")
  • Press Cmd + S (macOS) or Ctrl + S (Windows/Linux) to save the file.

Create your RAG API

What does app.py do?

This is your RAG API! When someone asks a question:

  1. It searches ChromaDB for relevant context from your knowledge base
  2. It sends the question and context to Ollama (the AI model)
  3. Ollama generates an answer based on the context
  4. The API returns the answer as JSON

RAG (Retrieval-Augmented Generation) combines retrieval (searching for relevant info) with generation (creating answers with AI) for more accurate, context-aware responses!

  • In Cursor, select New File in your project's root.
  • Name your new Python file app.py
  • Press Enter.
  • Click on the file to open it in the editor.
  • Paste this code:
from fastapi import FastAPI
import chromadb
import ollama

app = FastAPI()
chroma = chromadb.PersistentClient(path="./db")
collection = chroma.get_or_create_collection("docs")

@app.post("/query")
def query(q: str):
    results = collection.query(query_texts=[q], n_results=1)
    context = results["documents"][0][0] if results["documents"] else ""

    answer = ollama.generate(
        model="tinyllama",
        prompt=f"Context:\n{context}\n\nQuestion: {q}\n\nAnswer clearly and concisely:"
    )

    return {"answer": answer["response"]}
  • Save the file.

Create Dockerfile

Now let's create the Dockerfile that tells Docker how to build your container.

What does this Dockerfile do?

A Dockerfile is like a recipe with step-by-step instructions for building a Docker image. Let's break down the Dockerfile:

  • FROM python:3.11-slim - Start with a lightweight Python 3.11 image as the base
  • WORKDIR /app - Set the working directory inside the container to /app
  • RUN apt-get update... - Install system tools (like curl) for health checks
  • COPY app.py embed.py k8s.txt ./ - Copy your application files into the container
  • RUN pip install... - Install Python packages (FastAPI, ChromaDB, Ollama, etc.)
  • RUN python embed.py - Run the embedding script to build the database
  • EXPOSE 8000 - Document that the container listens on port 8000
  • CMD ["uvicorn"...] - Start the FastAPI server when the container runs

This Dockerfile is the blueprint - the actual image gets created when you build it!

  • In your project folder, create a new file called Dockerfile.
  • Right-click in your project folder in Cursor's file explorer.
  • Select New File.
  • Name it Dockerfile (no extension)

Note

Check Make sure:

  1. You've created Dockerfile in your project's root directory (where you store app.py, embed.py, and k8s.txt).
  2. Your Dockerfile is named exactly Dockerfile with no extension (not Dockerfile.txt or dockerfile).
  • Open Dockerfile in your IDE.
  • Paste this content:
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
COPY app.py embed.py k8s.txt ./
RUN pip install fastapi uvicorn chromadb ollama
RUN python embed.py
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
  • Save the file press Cmd+S on Mac or Ctrl+S on Windows.

The white circle next to your filename should disappear once the file is saved!

Having trouble with your Dockerfile?

If you see syntax errors when building, get help with Dockerfile syntax here.

Install Docker

To build your Docker image, we'll start by checking if you have Docker installed.

  • Run the following command:
docker --version

❌ Command not found

That tells us Docker isn't installed yet - let's get it set up 🐳

What's Docker Desktop? Is that the same thing as Docker?

Docker Desktop is a program that makes it easy to work with Docker, a tool for creating and managing containers. Think of Docker Desktop as the user-friendly app you install on your computer that includes Docker itself, along with a nice interface and helpful tools.

Engineers use Docker Desktop to build, test, and deploy applications right from their computers. It handles all the complex setup behind the scenes, so you can focus on using Docker in a user-friendly way.

Now download the correct installer for your system:

🍎 macOS

Select the correct download for your Mac

  • If you have Apple Silicon (M1, M2, or M3), select Download for Mac - Apple Chip.
  • If you have Intel, select Download for Mac - Intel Chip.
I don't know what version I should download for my Mac!

First, let's check what type of Mac you have.

  • Click the Apple logo in the top right of your display
  • Select About This Mac.
  • If you see Apple M1, Apple M2, or Apple M3 under Chip, you have Apple Silicon → Download for Mac - Apple Chip
  • If you see Intel Core, you have an Intel Mac → Download for Mac - Intel Chip

Install Docker Desktop

What's Docker Desktop? Is that the same thing as Docker?

Docker Desktop is a program that makes it easy to work with Docker, a tool for creating and managing containers. Think of Docker Desktop as the user-friendly app you install on your computer that includes Docker itself, along with a nice interface and helpful tools.

Engineers use Docker Desktop to build, test, and deploy applications right from their computers. It handles all the complex setup behind the scenes, so you can focus on using Docker in a user-friendly way.

  • Open the Docker.dmg file from your Downloads folder.
  • Drag the Docker icon to your Applications folder.
  • Open Docker from your Applications folder.
Why open Docker Desktop?

Opening Docker Desktop starts the Docker service in the background. This service needs to be running for any Docker commands to work. You'll see a whale icon in your menu bar when Docker is running.

  • Click Open at the security warning.

You may see a Docker Subscription Service Agreement screen.

  • Click Accept.

You'll see a configuration screen to finish setting up Docker Desktop.

  • Keep Use recommended settings selected (the default option).
  • Click Finish.
What are recommended settings?

Recommended settings usually include turning on the right virtualization backend for your OS, setting up file sharing so containers can read and write files from key folders on your machine, and giving Docker reasonable CPU and memory limits so it doesn't overwhelm your laptop. For most developers, these defaults are safe and work well out of the box, and you can always fine-tune them later in Settings if you notice Docker is using too many resources or you want to share different folders.

Docker may prompt for your password to apply these configurations.

  • Enter your password and click OK.

You'll see a Welcome to Docker screen.

  • Click Skip in the top right.

You should now see the Docker Desktop dashboard!

Now let's verify Docker is working!

  • Open Terminal on your Mac
  • Run this command:
docker --version
What should I see?

You should see something like Docker version 24.0.7, build afdd53b. The version and build ID on your computer will probably be different – that's totally expected and your Docker will work just the same. Why are version numbers different?

Perfect! Docker is installed and working on your Mac.

🖼️ Windows

  • Click Download for Windows 10/11 (64-bit)

While we wait for the download, did you know Docker Desktop on Windows requires WSL 2? Let's check if you have it installed.

  • Open PowerShell (right-click Start menu → Windows PowerShell).
  • Run the following command to check if WSL 2 is installed:
wsl --version
✅ I see WSL version info

Nice! WSL 2 is already installed.

⚠️ Command not found or error

If you see command not found or an error, you need to install WSL 2.

  • Open Windows Terminal as Administrator (right-click Start → Windows Terminal (Admin)).
  • Run the following command to install WSL 2:
wsl --install
What does wsl --install do?

This command installs WSL 2 and the default Ubuntu Linux distribution. WSL 2 is a lightweight Linux environment that runs inside Windows, which Docker Desktop uses to run Linux containers.

  • Wait for the installation to complete (this may take 5-10 minutes).
  • Restart your computer when prompted or when the installation completes.
  • After restarting, verify WSL 2 is installed by running wsl --version in PowerShell.
Note

I'm still having WSL 2 issues! If you continue to have problems installing WSL 2, you may need to:

  1. Enable virtualization in your BIOS settings
  2. Make sure Windows is up to date (Settings → Windows Update)
  3. Try troubleshooting your error here, or share your error message with the NextWork community!

Install Docker Desktop

Let's install Docker Desktop!

What's Docker Desktop? Is that the same thing as Docker?

Docker Desktop is a program that makes it easy to work with Docker, a tool for creating and managing containers. Think of Docker Desktop as the user-friendly app you install on your computer that includes Docker itself, along with a nice interface and helpful tools.

Engineers use Docker Desktop to build, test, and deploy applications right from their computers. It handles all the complex setup behind the scenes, so you can focus on using Docker in a user-friendly way.

  • Open your Downloads folder.
  • Find the Docker Desktop Installer.exe.
  • Double-click it to start the installation.

You'll see a security warning: Open File - Security Warning.

  • Click Run.

You might be prompted: Do you want this app to make changes to your device?

  • Click Yes.

On the configuration screen, you'll see a checkbox for Add shortcut to desktop (optional).

  • Click OK.
Pro Tip

The desktop shortcut makes it easy to open Docker Desktop later, but you can also find it in your Start menu.

Docker will now begin Unpacking files.

What's Docker unpacking?

Docker Desktop is installing its core components: the Docker Engine (runs containers), Docker CLI (command-line tools), and desktop interface. It's also setting up the virtualization layer that lets containers run on Windows using WSL 2.

When unpacking is complete, you'll see Installation succeeded with a message that Docker Desktop is installed.

  • Click Close.

If you see a Restart required popup, restart your computer when you're ready.

  • Click Restart to complete the installation.
Why restart?

Docker Desktop needs to install system-level components and drivers. The restart ensures these components are properly loaded before you start using Docker.

After restarting, you'll need to open Docker Desktop.

  • Open Docker Desktop from your Start menu.

You'll see a Welcome to Docker screen asking you to sign in.

  • Click Skip in the top right corner.

You should now see the Docker Desktop dashboard!

Why open Docker Desktop?

Opening Docker Desktop starts the Docker service in the background. This service needs to be running for any Docker commands to work. You'll see a whale icon in your system tray when Docker is running.

Now let's verify Docker is working!

  • Open PowerShell on your Windows computer
  • Run this command:
docker --version
What should I see?

You should see something like Docker version 24.0.7, build afdd53b. The version and build ID will likely be different on your computer - this is normal and doesn't affect functionality. Why are version numbers different?

Perfect! Docker is installed and working on your Windows machine.

Wait! ✋ Let's double-check Docker Desktop is running

Quick check before we move on - Docker Desktop needs to be running for the next steps to work:

  • Look for the whale icon 🐳 in your menu bar (Mac) or system tray (Windows)
  • Don't see it? Open Docker Desktop from your Applications folder (Mac) or Start menu (Windows)

Why check this?

Docker commands won't work unless Docker Desktop is running in the background. The whale icon is your visual confirmation that everything is ready to go!

✅ I see a version number

Perfect! That should mean Docker Desktop is already installed. Now let's make sure it's running.

  • Open Docker Desktop from your Applications folder (Mac) or Start menu (Windows).

Hmm, I can't find Docker Desktop

Aha! That means we will still need to download Docker Desktop after all. It's possible for your terminal to show a docker version, but not have Docker Desktop if it was installed, and later deleted, in the past.

Make sure to switch over to the ❌ Command not found tab and check out the steps for installing Docker Desktop.

  • Wait for Docker to fully start (the whale icon should be steady, not animated).

You should see the Docker Desktop dashboard:

Build the Docker Image

What is a Docker image?

A Docker image is the final packaged result you get after building your Dockerfile.

Here's why you need both: The Dockerfile is a text file tells Docker how to build your app, but you can't run a Dockerfile directly. The Docker image is what you actually create once you follow those instructions! The Dockerfile is reusable and shareable so others can build the same image, while the image is the ready-to-run package.

In this project, your rag-app image contains Python, your code, the embeddings database, and everything needed to run your API!

Now let's build your Docker image. This packages everything into a container.

What does docker build do?

docker build reads your Dockerfile and executes each instruction to create a Docker image. The -t rag-app flag tags (names) the image rag-app so you can reference it easily. The . at the end tells Docker to look for the Dockerfile in the current directory.

  • Make sure you're still in your project directory (~/Desktop/nextwork-rag-api).
  • In your terminal, run this command and press Enter:
docker build -t [[DOCKER_IMAGE_NAME="rag-app"]] .
  • Press Enter and wait.

This will take 2-3 minutes the first time - Docker is downloading Python, installing packages, and building your embeddings database. Don't close your terminal!

What do all these steps mean?

Each line in the build output corresponds to a command in your Dockerfile:

  • [1/6] FROM ... - Pulls the base Python image from Docker Hub (this might take a minute if it's your first time)
  • [2/6] WORKDIR ... - Sets the working directory inside the container
  • [3/6] RUN ... - Installs system dependencies (like curl)
  • [4/6] COPY ... - Brings your code files into the image
  • [5/6] RUN pip install ... - Installs Python packages (this is usually the longest step)
  • [6/6] RUN python embed.py - Pre-computes embeddings for your knowledge base

Each step creates a new "layer" in your image. If you rebuild later and only change one file, Docker will use cached layers for unchanged steps, making rebuilds much faster!

  • Wait for the build to complete. You should see Successfully built or View build details at the end of the terminal output.

Verify the Image

Let's confirm your image was built successfully.

  • Run this command and press Enter:

🍎 macOS

docker images | grep [[DOCKER_IMAGE_NAME="rag-app"]]

🖼️ Windows

docker images | Select-String "[[DOCKER_IMAGE_NAME="rag-app"]]"

You should see rag-app with the tag latest:

Perfect! Your Docker image is ready.

Why do I need a Docker image to deploy my app to Kubernetes?

Kubernetes is a container orchestration platform that manages and runs containers across multiple machines. It doesn't create containers from scratch - it needs Docker images as instruction manuals for what to run!

That's why we need both - in this project, we'll use your Docker image (which contains your RAG API) and deploy it to Kubernetes, so Kubernetes can manage and scale your API automatically!

Great work! You now have:

  • ✅ Docker image (rag-app) built and verified
  • ✅ Docker Desktop running

Now let's install the Kubernetes tools you'll need!

Why Kubernetes?

Docker is great for running a single instance of your RAG API in a container. But if you want to run many containers of your RAG API, handle more users asking questions, or recover from crashes automatically, you need something more powerful. That's where Kubernetes comes in!

In our case, Kubernetes manages your RAG API containers (i.e. starting, stopping, and scaling them) by organizing them into a cluster of machines that work together to keep your API running.

Set Up Kubernetes

Now that you have your containerized API, let's set up the tools you need to deploy it to Kubernetes. You'll install Minikube and kubectl - the essential tools for running Kubernetes locally.

In this step, you're going to:

  • Install Minikube
  • Install kubectl
  • Verify both tools are working

What is Minikube?

Minikube is a tool that lets you run a local Kubernetes cluster on your computer. It creates a small Kubernetes environment so you can experiment, test deployments, and learn Kubernetes concepts without needing a cloud provider or a large infrastructure!

💡 What is kubectl?

kubectl (pronounced "kube-control" or "kube-cuttle") is the command-line tool that lets you talk to Kubernetes clusters. Think of it like the docker CLI, but for Kubernetes - you use it to deploy applications, inspect resources, view logs, and manage your cluster from the terminal.

In this project, you'll need both Minikube (which runs the cluster) and kubectl (which lets you control the cluster using commands).

<!--

Check System Requirements

Before installing Kubernetes tools, let's make sure your system is ready. Minikube needs a few things to work properly.

Let's check if Docker Desktop is running.

  • Open your terminal:

🍎 macOS

  • Press Cmd+Space and type Terminal

🖼️ Windows

  • Press Win key and type PowerShell
  • Run this command and press Enter:
docker ps

You should see a list of containers (or an empty list if none are running). If you see an error like Cannot connect to the Docker daemon, start Docker Desktop first.

Extra for Experts: Why does Minikube need Docker?

Minikube uses Docker as a "driver" to create and manage the containers that make up your local Kubernetes cluster. In this setup, Docker provides the environment where all the Kubernetes components (like the control plane, scheduler, etc.) run as containers on your machine. This allows you to simulate a real Kubernetes cluster - all inside Docker - making it fast and easy to experiment without needing extra hardware or virtual machines. -->

<!-- Let's also verify you have enough disk space.

  • In your terminal, run this command and press Enter:

🍎 macOS

df -h | grep -E "Size|disk1"

Check the output - make sure you have at least 10GB free for Minikube to work comfortably!

🖼️ Windows

Get-PSDrive C | Select-Object Used,Free

Check the output - make sure you have at least 10GB free for Minikube to work comfortably!

Why does Minikube need so much space?

To run your Kubernetes cluster, Minikube will download Kubernetes components and container images, and create containers.

The cluster we deploy should only take about 2-3GB, but having 10GB free makes sure you won't run into performance issues during this project (Docker often crashes when there is little storage space left in your computer). -->

Install Minikube

Let's install Minikube for your operating system:

🍎 macOS

First, let's check if Homebrew is installed.

  • Run this command and press Enter:
brew --version

You should see something like:

I get Homebrew not found!

No worries!

  • Visit https://brew.sh in your browser
  • Copy the installation command from the homepage
  • Paste it into your terminal and press Enter
  • Follow the prompts to complete installation
  • After installation, run brew --version again to verify

🙋‍♀️ Having trouble installing Homebrew?

If you encounter errors during Homebrew installation, get help here.

Now let's install Minikube.

  • In your terminal, run this command and press Enter:
brew install minikube

What does this command do?

brew install minikube tells Homebrew to download and install Minikube and all its dependencies. Homebrew handles all the file placement and configuration automatically - you don't need to worry about where things go!

  • Press Enter and wait for installation to complete. This may take a few minutes.

🖼️ Windows

We'll use winget (Windows Package Manager) to install Minikube.

  • Run this command and press Enter:
winget install -e --id Kubernetes.minikube

What does this command do?

winget install is Windows' built-in package manager (similar to Homebrew on Mac). The -e --id flags tell it to install exactly the package we specify: Kubernetes.minikube.

  • Press Enter and wait for installation to complete. This may take a few minutes. You should see a success message when it's done.

I'm seeing Command not recognized or WSL2 issues!

If winget doesn't work:

  • Make sure you're running Windows 10 (version 1809 or later) or Windows 11
  • Try updating Windows via Settings → Windows Update
  • Alternatively, download the installer from minikube's GitHub releases

WSL2 Tip: If you're using WSL2, Minikube works best when installed in your Windows environment (not inside WSL). This lets it integrate properly with Docker Desktop.

🙋‍♀️ Still having trouble installing Minikube on Windows?

Try troubleshooting your error here, or share your error message with the NextWork community!

Let's verify Minikube was installed successfully.

  • Run this command and press Enter:
minikube version

What does this command do?

minikube version displays the installed version of Minikube, confirming it was installed correctly.

✅ I see a version number

Perfect!

What do the version numbers mean?

The version number tells you which release of Minikube you have. As long as you see v1 followed by any number, you're good! Newer versions have more features and bug fixes, but all v1.x versions work for this project.

❌ Command not found or not recognized

If you see an error like:

minikube : The term 'minikube' is not recognized as the name of a cmdlet, function, script file, or operable program.

Or:

minikube: command not found

This means Minikube isn't in your PATH yet. This is common after installation!

Solution 1: Restart your terminal (easiest)

  • Close your Terminal (Mac) or PowerShell (Windows) window completely.
  • Open a new Terminal (Mac) or PowerShell (Windows) window.
  • Try minikube version again.

Why does restarting help?

When you install software, it adds itself to your system's PATH (the list of places your terminal looks for commands). Your current terminal session doesn't see these PATH changes until you restart it!

Solution 2: Use the full path (if restart doesn't work)

🖼️ Windows

On Windows, minikube is usually installed to:

C:\Program Files\Kubernetes\Minikube\minikube.exe version

Or check:

$env:LOCALAPPDATA\Microsoft\WinGet\Packages\Kubernetes.minikube_*\minikube.exe version

🍎 macOS

On macOS, try:

/opt/homebrew/bin/minikube version

Solution 3: Manually add to PATH (advanced)

If neither solution works, you may need to manually add Minikube to your PATH. Get help with PATH configuration here.

Still having trouble with Minikube installation?

Try troubleshooting your error here, or share your error message with the NextWork community!

Install kubectl

Now let's install kubectl, the Kubernetes command-line tool.

What does kubectl do?

Every interaction with Kubernetes goes through kubectl!

In this project, kubectl connects to the Kubernetes cluster that Minikube will create and lets you run commands to deploy apps, check status, view logs, and more.

  • In your terminal, run this command and press Enter:

🍎 macOS

brew install kubectl

🖼️ Windows

winget install -e --id Kubernetes.kubectl
  • Press Enter and wait for installation to complete.

This may take a minute or two. You should see kubectl was successfully installed! when it's done.

I see "Warning: kubernetes-cli 1.35.0 is already installed and up-to-date."

Don't worry - this is totally normal! When you installed Minikube earlier, Homebrew automatically installed kubectl as a dependency (since Minikube needs kubectl to work).

This warning just means kubectl is already on your system, so you can skip ahead to verifying the installation. Your kubectl is ready to use!

Let's verify kubectl was installed successfully.

  • Run this command and press Enter:
kubectl version --client

What does this command do?

kubectl version --client displays the version of your kubectl CLI tool, confirming the installation was successful.

💡 What does --client mean?

The --client flag tells kubectl to only show the version of the kubectl tool itself, not the Kubernetes cluster version (since we haven't started the cluster yet). Once Minikube is running, you can run kubectl version without --client to see both the client and server versions!

✅ I see a version number

Extra for Experts: What do the Client vs Kustomize versions mean?

When you run kubectl version --client, you'll see two version numbers:

  • Client Version - This is the version of kubectl itself (the command-line tool you just installed). This is the main version you care about for this project!
  • Kustomize Version - This is the version of Kustomize, a tool that's built into kubectl. Kustomize helps you customize Kubernetes configuration files without editing them directly. You won't need it for this project, but it's nice to know it's there!

Both tools are bundled together, so seeing both versions confirms everything is installed correctly.

Perfect! kubectl is installed and working.

❌ Command not found or error

If you see kubectl: command not found:

  • Close and reopen your terminal (sometimes PATH updates don't apply immediately)
  • On macOS: Make sure Homebrew's bin directory is in your PATH
  • On Windows: Restart PowerShell as Administrator and try again
  • Get help with kubectl PATH configuration
  • If still not working, your package manager might not have added kubectl to your PATH. Try running the full path: /usr/local/bin/kubectl version --client (Mac) or check the installation location

Still having trouble with kubectl installation?

Try troubleshooting your error here, or share your error message with the NextWork community!

Verify kubectl config file

Let's also verify kubectl can find a config file (it won't have anything yet, but this tests the setup).

Why do we care about the config file?

The kubectl config file is like kubectl's address book - it stores information about which Kubernetes clusters kubectl can connect to, how to authenticate with them, and which cluster to use by default.

Right now, your config file is empty because we haven't created a cluster yet. But once Minikube starts your local cluster, it will automatically add connection details to this config file. That's how kubectl knows how to talk to your Minikube cluster!

By checking the config file now, we're making sure kubectl can read and write to it properly, so it's ready when Minikube needs to add cluster information.

  • Run this command and press Enter:
kubectl config view

What does this command do?

kubectl config view displays your kubectl configuration file, which stores information about clusters, users, and contexts. It's empty now because we haven't created a cluster yet!

✅ I see my config file

Extra for Experts: What does this output mean?

This is the structure of your kubectl config file, shown in YAML format! Here's what each field means:

  • apiVersion: v1 - The version of the Kubernetes API this config file uses. This is standard and you don't need to change it.
  • clusters: null - This is where cluster connection details will be stored. Right now it's empty (null) because we haven't created a cluster yet. Once Minikube starts, it will add your local cluster here!
  • contexts: null - A context is a combination of a cluster, user, and namespace. It's like a "profile" that tells kubectl which cluster to connect to and how. This will be populated when Minikube sets up your cluster.
  • current-context: "" - This shows which context is currently active. It's empty because there are no contexts yet!
  • kind: Config - This tells Kubernetes that this is a configuration file (as opposed to a Deployment, Service, or other Kubernetes resource).
  • users: null - This stores authentication information for different users. It's empty now, but Minikube will add authentication details when it creates the cluster.

This shows kubectl is ready to connect to a cluster - the empty lists are normal since Minikube hasn't created a cluster yet!

❌ I see an error or different output

Don't worry! Here are common scenarios:

If you see error: no configuration has been provided:

  • This means kubectl can't find its config file location. This is usually fine and will be fixed when Minikube creates the cluster.

If you see nothing or a blank output:

  • Try running the command again
  • Make sure kubectl installed correctly by running kubectl version --client again

If you see clusters or contexts listed:

  • Great! This means you already have a Kubernetes config from a previous installation. This won't interfere with Minikube.

Still having issues?

Try troubleshooting your error here, or share your error message with the NextWork community!

Perfect! You've successfully installed Minikube and kubectl.

This is huge - you now have kubectl, the same command-line tool that DevOps engineers use to manage production Kubernetes clusters!

While Minikube runs a local cluster (production clusters typically run on cloud services like AWS EKS or Google GKE), the kubectl commands and Kubernetes concepts you'll learn here apply directly to production.

Now let's use them to start your own local cluster! ☸️

Start Your Kubernetes Cluster

With your Kubernetes tools ready, let's run a real Kubernetes cluster on your computer!

What's a Kubernetes cluster and why do we need one?

A Kubernetes cluster is the foundation that makes Kubernetes work - it's a set of machines (called nodes) that work together to run and manage your containerized applications.

You can't use Kubernetes without a cluster! Think of it like this: Docker runs containers on a single machine, but Kubernetes needs a cluster (even if it's just one node) to orchestrate containers across multiple machines, handle failures, scale up or down, and manage networking.

In production, clusters can have hundreds of nodes spread across data centers. Minikube creates a single-node cluster perfect for learning - it's still a real cluster, just smaller! This cluster will manage your RAG API containers - starting them, restarting them if they crash, scaling them when needed, and routing traffic to them.

In this step, you're going to:

  • Start Minikube cluster
  • Verify the cluster is running

Start Minikube Cluster

Let's start your local Kubernetes cluster.

Had PATH issues during installation?

If minikube version didn't work earlier and you're using the full path to run minikube commands, make sure to use the full path in other minikube commands!

For example, on Windows: & "C:\Program Files\Kubernetes\Minikube\minikube.exe" start

For example, on Mac: /opt/homebrew/bin/minikube start

  • Run this command in your terminal and press Enter:
minikube start

What does this command do?

minikube start creates and starts a local Kubernetes cluster on your machine. It downloads Kubernetes components, configures networking, and starts all the necessary services.

💡 Extra for Experts: What services does Minikube start?

Minikube starts several core Kubernetes services that work together to manage your containers:

  • API Server - The "front desk" of Kubernetes that receives and processes all commands (like creating pods or deployments)
  • Scheduler - Decides which node (computer) should run each container
  • Controller Manager - Keeps track of the desired state of your cluster and makes sure everything matches
  • etcd - A database that stores all the configuration and state of your cluster
  • kube-proxy - Handles networking between containers and services

These services run as containers inside Minikube's virtual environment, working together to create a fully functional Kubernetes cluster on your computer!

  • Press Enter and wait.

This will take 2-5 minutes the first time. Don't close your terminal!

What's happening during these 2-5 minutes?

Minikube is:

  • Creating a Docker container to run Kubernetes
  • Downloading and installing all the Kubernetes components (API server, scheduler, etc.)
  • Setting up networking components so containers can communicate
  • Starting all the Kubernetes services we mentioned above
  • Wait for the final Done! message to appear.

What do you see?

✅ Minikube started successfully

Perfect! Now let's verify the cluster is ready.

  • Run this command and press Enter:
kubectl get nodes

What does this command do?

kubectl get nodes lists all the nodes (machines) in your Kubernetes cluster, showing their status, roles, and other details.

When a node shows Ready status, it means Kubernetes has finished starting up all the necessary services on that node and it's ready to run containers. If the node isn't ready, Kubernetes won't be able to deploy your RAG API containers to it!

💡 What are nodes in Kubernetes?

A node is a machine (physical or virtual) that runs your containers. Think of it like a worker in a factory - the node is where your containers actually execute.

In production, Kubernetes clusters have many nodes (hundreds of servers working together). But Minikube creates a single-node cluster - just one machine running on your laptop.

Here's how the layers work:

  1. Your laptop - Your physical computer (Mac or Windows)
  2. Minikube - The tool that creates and manages a local Kubernetes cluster on your laptop
  3. The node (named "minikube") - A virtual machine or container that Minikube created inside your laptop. This is where Kubernetes actually runs.
  4. Kubernetes (the cluster) - The Kubernetes system running on the node. This is what manages your Deployments, Services, Pods, and containers. When you run kubectl commands, you're talking to Kubernetes on this node.
  5. Your Kubernetes resources - Your Deployments, Services, Pods, and containers. These are all managed by Kubernetes on the node. When you create a Deployment or Service (in the next few steps), Kubernetes stores and manages them here.

So when we say "your node" or "the node" in this project, we're talking about that virtual machine/container named minikube that Minikube created. This single node will:

  • Run your Docker containers (the containerized RAG API)
  • Store and manage all your Kubernetes resources (Deployments, Services, Pods, etc.)
  • Manage networking between containers
  • Handle incoming traffic from outside the cluster (this is where NodePort comes in!)
  • Keep your containers running even if they crash <!-- > 💡 What's the difference between a cluster and a node? They're not the same thing, but they're closely related:
  • The node = The machine (virtual or physical) that runs Kubernetes and your containers. In this project, it's the virtual machine named minikube that Minikube created.
  • The cluster = The entire Kubernetes system (the management software, API server, scheduler, etc.) that runs ON the node. The cluster is what manages all your resources.

Think of it like this: The node is the building, and the cluster is the management company that runs everything inside that building. In Minikube, you have one building (one node) with one management company (one cluster) running inside it.

In production, you'd have many nodes (many buildings) all managed by one cluster (one management company).

💡 Are Deployments and Services inside the node?

Yes! When you create a Deployment or Service using kubectl apply, the cluster (which runs on your node) stores and manages them.

Think of it this way: The node is like a building, and inside that building, Kubernetes (the management system) keeps track of:

  • Your Deployment (the blueprint that says "run 1 copy of my RAG API")
  • Your Service (the network rules that say "route traffic to Pods with label app=rag-api")
  • Your Pods (the actual running containers)

All of these exist as objects that Kubernetes manages on the node. When you access your API, the request goes: Your laptop → Node (via NodePort) → Service (managed by Kubernetes on the node) → Pod (running on the node) → Your container! -->

The STATUS should be Ready. If it says NotReady, wait 30 more seconds and check again - the node is still initializing.

What do you see?

✅ My node shows Ready

You should see:

NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   30s   v1.28.3
What does each column mean?
  • NAME: This is the name assigned to your node in the cluster. With Minikube, it's usually minikube.
  • STATUS: Shows the current state of the node. Ready means it's able to run workloads and accept pods.
  • ROLES: Indicates the role of the node. For Minikube, it acts as the control-plane - the component that manages the cluster.
  • AGE: Displays how long this node has been running since it was started.
  • VERSION: The version of Kubernetes that's currently installed on this node.

Awesome! Your Kubernetes cluster is running!

❌ My node stays NotReady

If your node is stuck in NotReady after 2-3 minutes:

  • Check Docker Desktop is running and has enough resources (Settings → Resources → at least 2GB RAM, 2 CPUs)
  • Try deleting and restarting: minikube delete && minikube start
  • Check Minikube logs: minikube logs to see what's failing
  • Ensure your network allows Minikube to download images
Still having trouble getting your Kubernetes node to Ready status?

Try troubleshooting your error here, or share your error message with the NextWork community!

❌ I see errors during startup

No worries! Let's get Minikube running. Here are the most common startup issues:

Command not recognized

If you see an error like:

minikube : The term 'minikube' is not recognized as the name of a cmdlet, function, script file, or operable program.

Or:

minikube: command not found

This means Minikube still isn't in your PATH. You have two options:

Option 1: Use the full path for all minikube commands

Instead of minikube start, use the full path:

🖼️ Windows
& "C:\Program Files\Kubernetes\Minikube\minikube.exe" start
Why the & symbol?

In PowerShell, the & (call operator) is needed when running executable files with spaces in the path. It tells PowerShell to execute the command at that location.

For the rest of this project, you'll need to use the full path for all minikube commands. For example:

  • & "C:\Program Files\Kubernetes\Minikube\minikube.exe" start
  • & "C:\Program Files\Kubernetes\Minikube\minikube.exe" status
  • & "C:\Program Files\Kubernetes\Minikube\minikube.exe" service rag-app-service --url
🍎 macOS
/usr/local/bin/minikube start

Or if you're on Apple Silicon:

/opt/homebrew/bin/minikube start

For the rest of this project, use the full path for all minikube commands.

Option 2: Fix your PATH (recommended)

  • Close your terminal completely and open a new one (this often fixes the issue).
  • If that doesn't work, get help manually adding Minikube to your PATH here.
Still having trouble running minikube commands?

Try troubleshooting your error here, or share your error message with the NextWork community!

Addon errors

If you see errors about addons (like storage-provisioner or dashboard):

This is normal!

It's common to see errors like connection refused or addon failures during minikube startup. This happens because addons try to configure themselves before the Kubernetes API server is fully ready.

These errors are usually harmless. Wait for the Done! message to show in your terminal.

  • Verify the cluster:
kubectl get nodes

If the node shows Ready, you're good to go! The addon errors don't affect basic functionality.

  • If you want to fix the addon , delete and recreate your cluster:
minikube delete
minikube start

This creates a fresh cluster and usually resolves startup issues.

Still having trouble with Minikube addon errors?

Try troubleshooting your error here, or share your error message with the NextWork community!

Docker not running

If you see Docker is not running:

  • Make sure Docker Desktop is running (check for the whale icon).
  • Start Docker Desktop if it's not running.
  • Wait for Docker to fully start, then try minikube start again.
Still having trouble with Docker not running?

Try troubleshooting your error here, or share your error message with the NextWork community!

Virtualization errors (Windows)

If you see virtualization errors on Windows:

  • Make sure virtualization is enabled in BIOS (see Step #2 for WSL setup instructions).
  • Some Windows features might need to be enabled. Check Windows Features for "Virtual Machine Platform" and "Windows Subsystem for Linux".

Not enough memory

If you see memory errors:

  • Minikube needs at least 2GB of RAM.
  • Close other applications to free up memory.
  • You can specify less memory (not recommended):
minikube start --memory=2048

Other errors

If you see a different error:

  • Read the error message carefully - it usually tells you what's wrong.
  • Try deleting and recreating the cluster:
minikube delete
minikube start
  • Check Minikube logs:
minikube logs
Still having trouble starting Minikube?

Try troubleshooting your error here, or share your error message with the NextWork community!

Load rag-app Image into Minikube

Now we need to get your Docker image into Minikube so Kubernetes can use it.

Why does Kubernetes need my Docker image?

Kubernetes runs containers, and containers come from Docker images! Your rag-app Docker image contains everything your RAG API needs to run - the code, dependencies, and configuration.

Remember that Minikube creates a separate, isolated environment on your computer - think of it like a virtual machine that runs its own copy of Docker. This means Minikube has its own Docker storage where it keeps images, completely separate from your computer's Docker Desktop (the one you use on your host machine).

When we say we need to "get your Docker image into Minikube," this means we need to copy it from your computer's Docker storage (where Docker Desktop stores images) into Minikube's Docker storage. This way, when Kubernetes (which runs inside Minikube) looks for the image, it can find it in Minikube's Docker storage.

💡 Extra for Experts: Why can't Kubernetes just use my local image?

This separation exists for security and isolation - Minikube's environment is completely isolated from your host machine. This means processes, files, and Docker images don't automatically share between them.

In production, you'd typically push your image to a container registry (like Docker Hub or Amazon ECR) and Kubernetes would pull it from there. But for local development with Minikube, we use minikube image load to copy the image directly into Minikube's Docker storage.

First, let's check if you have the rag-app image on your host machine.

  • Run this command and press Enter:

🍎 macOS

docker images | grep [[DOCKER_IMAGE_NAME="rag-app"]]

🖼️ Windows

docker images | Select-String "[[DOCKER_IMAGE_NAME="rag-app"]]"

✅ I see rag-app in the list

Perfect! You have the image from Project #2. Now let's get it into Minikube.

❌ I don't see rag-app

No worries! Let's create the Docker image first.

  • Make sure you're in your project directory (the one with your Dockerfile and app.py):

🍎 macOS

cd ~/Desktop/nextwork-rag-api

🖼️ Windows

cd ~\Desktop\nextwork-rag-api

Now let's switch to Minikube's Docker environment.

  • Run this command and press Enter:
eval $(minikube docker-env)

What does this command do?

eval $(minikube docker-env) sets environment variables that redirect your Docker CLI to use Minikube's Docker daemon instead of your host's Docker daemon.

💡 What is a Docker daemon?

The Docker daemon is the background service that actually runs Docker containers and manages Docker images. Think of it as the "engine" that powers Docker - when you run commands like docker build or docker run, your terminal (the Docker CLI) sends instructions to the Docker daemon, which then does the actual work.

Your computer has its own Docker daemon (running through Docker Desktop), and Minikube has its own separate Docker daemon running inside its virtual environment. This command switches your terminal to talk to Minikube's Docker daemon instead of your computer's Docker daemon - it's like switching which Docker "server" your terminal talks to!

After running this command:

  • docker build will build images inside Minikube
  • docker images will show Minikube's images
  • docker ps will show containers running inside Minikube

PS: This only affects the current terminal window. If you open a new terminal, it will use your host Docker again.

Verify You're Connected to Minikube's Docker

Let's make sure the command worked and you're actually connected to Minikube's Docker daemon.

  • Run this command and press Enter:
docker ps
  • Look for containers with names starting with k8s_ (like k8s_etcd, k8s_kube-proxy, etc.). These are Kubernetes containers running inside Minikube.

What should I see?

If you see containers with names like k8s_, you're successfully connected to Minikube's Docker daemon! Your terminal prompt might also show (minikube) if configured, but seeing the Kubernetes containers is the real confirmation.

Navigate to your project directory if you're not already there.

  • Run this command and press Enter:

🍎 macOS

cd ~/Desktop/nextwork-rag-api

🖼️ Windows

cd ~\Desktop\nextwork-rag-api

What does this command do?

cd changes your current directory to the project folder where your Dockerfile and application files are located on your local machine.

Now build your image in Minikube's Docker environment.

  • Run this command and press Enter:
docker build -t rag-app .

What does this command do?

docker build -t rag-app . builds a Docker image from your Dockerfile in the current directory (.), tagging it with the name rag-app.

You'll see the same build output as when you built the Docker image in your local environment, but now the image is stored in Minikube's environment!

This will take 2-3 minutes! Wait until you see a success message like:

Let's verify the image is in Minikube.

  • Run this command and press Enter:

🍎 macOS

docker images | grep rag-app

What does this command do?

This command lists all Docker images and filters to show only your rag-app image with grep. Since we're still connected to Minikube's Docker, this shows images inside Minikube.

🖼️ Windows

docker images | Select-String rag-app

What does this command do?

This command lists all Docker images and filters to show only your rag-app image with Select-String. Since we're still connected to Minikube's Docker, this shows images inside Minikube.

You should see rag-app in the list:

rag-app       latest    abc123def456    30 seconds ago    450MB

Awesome! Your Kubernetes cluster is ready.

You've successfully:

  • ✅ Started your local Kubernetes cluster
  • ✅ Loaded your Docker image into Minikube

This is amazing - you're running a real Kubernetes cluster on your machine! Now let's deploy your API to it and let Kubernetes take over managing your containers! 🚀

Create Kubernetes Deployment

Now let's create a Kubernetes Deployment that will run your container. This is where Kubernetes takes over managing your application!

What is a Kubernetes Deployment?

Think of it as a blueprint that tells Kubernetes how to run your app. Kubernetes will make sure your app always matches this blueprint - if a container crashes, Kubernetes automatically starts a new one. It:

  • Specifies which container image to run
  • Defines how many replicas (copies) to run
  • Handles rolling updates (updating without downtime)
  • Restarts containers if they crash
  • Scales your application up or down

💡 Extra for Experts: How do Deployments work?

When you create a Deployment, Kubernetes:

  1. Creates a ReplicaSet (which manages the actual pods)
  2. The ReplicaSet creates Pods (running containers)
  3. If a Pod crashes, the ReplicaSet creates a new one
  4. If you update the Deployment, Kubernetes performs a rolling update (gradually replacing old pods with new ones)

This is how production systems stay available even when things go wrong!

In this step, you're going to:

  • Create a Kubernetes Deployment

Create Kubernetes Deployment

Let's create a file called deployment.yaml.

  • In Cursor's file explorer (left sidebar), right-click in an empty area or on your project folder.
  • Select New File from the context menu.
  • Name the file deployment.yaml
  • Press Enter.
  • Click on the file to open it in the editor.
  • Copy this entire code block and paste it into the file:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rag-app-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rag-api
  template:
    metadata:
      labels:
        app: rag-api
    spec:
      containers:
      - name: rag-api-container
        image: rag-app
        imagePullPolicy: Never
        ports:
        - containerPort: 8000
        env:
        - name: OLLAMA_HOST
          value: "host.docker.internal:11434"
  • Save the file - press Cmd+S (macOS) or Ctrl+S (Windows)

What does this Deployment file do?

This file tells Kubernetes how to run your RAG API:

  • What to run - Your rag-app Docker image (the containerized RAG API you built earlier)
  • How many copies - 1 copy for now (in production, you'd run multiple copies for reliability)
  • How to find it - The app=rag-api label helps Kubernetes identify and manage your Pods
  • How to connect - The OLLAMA_HOST environment variable tells your RAG API where to find Ollama running on your computer

When you apply this file, Kubernetes will create a Pod (a running container) with your RAG API inside it. The Deployment keeps watching that Pod - if it crashes, Kubernetes automatically restarts it!

Click here for a line-by-line breakdown of the code block!

🙋‍♀️ Extra for Experts: Wait, why do we have both "rag-app" AND "rag-api"? What's the difference?

Great question! These similar names represent two completely different things:

rag-app = Your Docker image name:

  • This is the name you gave your Docker image when you built it: docker build -t rag-app
  • You created this in Project #2 or earlier in this project
  • It's stored in Docker and contains your actual application code
  • You can't change this without rebuilding your image

rag-api = The Kubernetes label that connects resources:

  • This is a tag that Kubernetes uses to group and find Pods
  • The Deployment uses selector: app=rag-api to find its Pods
  • The Service (coming next) will use this same label to find Pods to route traffic to
  • You can pick any label name you want - you could use rag-app if you want to, but the label name doesn't have to match your image name. We're intentionally picking rag-api so you can tell which labels are referencing your Docker image, and which are referencing the Kubernetes label.

Now let's tell Kubernetes to create your deployment.

  • In your terminal, run this command and press Enter:
kubectl apply -f deployment.yaml

What does this command do?

kubectl apply -f deployment.yaml tells Kubernetes to create or update resources defined in the deployment.yaml file. The -f flag specifies the file to read from.

What do you see?

✅ Deployment created

Perfect! You should see:

deployment.apps/rag-app-deployment created

Perfect! Kubernetes is now creating your Pod.

What is a Pod?

A Pod is the smallest deployable unit in Kubernetes. It's a group of one or more containers that share storage and network resources. In our case, each Pod will contain one container (your RAG API).

Think of a Pod like a "wrapper" around your container - Kubernetes doesn't run containers directly. Instead, it runs Pods, and Pods contain containers. This design allows Kubernetes to manage multiple containers together as a single unit, share networking and storage between them, and keep them on the same node.

When you create a Deployment (which we'll do next), Kubernetes will create Pods based on your Deployment's specifications. Each Pod runs your containerized RAG API, and Kubernetes manages these Pods - starting them, restarting them if they crash, and ensuring the right number are always running.

❌ I see an error

No worries, let's fix this together! Here are the most common issues and how to solve them:

Image pull errors

If you see ErrImagePull or ImagePullBackOff:

  • Make sure you built the image in Minikube's Docker environment (you ran eval $(minikube docker-env) and then docker build -t rag-app .).
  • Check the image exists in Minikube:
🍎 macOS
eval $(minikube docker-env)
docker images | grep rag-app
🖼️ Windows
& minikube docker-env | Invoke-Expression
docker images | Select-String "rag-app"
  • If it's not there, rebuild it:
docker build -t rag-app .
  • Make sure imagePullPolicy: Never is set in your deployment.yaml (so Kubernetes uses the local image).
Still having trouble with image pull errors?

Try troubleshooting your error here, while deploying my RAG API to Kubernetes, can you please suggest solutions that match my operating system?) or share your error message with the NextWork community!

Pod not starting

If the Pod shows Pending or CrashLoopBackOff, this means the Pod is not running successfully - it's either stuck waiting for resources, waiting for an image, or keeps crashing and restarting due to errors in your app or configuration.

  • Check the Pod logs:
kubectl logs [[POD_NAME="pod-name"]]
Tip

Run kubectl get pods to find your Pod's name!

  • Check Pod events:
kubectl describe pod [[POD_NAME="pod-name"]]

This shows detailed information about what's happening with the Pod.

  • Common issues:
    • Container crashes on startup (check logs)
    • Not enough resources (memory/CPU)
    • Image not found (see image pull errors above)
Still having trouble getting your Pod to start?

Try troubleshooting your error here,, can you please suggest solutions that match my operating system?) or share your error message with the NextWork community!

YAML syntax errors

If you see error validating data or YAML parsing errors:

  • Check your YAML syntax - indentation matters! Use 2 spaces (not tabs).
  • Make sure all required fields are present.
  • Need help with YAML rules?
  • Validate your YAML:
kubectl apply -f deployment.yaml --dry-run=client

This checks the YAML without actually creating the resource.

🙋‍♀️ Still having trouble with YAML syntax errors?

Try troubleshooting your error here, or share your error message with the NextWork community!

Other errors

If you see a different error:

  • Read the error message carefully - Kubernetes error messages are usually helpful.
  • Check the deployment status:
kubectl describe deployment rag-app-deployment
  • Check Pod events:
kubectl get events

This shows recent events in your cluster, which can help diagnose issues.

🙋‍♀️ Still having trouble creating your Kubernetes Deployment?

Try troubleshooting your error here, or share your error message with the NextWork community!

Let's check its status.

  • Run this command and press Enter:
kubectl get deployments

What does this command do?

kubectl get deployments lists all Deployments in your cluster, showing their status and how many replicas are running.

You should see output like:

NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
rag-app-deployment   1/1     1            1           30s

Nice! This tells us your Deployment is working perfectly!

What does each column mean?

  • NAME: Your Deployment's name
  • READY: Shows 1/1 (1 replica ready out of 1 desired). If you see 0/1, Kubernetes is still starting the Pod
  • UP-TO-DATE: Number of replicas updated to the latest configuration
  • AVAILABLE: Number of replicas currently available to serve traffic
  • AGE: How long the Deployment has been running

💡 Extra for Experts: What does "READY 1/1" mean?

The first number is how many replicas are ready, the second is how many you requested:

  • 1/1 means success!
  • 0/1 means Kubernetes is still starting your Pod
  • 0/3 would mean you requested 3 replicas but none are ready yet.

Now let's see the actual running container.

  • Run this command and press Enter:
kubectl get pods

What does this command do?

kubectl get pods lists all Pods in your cluster, showing their current status, how many containers are ready, and how many times they've restarted.

Remember: A Pod is the smallest deployable unit in Kubernetes - it's the wrapper that contains your container(s). Your Deployment created this Pod to run your RAG API container.

💡 What does each column mean?

  • NAME: Pod name (Deployment name + ReplicaSet ID + unique ID)
  • READY: 1/1 means 1 container ready out of 1 total in this Pod
  • STATUS: Current state - we want Running!
  • RESTARTS: How many times the container has restarted (0 is good!)
  • AGE: How long the Pod has been running

💡 What's the difference between Deployments and Pods?

Think of it this way:

  • Deployment = The manager that creates and maintains Pods. It's like a blueprint that says "I want 1 replica of my RAG API running at all times." If a Pod crashes, the Deployment automatically creates a new one to replace it.
  • Pod = The actual running container(s). This is where your application code executes. Pods are temporary - if they crash, they're gone.

When you run kubectl get deployments, you see the manager (rag-app-deployment). When you run kubectl get pods, you see what the manager created (the actual Pods). The Deployment ensures your desired state (1 replica) matches reality!

Awesome! Your Deployment is running.

Kubernetes is now managing your API - if a container crashes, it'll automatically restart it. Now let's create a Service so you can actually access your API! 🎉

Create Kubernetes Service

Your Deployment is running your RAG API in a Pod, and Kubernetes is managing it. But there's one problem: how do you actually access your RAG API from your laptop?

Right now, your Pod has an IP address, but that address changes every time the Pod restarts or gets recreated. This happens because Pods are ephemeral - they can be created, destroyed, and recreated by Kubernetes. If you tried to connect directly to the Pod's IP, your connection would break whenever Kubernetes restarts the Pod!

A Service solves this by providing a stable network endpoint for your Pods. The Service gives you a consistent way to reach your RAG API no matter what happens to the Pod - even if the Pod gets recreated with a new IP address!

But there's another challenge: How do you reach the Service from outside the Kubernetes cluster? Your Pods and Services run inside the cluster (on your node), but you're accessing them from your laptop. That's where NodePort comes in - it opens a special port on your node that routes traffic to your Service, making your API accessible from your computer!

How does a Service provide stable networking?

A Service provides:

  • A stable IP address that never changes
  • A DNS name (like rag-app-service) that always works
  • Load balancing across multiple Pods (if you have replicas > 1)
  • Automatic routing to healthy Pods

No matter how many times the Pods behind it restart, the Service always routes traffic to them correctly.

In this step, you're going to:

  • Create a Kubernetes Service
  • Verify your Service is running

Let's create a file called service.yaml.

  • In Cursor's file explorer (left sidebar), right-click in an empty area or on your project folder.
  • Select New File from the context menu.
  • Name the file service.yaml
  • Press Enter.
  • Click on the file to open it in the editor.
  • Copy this entire code block and paste it into the file:
apiVersion: v1
kind: Service
metadata:
  name: rag-app-service
spec:
  selector:
    app: rag-api
  ports:
  - port: 8000
    targetPort: 8000
  type: NodePort
  • Press Cmd + S (macOS) or Ctrl + S (Windows/Linux) to save the file.

What does this Service file do?

This file tells Kubernetes how to access your RAG API:

  • Which Pods to route to - The selector: app=rag-api finds all Pods with the label app=rag-api (MUST match your Deployment's Pod labels!)
  • Where to send traffic - The targetPort: 8000 routes incoming requests to port 8000 on your Pods (where your RAG API is listening)
  • How to expose it - The type: NodePort makes your Service accessible from outside the cluster
  • The Service port - The port: 8000 is the port the Service listens on internally

When you apply this file, Kubernetes creates a stable network endpoint for your RAG API. No matter how many times your Pods restart or get recreated with new IP addresses, you can always access your API through this Service!

💡 What is NodePort and why do we need it?

NodePort is a way to make your Service accessible from outside your Kubernetes cluster. Here's how it works:

When you set type: NodePort, Kubernetes automatically:

  1. Opens a special port on each node - Kubernetes picks a random port between 30000-32767 (like 30000, 30123, etc.) and opens it on every node in your cluster
  2. Routes traffic from that port to your Service - When someone connects to node-ip:30000 (or whatever port Kubernetes assigned), the node forwards that traffic to your Service on port 8000 (the Service port you defined in your service.yaml)
  3. The Service then routes to your Pods - Your Service receives the request and sends it to one of your Pods running the RAG API.

Think of it like this: If your node is the building, NodePort is the front door (port 30000), the Service is the receptionist who knows where to send visitors, and your Pod is the office where your API actually works!

Why NodePort instead of other Service types?

  • ClusterIP (default) - Only accessible from inside the cluster. Your laptop can't reach it!
  • NodePort - Opens a port on each node, making it accessible from outside. Perfect for local development with Minikube!
  • LoadBalancer - Requires cloud infrastructure (AWS, GCP, etc.). Not available in local Minikube.

NodePort is the simplest way to access your API from your laptop when using Minikube!

💡 How do NodePort, Nodes, and Services work together?

Here's the complete flow when you access your API:

  1. Your computer sends a request to the node's IP address on the NodePort (e.g., 192.168.49.2:30000 - your actual port will be different)
  2. The node receives the request on the NodePort (the random port Kubernetes assigned, like 30000) and forwards it to your Service on port 8000 (the Service port)
  3. The Service (rag-app-service) receives the request on port 8000 and uses its selector to find Pods with label app=rag-api
  4. The Service routes the request to one of those Pods (on port 8000, where your RAG API is listening)
  5. Your Pod's container processes the request and sends a response back through the same path

The key insight: NodePort opens the door on the node, Service knows where to send traffic, and Pods do the actual work!

Click here for a line-by-line breakdown of the code block!

Now let's tell Kubernetes to create your Service.

  • In your terminal, run this command and press Enter:
kubectl apply -f service.yaml

You should see:

service/rag-app-service created

What does this command do?

kubectl apply -f service.yaml tells Kubernetes to create or update the Service defined in the service.yaml file.

Perfect! Kubernetes is now creating your Service.

Let's check its status.

  • Run this command and press Enter:
kubectl get services

What does this command do?

kubectl get services lists all Services in your cluster, showing their type, cluster IP, external IP, and ports.

You should see output like:

NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
rag-app-service  NodePort    10.96.xxx.xxx   <none>        8000:3xxxx/TCP   30s
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          5m

The rag-app-service is the Service you just created for your RAG API, and the kubernetes service is the default Kubernetes API server that's always present in your cluster.

Nice! This tells us your Service is working perfectly!

What does each column mean?

  • NAME: Your Service's name
  • TYPE: NodePort means the Service is accessible from outside the cluster
  • CLUSTER-IP: The internal IP address of the Service (used by Pods within the cluster)
  • EXTERNAL-IP: For NodePort services, this shows <none> - Minikube will provide access through a different method
  • PORT(S): Shows 8000:3xxxx/TCP - port 8000 is the Service port, and 3xxxx is the NodePort (a random port between 30000-32767) that Minikube assigned
  • AGE: How long the Service has been running

💡 What does "8000:3xxxx/TCP" mean?

This shows the port mapping for your NodePort Service:

  • 8000 is the Service port (the port the Service listens on internally)
  • 3xxxx (like 30000, 30123, etc.) is the NodePort - the port that Kubernetes opened on your node
  • TCP is the protocol (Transmission Control Protocol)

Here's how the ports work together:

When you access your API, you'll connect to the NodePort (the 3xxxx number) on your node's IP address. The node receives your request on that NodePort and forwards it to your Service on port 8000. The Service then routes it to your Pod, which is listening on port 8000.

Think of it like this: - NodePort (3xxxx) = The front door of the building (node) - Service port (8000) = The reception desk inside - Pod port (8000) = The actual office where work happens

Your request goes: Front door (NodePort) → Reception (Service) → Office (Pod)!

Great! Your Service is created. Kubernetes is now routing traffic to your Pods.

Awesome! Your Service is created.

Your API is now accessible through a stable network endpoint. The Service will automatically route traffic to your Pods, even if they restart with new IP addresses.

Now let's test your API and see it in action! 🎉

Access Your API Through Kubernetes

Now let's access your API running in Kubernetes! This is the moment of truth - we'll see your containerized API running in an orchestration system.

In this step, you're going to:

  • Access your API using NodePort
  • Test your API running in Kubernetes

Access Your API Using NodePort

Let's get the URL to access your API.

  • In your terminal, run this command and press Enter:
minikube service rag-app-service --url

What does this command do?

minikube service rag-app-service --url creates a tunnel - a connection between your computer and your Kubernetes Service - and gives you the URL to access it. The --url flag returns just the URL without opening a browser.

Think of a tunnel like a bridge: since Minikube runs inside a Docker container (separate from your computer), it needs to create this tunnel so your requests can reach your API inside the cluster.

What do you see?

✅ I see a URL

Perfect! Your output will look different depending on which Minikube driver you're using. Find your output below:

🐳 Docker driver (http://127.0.0.1:...)

Most common on macOS/Windows

Your terminal shows:

http://127.0.0.1:54589
❗  Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
What is this URL?

This is the address you'll use to access your API! Let's break it down:

  • 127.0.0.1: This is your computer (localhost)
  • Port (e.g., 54589): Minikube picked this port number to connect you to your Service. The number will be different each time you run the command, but that's okay - just use whatever URL Minikube gives you!

⚠️ Important: Keep this terminal window open! Minikube is creating a tunnel from your localhost to the Service. If you close the terminal, the connection will break.

💻 VM driver (http://192.168...)

Your terminal shows:

http://192.168.49.2:30000
What does this URL mean?

This is a NodePort URL - let's break it down:

  • 192.168.49.2: This is your node's IP address (the machine running your Kubernetes cluster)
  • Port (e.g., 30000): This is the NodePort that Kubernetes assigned to your Service (a random port between 30000-32767)

Here's the complete journey your request takes:

  1. Your computer → Sends request to 192.168.49.2:30000 (node IP + NodePort - your actual port will be different)
  2. The node → Receives request on the NodePort (the random port Kubernetes assigned) and forwards it to your Service on port 8000
  3. Your Service (rag-app-service) → Receives the request on port 8000 and finds Pods with label app=rag-api
  4. Your Pod → Receives the request on port 8000 (where your RAG API is listening)
  5. Your container → Processes the request and generates a response
  6. Response travels back → Pod → Service → Node → Your computer

The node is the entry point (via NodePort), the Service is the traffic director, and the Pod does the actual work!

❌ Service not found error

If you see an error like:

❌ Exiting due to SVC_NOT_FOUND: Service 'rag-app-service' was not found in 'default' namespace.

This means Kubernetes can't find your Service. Let's check what happened:

  • First, verify your Service exists:
kubectl get services

You should see rag-app-service in the list. If you don't see it:

  • Make sure you created the Service in Step #4:
kubectl apply -f service.yaml
  • Verify it was created successfully:
kubectl get service rag-app-service

If you see NAME, TYPE, CLUSTER-IP, etc., the Service exists. Try the minikube service command again:

minikube service rag-app-service --url

Still having trouble finding your Service?

Try troubleshooting your error here, or share your error message with the NextWork community!

When I ask a question to my API, how will the request get to the right places?

Let's trace the complete journey of your request through the Kubernetes cluster:

  1. You run a curl command → This initiates a POST request to http://192.168.49.2:30000 (node IP + NodePort - your actual port will be different, assigned randomly by Kubernetes)
  2. Your computer's network → Routes the request to 192.168.49.2:30000 (the node's IP address on the NodePort)
  3. The node → Intercepts the request on port 30000 (the NodePort from step 1). The node knows "port 30000 goes to rag-app-service" and forwards the request to your Service on port 8000
  4. The Kubernetes Service (rag-app-service) → Accepts the request on port 8000 (from step 3). The Service uses its selector (app=rag-api) to find matching Pods and routes the request to one of them
  5. Your Pod → Accepts the request from the Service (from step 4). If you had multiple replicas, the Service would load balance between them
  6. Your container (inside the Pod) → Accepts the request on port 8000 (from step 5) where your RAG API is listening
  7. Your RAG API → Processes the request:
    • Searches Chroma for relevant context
    • Sends the question and context to Ollama
    • Generates an AI response
  8. The response → Travels back through the same path: Container → Pod → Service → Node (via NodePort) → Your computer → Your terminal

Key takeaway: The node provides the entry point (NodePort), the Service knows where to route traffic, and your Pod does the actual work. This is why we need all three components working together!

Kubernetes is managing your Pod - if it crashes, Kubernetes will automatically restart it. If you scale to multiple replicas, the Service will load balance between them.

Test Your API

Now let's test your API is working.

Since your first terminal is keeping the Minikube tunnel open, you'll need to open a new terminal window to test your API.

  • Select the New Terminal button at the top of your terminal window.

A new terminal will open up beneath your existing terminal.

  • Replace your-api-url in the command below with YOUR actual URL from the previous step.
  • Run the command and press Enter:

🍎 macOS

curl -X POST "[[API_URL="your-api-url"]]/query" -G --data-urlencode "q=What is Kubernetes?"

What does this command do?

This command sends an HTTP request to your RAG API running in Kubernetes:

  • curl - Makes HTTP requests from the command line
  • -X POST - Specifies this is a POST request
  • your-api-url/query - The endpoint where your API accepts questions
  • -G - Converts the POST to a GET request with query parameters
  • --data-urlencode "q=What is Kubernetes?" - Safely encodes your question as a URL parameter

The API receives your question, searches its knowledge base (the k8s.txt file), sends the question and context to Ollama, and returns an AI-generated answer!

🖼️ Windows

Invoke-RestMethod -Uri "[[API_URL="your-api-url"]]/query?q=What is Kubernetes?" -Method POST

What does this command do?

This command sends an HTTP request to your RAG API running in Kubernetes:

  • Invoke-RestMethod - PowerShell's built-in command for making HTTP requests
  • -Uri - Specifies the URL endpoint where your API accepts questions
  • /query?q=What is Kubernetes? - The API endpoint with your question as a URL parameter
  • -Method POST - Specifies this is a POST request

The API receives your question, searches its knowledge base (the k8s.txt file), sends the question and context to Ollama, and returns an AI-generated answer!

You should see a response like:

What do you see?

✅ I got a JSON response

Perfect! You should see a JSON response like:

{"answer":"Kubernetes is a container orchestration platform that helps manage containers at scale..."}

❌ I got an error or no response

No worries! Let's troubleshoot and get your API working:

Connection refused

If you see Connection refused, this means your request reached the Minikube node, but Kubernetes could not route your traffic to a running Pod - usually because the Pod isn't running, is still starting up, or the Service isn't set up correctly.

  • Make sure the Pod is running:
kubectl get pods

The Pod should show Running. If it's Pending or CrashLoopBackOff, check the Pod logs:

kubectl logs [[POD_NAME="pod-name"]]
Tip

Run kubectl get pods to find your Pod's name!

  • Make sure the Service exists:
kubectl get services

You should see rag-app-service listed.

Still having connection issues accessing your API?

Try troubleshooting your error here, or share your error message with the NextWork community!

Request timeout

If the request times out, it means your request reached the Service, but no response was received from the API within the expected time! This is usually because the Pod is not ready, is starting slowly, or the API app is not running properly inside the Pod.

  • Check if the Pod is ready:
kubectl get pods

Look for 1/1 in the READY column. If it shows 0/1, the Pod isn't ready yet - wait a bit.

  • Check Pod logs for errors:
kubectl logs [[POD_NAME="pod-name"]]
  • Make sure Ollama is running on your host (the Pod needs to connect to it).
Still experiencing timeouts when accessing your API?

Try troubleshooting your error here, or share your error message with the NextWork community!

Ollama connection error

If you see errors about connecting to Ollama (most common issue!):

Why is Ollama connectivity tricky?

Your Pod runs inside Minikube's Docker environment, which is isolated from your host. The environment variable OLLAMA_HOST=host.docker.internal:11434 tells the Pod how to reach your host machine where Ollama is running. If this doesn't work, your API can start but fails when trying to generate answers.

  • First, verify Ollama is running on your host:
curl http://localhost:11434

You should see Ollama is running. If not, start Ollama.

  • Check the Pod logs for Ollama connection errors:
kubectl logs [[POD_NAME="pod-name"]]
Tip

Run kubectl get pods to find your Pod's name!

Look for errors like Connection refused or Failed to connect to Ollama.

  • Test Ollama connectivity from inside the Pod:
kubectl exec -it [[POD_NAME="pod-name"]] -- curl http://host.docker.internal:11434

If this fails, host.docker.internal isn't resolving properly. This can happen on some systems.

host.docker.internal doesn't work!

If you can't reach Ollama from the Pod:

  • Option 1 (macOS/Windows): Make sure Docker Desktop is configured to allow host access. In Docker Desktop → Settings → Resources → Network, ensure "Allow the default Docker socket to be used" is enabled.
  • Option 2 (Linux or if Option 1 fails): Find your host's IP on the Docker network:Use that IP (e.g., 172.17.0.1:11434) instead of host.docker.internal:11434 in your deployment.yaml, then apply the changes:
  • Option 3 (Debug mode): Run Ollama in the cluster itself (advanced - not recommended for this project but possible in production).
ip addr show docker0 | grep inet
kubectl apply -f deployment.yaml
kubectl rollout restart deployment/rag-app-deployment

🙋‍♀️ Still having trouble connecting to Ollama from your Kubernetes Pod?

Try troubleshooting your error here, or share your error message with the NextWork community!

Other errors

  • Check the Pod logs for error messages:
Tip

Run kubectl get pods to find your Pod's name!

kubectl logs [[POD_NAME="pod-name"]]
  • Check Service endpoints:
kubectl get endpoints rag-app-service
What are Service endpoints?

Service endpoints are the internal IP addresses of Pods that a Service routes traffic to. When you run kubectl get endpoints rag-app-service, it shows the list of matching Pods (their IPs and ports) that the Service knows about.

  • If endpoints are listed, your Service has found Pods labeled app=rag-api and is ready to route requests.
  • If the output is empty, the Service can't find any matching Pods. This usually means:
    • The Pods aren't running or are still starting up,
    • The label selector in your Service doesn't match any Pod labels,
    • Or the Pods are not Ready (e.g., failing health checks).

In summary: endpoints connect Services to healthy, ready Pods. If you see no endpoints, get help with label selectors!

  • Describe the Service for more details:
kubectl describe service rag-app-service
Still having trouble accessing your API through Kubernetes?

Try troubleshooting your error here, or share your error message with the NextWork community!

Your API is running in Kubernetes! 🎊

This is huge - you just deployed a containerized application to Kubernetes, tested it through a Service, and got AI-generated responses. Kubernetes handles networking, routing, and container management automatically - all the complexity is abstracted away so you can focus on your application!

<!--

Extra for Experts: Use port-forward for easier access

NodePort works, but there's an easier way for local development...

  • Run this command and press Enter:
kubectl port-forward service/rag-app-service 8000:8000

What does this command do?

kubectl port-forward creates a secure tunnel from your local machine (localhost:8000) directly to the Service (port 8000). This lets you access your Kubernetes Service as if it were running locally on your computer.

  • Keep this terminal window open - the port-forward must stay active!

Port-forward keeps disconnecting?

If your kubectl port-forward keeps dropping the connection, get troubleshooting help here.

  • Now you can access your API at http://localhost:8000 just like before, but it's running in Kubernetes!
  • Open a NEW terminal window (keep the other one open)!
  • In your new terminal, run this command and press Enter:

🍎 macOS

curl -X POST "http://127.0.0.1:8000/query" -G --data-urlencode "q=What is Kubernetes?"

What does this command do?

This command sends a request to localhost:8000, which kubectl port-forward is forwarding to your Kubernetes Service. It's the same API call as before, just using a different access method!

🖼️ Windows

Invoke-RestMethod -Uri "http://127.0.0.1:8000/query?q=What is Kubernetes?" -Method POST

What does this command do?

This command sends a request to localhost:8000, which kubectl port-forward is forwarding to your Kubernetes Service. It's the same API call as before, just using a different access method!

You should get the same JSON response - but now your API is running in Kubernetes with automatic management, scaling, and recovery capabilities! Nice work 👏

Awesome! Your API is accessible through Kubernetes.

Great work! You've successfully:

  • ✅ Accessed your API using NodePort
  • ✅ Tested your API running in Kubernetes
  • ✅ Used port-forward for easier access

Your containerized API is now running in an orchestration system! 🎉

-->

Secret mission

Ready to see Kubernetes' superpower in action? You're about to witness self-healing in action. See what happens when you delete your pod - and watch Kubernetes automatically bring it back!

What is self-healing?

When you run Docker, if a container crashes, it stays dead until you manually restart it. You have to notice the problem, SSH into the server, and run commands to bring it back up.

Kubernetes is different - if a pod crashes or is deleted, Kubernetes automatically creates a new one to match your desired state. No human intervention needed. This is what keeps production systems running 24/7!

💎 In this secret mission, you're going to:

  • Delete the pod and watch what happens
  • See Kubernetes automatically create a replacement
  • Test that your API stayed available throughout

Watch Kubernetes Heal Itself!

Delete your resources

Delete your resources

Heads up! Did you know this is Project #3 of a 4-part DevOps x AI series? 🎉

Project #4 will pick up right where you left off - building on the same Kubernetes deployment you just created! We highly recommend keeping everything if you plan to continue the series (it's free) - you'll save time and can jump straight into the next project.

Choose your path:

🚀 Bring on Project #4!

Awesome! You're ready to continue the series. See you in the next one 💪

🛑 I'm done for now

No problem! Let's clean up your local resources to free up space on your computer.

Resources to delete:

  • Stop and remove the Kubernetes deployment and service
  • Stop Minikube cluster
  • Remove Docker images

Delete Kubernetes Deployment and Service

Let's delete the deployment.

  • Run this command and press Enter:
kubectl delete deployment rag-app-deployment
  • Now delete the service.
  • Run this command and press Enter:
kubectl delete service rag-app-service
  • Let's verify they're deleted. Run these commands and press Enter:
kubectl get pods
kubectl get services

Both commands should show no resources.

Stop Minikube Cluster

Let's stop the Minikube cluster.

  • Run this command and press Enter:
minikube stop
  • Optionally, you can delete the cluster completely (removes all data).
  • Run this command and press Enter:
minikube delete

What's the difference?

  • minikube stop - Pauses the cluster but keeps all data. You can restart it later with minikube start.
  • minikube delete - Completely removes the cluster and all its data. You'll need to run minikube start again to recreate it.

Remove Docker Images

Let's list your images to see what you have.

  • Run this command and press Enter:
docker images
  • Now remove the rag-app image.
  • Run this command and press Enter:
docker rmi [[DOCKER_IMAGE_NAME="rag-app"]]
  • Optionally, remove all unused images.
  • Run this command and press Enter:
docker image prune -a

Warning

This removes all unused images, not just rag-app. Only do this if you're sure you don't need other images!

💡 Changed your mind?

If you decide to continue the series later, you'll need to rebuild your Docker image and redeploy to Kubernetes. Don't worry - you've done it once, so you can do it again! The next project will guide you through any setup needed.

Well done!

Well done!

Nice work! ☸️ You've just deployed your containerized RAG API to Kubernetes.

You've learned how to:

  • Set up a local Kubernetes cluster with Minikube
  • Create Kubernetes Deployments to orchestrate containers
  • Create Kubernetes Services for stable networking
  • Deploy and access containerized applications in Kubernetes

Ready to quiz yourself? 💪

What's Next in the DevOps × AI Series

You've built the foundation - a RAG API that's containerized and orchestrated! Next up...

  • 🔄 Project #4: Automate Deployments with GitHub Actions When your knowledge base changes, how do you rebuild and redeploy automatically? Project #4 adds CI/CD pipelines to automate the entire build → test → deploy workflow.

p.s. Does it say "Still tasks to complete!" at the bottom of the screen?

This means you still have screenshots left to upload, or questions left to answer!

  1. Press Ctrl+F (Windows) or Command+F (Mac) on your keyboard.
  2. Search for the text Return to later.
  3. Jump straight to your incomplete tasks!
  4. 🙋‍♀️ Still stuck? Ask the community!