Building Kubernetes (a lite version) from scratch in Go
Nov 11, 2025
Host:
- Bart Farrell
This episode is sponsored by StormForge by CloudBolt — automatically rightsize your Kubernetes workloads with ML-powered optimization
Festus Owumi walks through his project of building a lightweight version of Kubernetes in Go. He removed etcd (replacing it with in-memory storage), skipped containers entirely, dropped authentication, and focused purely on the control plane mechanics. Through this process, he demonstrates how the reconciliation loop, API server concurrency handling, and scheduling logic actually work at their most basic level.
You will learn:
How the reconciliation loop works - The core concept of desired state vs current state that drives all Kubernetes operations
Why the API server is the gateway to etcd - How Kubernetes prevents race conditions using optimistic concurrency control and why centralized validation matters
What the scheduler actually does - Beyond simple round-robin assignment, understanding node affinity, resource requirements, and the complex scoring algorithms that determine pod placement
The complete pod lifecycle - Step-by-step walkthrough from kubectl command to running pod, showing how independent components work together like an orchestra
Relevant links
Transcription
Bart: Kubernetes down to its core, remove etcd, drop authentication, skip containers entirely, and try to rebuild it yourself? In this episode of KubeFM, I got a chance to speak with Festus Owumi, an engineer at Plexe AI, who decided to do exactly that. He built what he calls a light version of Kubernetes to understand the control plane from first principles: how the API server handles concurrency, how scheduling logic really works, and why the reconciliation loop is the heart of the entire system.
If you've ever wanted to understand Kubernetes beyond the abstractions, this conversation breaks it down piece by piece—clear, elegant, and surprisingly accessible for anyone working deep in the stack. This episode is sponsored by StormForge by CloudBolt. Automatically right-size your Kubernetes workloads with ML-powered optimization. Now, let's get into the episode with Festus. Festus, welcome to KubeFM. Which three emerging Kubernetes tools are you keeping an eye on?
Owumi: That's an interesting question because I just saw a couple of tools released over the last few days that are quite interesting to share.
The first one is KubeIntelect, which is incredibly fresh and published by the Department of Electrical and Electronics of FPT. KubeIntelect is an LLM-powered multi-agent orchestration system that allows you to operate Kubernetes using natural language—imagine talking to the control plane and creating pods.
The second tool I'm looking forward to is Skypilot, a Kubernetes-native scheduler that allows you to optimize GPU use.
The last tool is KubeGuard, which focuses on LLM-assisted Kubernetes security. Beyond statistical scanning, it analyzes your manifests and real-time logs to suggest least privilege configurations and helps harden cluster security.
Those are the three tools I'm really interested in.
Bart: Fantastic. And Festus, for people who don't know you, can you tell me more about what you do and where you work?
Owumi: I'm Festus, and I work at Plexe AI. We are building machine learning agents to automate the entire lifecycle of machine learning development—from data tasks down to the model. I usually work on scaling the ML platform. Using natural language, you can build a model from start to end, which is really cool work.
Bart: And how did you get into Cloud Native? Tell me about your journey.
Owumi: I have a background in core application development, writing Python and Golang backends, and frontend Flutter. I'm essentially a full-stack developer. At one point, I had to deploy an app to Cloud Run, which was how I got into Cloud Native. I found it fascinating and wanted to explore more. I went down the rabbit hole of cloud platforms like AWS, Azure, and GCP, and learned tools such as Kubernetes and Helm. That first deployment was my initial step into the cloud native ecosystem.
Bart: I noticed that the provided transcript is just "Okay." which doesn't provide much context for hyperlinking. Could you provide more of the transcript or context about the conversation? Without more details, I cannot meaningfully apply the hyperlinking guidelines.
Owumi: I noticed that the transcript you provided is incomplete. Could you share the full transcript so I can apply the hyperlinking guidelines effectively? Without the complete context, I cannot confidently identify terms to link or apply the specified editing approach.
Bart: The cloud native and Kubernetes ecosystem moves very quickly. How do you stay up to date? What resources work best for you?
Owumi: Twitter is one platform where you can connect with many Kubernetes contributors. Everybody loves to share knowledge on Twitter, so it's a great way to connect and consume information. YouTube is another resource - my algorithm now suggests the best videos, especially Kubernetes-related content.
Getting into the ecosystem is crucial. It's a big open-source community with many tools. I recently discovered some new tools just a few days ago. By getting involved and contributing to open-source projects, you can stay current with the latest developments in the Kubernetes ecosystem.
Bart: If you could go back in time and share one career tip with your younger self, what would it be?
Owumi: Post your work online. Don't just work in isolation or stay in a rabbit hole. Post your projects and do really hard, complex projects that stretch you. Share them online, and you never know who might take a look at it.
Bart: All right. As part of our monthly content discovery, we found an article you wrote titled "Building Kubernetes, a Light Version from Scratch in Go". We want to dive into this topic in more detail. You decided to build your own version of Kubernetes from scratch in Go, which honestly seems like a pretty wild project. What inspired you to take on this challenge, and what did you hope to learn from it?
Owumi: I personally come from the school of thought that building things from scratch really helps you understand the idea behind systems. You can take that information to do more complex things. For example, the whole evolution of LLMs was based on the "Attention Is All You Need" paper. Understanding things from scratch helps you do greater things in the future.
I've been using Kubernetes for a while, and I wanted to see how it actually works underneath the hood. After doing the project, I realized it was actually more simple than I thought. I felt compelled to share this with the world because people perceive Kubernetes as having a steep learning curve. I personally don't believe that. Once you break it down to first principles, you can see the basic mechanism that controls how it works. Understanding this simplifies your comprehension of everything layered on top of it.
Bart: Before we get into the technical details, can you help our listeners understand what Kubernetes actually does at its core? I ask this because you mentioned a reconciliation loop, and I want to know more about that.
Owumi: Kubernetes is a continuous orchestration software, but at its core, you should think about it like this: you have a system with a desired state and a current state. Kubernetes takes the current state and moves it to the desired state.
An example to illustrate this is a thermostat. In a room with a certain temperature, you set a target temperature, and the thermostat works to ensure the room reaches that specific temperature. This is exactly how Kubernetes works internally.
You specify the number of pods and deployments you want, and Kubernetes initiates events to make that configuration a reality.
Bart: And when you say you built a light version, what exactly did you strip away from the real Kubernetes to make this manageable? What are the key simplifications that you made?
Owumi: When I wanted to use a light version, I aimed to strip it down so people could understand things from first principles, without making it too different from how Kubernetes actually works.
First, I removed etcd, which is the distributed key-value store in Kubernetes, and replaced it with an in-memory store. This was because etcd involves complex distributed consensus mechanisms and high availability requirements. I wanted people to run the project on their local laptop without worrying about those complexities. The in-memory store is not persistent—if you close the terminal, the data is gone, whereas actual etcd is persistent.
I also skipped containers entirely. Instead, we had state logging, which allowed us to focus on understanding the pod lifecycle without dealing with container runtimes or image pulling.
Additionally, I removed authentication, service discovery, and RBAC. The system became simple—you could deploy pods without any permissions, just to understand the core concepts.
For the API server, instead of using watches and streams, the components simply poll the API server. The only resource definition possible is the pod, which helps people understand its lifecycle through the system.
Bart: Now let's talk more about the architecture. Kubernetes has this control plane concept that manages everything. How did you approach building your own control plane components?
Owumi: So, just like Kubernetes, I mentioned differences between the control plane and the data plane. In the control plane, we have the API server, control manager, kube scheduler, and etcd. On the data plane, we have the kubelets, which run on the nodes. We have the control plane and worker nodes, and the kubelet actually runs on every worker node.
One thing I really wanted was to create something very different from this. In the article and code, I have the API server, kube scheduler, an in-memory etcd, and kubelet running on the node. The only thing omitted is the control manager, which handles sophisticated control mechanisms for deployments, node health, and jobs. Since we're focusing on pods, it makes sense to strip that out.
The architecture remains the same: in the control plane, you have your API server, scheduler, and etcd. On the data plane, you have the kubelet running on your worker nodes.
Bart: The API server is often called the heart of Kubernetes. What challenges did you face when implementing your own API server, and what surprised you about how it works?
Owumi: One thing that was really surprising about the API server was how Kubernetes manages race conditions. Race conditions occur when you're trying to put two or more requests and update a Kubernetes resource concurrently. Kubernetes manages this using optimistic concurrency control, which looks at the resource version of each definition and compares versions to prevent conflicts.
This led me to understand the architecture of why Kubernetes has the API server as the gateway to etcd. Before accessing etcd, you must go through the API server. If the API and other components could access etcd directly—for example, if the kubelet could submit information directly—it would create a duplication of logic.
The API server performs critical functions like validation, ensuring that resources are being created correctly. If these functions were scattered across different components, we would have redundant logic. Having the API server as a gate layer to etcd ensures that it becomes the central source of truth for retrieving information.
What surprised me is how Kubernetes manages these complexities in one place, effectively handling race conditions and serving as the single source of truth for distributing information from etcd to other components that need it.
Bart: One of the most interesting parts of Kubernetes is the scheduler. It decides which containers run where. How did you approach building a scheduler, and what did you learn about how scheduling actually works?
Owumi: For my Kubernetes Lite version, I looked at how the scheduler works. It actually uses a complicated algorithm, but in my article, I specifically focused on a round-robin approach.
The Kubernetes scheduler identifies a pod that needs to be assigned to a node and decides how to assign it. My approach was a round-robin method where I have a list of nodes and assign pods in order: first to node A, then node B, then node C.
However, the actual scheduling process is much more complex. There are many factors the scheduler considers that my simple approach didn't account for. For example, does the node have the necessary memory and CPU requirements for the pod? The actual scheduler looks into details like node affinity, pod affinity, and deployment policies.
Some pods can be configured to deploy only on specific nodes or to avoid certain nodes. The scheduler evaluates all these defined conditions to determine pod placement.
Interestingly, after applying scoring models based on policies, affinities, and taints, there are times when two candidate nodes meet all criteria. In such cases, the scheduler uses a round-robin approach to distribute the load across those nodes. So my round-robin method wasn't entirely out of place—it's actually similar to the final layer Kubernetes uses to decide pod scheduling.
Bart: Kubelet is what actually runs on each node and manages containers. Since you're not running real containers, how did you simulate this and what insights did you gain about what the kubelet actually does?
Owumi: Since I'm not simulating this or using the container runtime, I'm logging states. Typically, you have a lifecycle of running, terminated, and deleted. I'm logging this so the user can understand what is happening.
This helped me appreciate how much the kubelet actually does. When the kubelet receives the pod spec from the API server, it uses the container runtime to build and run the container. It also handles liveness and readiness probes, checking if the container is running successfully and reporting any issues back to the API server.
There's significant complexity involved. By showing the state object and following the reconciliation loop, you can see a series of events that ensure you get what you want. You can observe the pod's progression: running, terminated, or encountering issues like image pulling problems. Seeing these state updates helps you understand the pod's lifecycle and the kubelet's role in moving pods through different stages.
Bart: You implemented an in-memory store instead of using etcd like real Kubernetes. What trade-offs did this involve and what did it teach you about why Kubernetes uses a distributed key-value store?
Owumi: I implemented an in-memory key-value store. The reason for this is that Kubernetes uses etcd, and I wanted users to be able to run this on a local machine without any external dependencies. Etcd needs to be persistent, whereas an in-memory store means that if a node or cluster crashes, the data is lost. This was a trade-off I made to avoid external persistence.
Regarding distributed consensus mechanisms, etcd is a distributed database that uses algorithms like Raft to determine the state of data when a node fails. Across active nodes, Raft helps decide the final state of the data. This would have been too complex to deploy, so since the database was running in memory, it didn't need to be highly available or distributed.
These trade-offs allow for an easily set-up system without external dependencies or node persistence. It still demonstrates that Kubernetes maintains a persistent layer in the background.
Bart: Now let's walk through the lifecycle. When you create a pod in your system, what actually happens step by step? And what was surprising about implementing this end-to-end flow?
Owumi: In the system, I created a kubectl lite, a simple CLI tool. When designing the entire system, I considered the API server, etcd, and kubelet. I wanted to understand how clients would talk to the API server for deploying pods, mirroring real-life cloud development where kubectl is a favorite tool.
On the platform, when you use kubectl lite to create a pod in a specific namespace with a given image, an API client inside kubectl lite communicates with the API server. The API server receives the request, validates it, and marks the pod as pending.
The scheduler then pulls the API server, looking for pending pods that haven't been scheduled. Using a round-robin approach (similar to Kubernetes' complex algorithm), it decides which node the pod should go to. The kubelet pulls the API server to identify pods assigned to its nodes and runs the pod's definition.
The lifecycle goes from pending to scheduled, then the kubelet marks it as running. When deleting a pod, it's marked as deleted on the API server and stored in etcd using the Raft consensus algorithm, and the kubelet pulls this information to clean up.
Implementing this helped me appreciate the complexity behind a single kubectl command. It's an event-driven mechanism where different independent systems work together like an orchestra to accomplish a task. Seeing how one change could be implemented and used by different systems to move a pod through its stages was truly fascinating.
Bart: I know your curiosity doesn't just start and stop with Kubernetes because you also built a kubectl-like CLI tool.
Owumi: The provided transcript contains only the word "Yeah", which appears to be insufficient for meaningful analysis or hyperlinking. Could you provide the full context or surrounding text from the transcript?
Bart: How important was this for testing and understanding your system, and what challenges did building a CLI present?
Owumi: This was very important for testing the system. I didn't want a case where people have to use Postman and call the API server directly. I felt that wouldn't do justice to how you actually integrate or work with Kubernetes in production. Typically, we use kubectl to get information.
I decided to build CLI tools. When building them from a user experience perspective, I came to understand and appreciate how kubectl is designed. The goal is to create a UX that reads almost like English: kubectl create, kubectl delete.
With kubectl lite, you can just do kubectl create, define what you want to create, and specify the namespace. It almost reads like a natural language command: "kubectl lite, I want to create a pod in this namespace."
Getting the UX right means developers don't always have to look up commands. Once you have a mental model of how the UX works—using namespace flags and images—it becomes easier for developers to understand.
The whole idea of the project is to create a simple system that isn't far from actual Kubernetes, allowing users to draw parallels and understand things from first principles.
Bart: What was the most challenging part of this entire project? Was there anything that seemed simple in theory, but turned out to be surprisingly complex in practice?
Note: Since this is a direct transcript question without specific technical terms that warrant immediate linking, I'll keep the text as-is. However, given the context of the discussion about building Kubernetes, I would suggest a potential link to Building Kubernetes, a Light Version from Scratch in Go which appears to be the article referenced in the discussion.
Owumi: The most challenging part of this project was stripping down the details. When I looked at Kubernetes, I saw a whole lot of components, complicated scheduling, and the kubelet running the container runtime on the node. My goal was to communicate that when you strip Kubernetes down to its first principles, it's not complex. Once you understand this, it becomes a perfect beginning layer where you can start layering things on top.
Stripping down those details and keeping the essence was the most complex part. Things that seem simple in theory, like the scheduler, are more intricate than they appear. You might think the scheduler just decides where a pod should run, but when I began to understand the complex algorithms involved, it became more nuanced.
The scheduler has to consider multiple factors: node affinities, pod taints, policies, and resource usage. It must evaluate what the pod is doing, what resources it requires, and whether the node can actually accommodate it. I ultimately simplified this to a round-robin approach, which still conveyed the different types of algorithms the scheduler uses in deciding pod placement.
Bart: On the flip side, what aspects of Kubernetes turned out to be simpler or more elegant than you expected once you stripped away the production complexity?
Owumi: The reconciliation loop is a simple way to understand Kubernetes. Instead of seeing it as a complex container orchestration management system, it's about desiring a certain state for your system and having Kubernetes perform events to achieve that state.
Once I stripped it down to this core concept, it made sense. I could understand the flow and lifecycle of events, what the kubelet does, and the roles of different components like the API server. As a cloud developer, this helped me debug more effectively because I now know exactly where to look when a component has an issue.
Bart: How did this project change your understanding of Kubernetes? And do you feel differently about using production Kubernetes now that you've built your own version?
Owumi: Kubernetes becomes more approachable when you break it down to its fundamental components. Once you uncover a complex system and realize it's actually simple, you become more confident. With Kubernetes, once you strip out complex details and focus on first principles, it isn't as scary as you think. You can better understand and visualize the flow of information and events. That's what I wanted to communicate in the article: Kubernetes becomes more approachable when you break it down to its fundamental components.
Bart: For someone interested in diving deeper into how Kubernetes works, what would you recommend? Should they try building their own version as well?
Owumi: Kubernetes is a complex system that can be better understood by building your own version. Many tools like Git, Redis should be explored by building your own version—not just to understand the details. Once you understand, using those tools becomes easier.
It also gives you ideas because a lot of engineering was involved in building these tools. Many engineers made intelligent decisions and architectures that you can apply to future problems. Build your own Kubernetes from scratch.
Do not be scared to explore the source code of Kubernetes or other tools you use. It was written by humans, after all. Rather than overwhelming yourself with complexity, start by stripping things down to first principles and then build up from there. As long as you can break complex systems down to their fundamental components and gradually reconstruct them, you'll develop a deeper understanding.
Bart: Looking back on this project, what's your biggest takeaway? And what would you want other developers to understand about the tools we use every day?
Owumi: My biggest takeaway is that the tools we have today, the big and complex tools, are really built on simple concepts. It's about applying a layer of complexity on top of them. We should not see these tools as alien technology, as that creates fear to contribute or get started.
There are many tools with open, simple issues. People often try to tackle the entire complexity at once, but the key is to start with the basics and build up gradually. You'll then be able to do really nice things. These tools we use every day were built by developers like us, and they're written in English.
The approach is to start from first principles, try building your own version. It doesn't have to be an exact replica—strip out the details, understand the core, and then begin to add layers of complexity on top of it.
Bart: I noticed that the provided transcript is incomplete and appears to be a meta-comment about transcription improvement, rather than an actual transcript from the interview. Could you provide the full transcript so I can apply the hyperlinking and editing guidelines effectively?
Without the complete context, I cannot generate a meaningful answer with hyperlinks. I would need:
The full transcript text
Confirmation of the speakers
The specific context of the discussion
If you can provide the complete transcript, I'll be happy to help you enhance it with appropriate hyperlinks and editing.
Owumi: I have quite a few from-scratch articles on complex concepts. Expect several new articles coming up in the future.
Bart: Very good. If people want to get in touch with you to work on a project together, what's the best way to do that? Plexe AI
Owumi: I'm Owumi Festus on LinkedIn (O-W-U-M-I-F-E-S-T-U-S). I'm @_enfinity on Twitter, and my GitHub is available in the article. You can raise issues and add PRs to the project.
One good approach would be to use this as a first-principles layer and then extend some parts, building on that understanding. We could potentially implement more complex scheduling in the scheduler or more complex validation in the API server. Feel free to contribute.
Bart: Awesome! Looking forward to seeing all the cool stuff we'll be doing in the future. Thanks so much for sharing your time and knowledge with us today. Take care and have a great day.
Owumi: The provided transcript contains only the word "Yeah", which appears to be insufficient for meaningful analysis or hyperlinking. Could you provide the full context or surrounding text from the transcript?
Bart: Cheers.
