Sitemap
Learnkali

Learnkali

Getting Started with Cilium: My First Hands-On Experience with eBPF and Kubernetes Networking

8 min readOct 13, 2026

--

I recently started getting hands-on with Cilium, and I wanted to document what I learned from the first couple of labs.

Before this, I had worked with Kubernetes networking at a more conceptual level, but Cilium was interesting because it approaches networking and security using eBPF rather than relying primarily on traditional iptables-based networking. Cilium uses eBPF extensively to implement networking, load balancing, and policy enforcement in the Linux kernel. Depending on the configuration and environment, other networking mechanisms may still be involved.

My goal in this first exercise was fairly simple:

  • Get Cilium running on a Kubernetes cluster
  • Understand its main components
  • See where eBPF fits into the picture
  • Create some network policies
  • Test what happens when traffic is allowed or blocked
  • Get a basic understanding of L7 and DNS-based policies

I used a Kind cluster for the lab so that everything could run locally.

Setting up the Kubernetes cluster

I started by creating a small multi-node Kind cluster.

The cluster consisted of one control-plane node and two worker nodes:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

I created it using the command:

kind create cluster --config kind-config.yaml --name cilium-lab

Using multiple nodes was useful because Cilium runs an agent on each node. It made it easier to understand that Cilium isn’t just a single component sitting somewhere inside the cluster.

Once the cluster was ready, I installed the Cilium CLI and used it to install Cilium.

Installing Cilium

For the lab, I enabled Hubble along with Cilium:

cilium install \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true

After installation, the first thing I checked was:

Press enter or click to view image in full size
Checking cilium status after installation

Seeing the main components come up successfully was a good first checkpoint.

The installation included components such as the Cilium Agent, Cilium Operator, Envoy, and Hubble Relay.

At this point, I wanted to understand what each of these components actually does rather than just treating them as Kubernetes pods that happened to be running.

Understanding the Cilium Agent and Operator

One of the first things that became clear was the difference between the Cilium Agent and the Cilium Operator.

The Cilium Agent runs on every node as a DaemonSet.

Its job is closely tied to the actual networking and security operations happening on that node. It manages Cilium’s datapath, interacts with eBPF programs, handles endpoints, and enforces network policies.

The Operator is different.It is a cluster-level component that handles tasks such as IP address management and other cluster-wide Cilium operations.

A simple way I think about it now is:

The Agent deals with networking on individual nodes, while the Operator handles tasks that need to be coordinated across the cluster.

That distinction made the Cilium architecture much easier to understand.

Looking inside Cilium

After understanding the components and getting the basic installation working, I started inspecting what Cilium was actually doing.

I first looked at the Cilium pods:

kubectl get pods \
--namespace=kube-system \
-l k8s-app=cilium \
-o wide
Press enter or click to view image in full size

Since the Cilium Agent runs as a DaemonSet, I expected to see an agent associated with each node.

I then checked the detailed status from inside the Cilium pod:

kubectl exec -n kube-system ds/cilium -- \
cilium status --verbose
Press enter or click to view image in full size

One of the more interesting commands was:

kubectl exec -n kube-system ds/cilium -- \
cilium bpf map list
Press enter or click to view image in full size

This showed the eBPF maps being used by Cilium.

This was probably one of the points where eBPF started feeling less abstract to me.

Seeing Cilium’s eBPF maps being loaded and managed inside the cluster made the architecture much more concrete.

So where does eBPF actually fit?

The basic idea behind Cilium is that it can use eBPF programs inside the Linux kernel to handle networking and security operations.

Instead of thinking about Cilium as simply another Kubernetes networking plugin, I started thinking about it more like this:

Press enter or click to view image in full size

Those eBPF programs can be involved in things such as:

  • Network policy enforcement
  • Packet processing
  • Load balancing
  • Security decisions
  • Network visibility

This is one of the main differences between Cilium and more traditional approaches that depend heavily on iptables.

Moving from Installation to Network Policies

Once I had Cilium running, the next part of the lab was much more interesting: actually controlling traffic.

I deployed the Cilium Star Wars example:

kubectl create -f \
https://raw.githubusercontent.com/cilium/cilium/main/examples/minikube/http-sw-app.yaml
Press enter or click to view image in full size
Cilium Star Wars Example

The application includes three workloads:

  • deathstar
  • tiefighter(friend)
  • xwing(enemy)

The easiest way to understand the example is to think of the Death Star as a web service, while the TIE Fighter and X-Wing are clients trying to communicate with it.

Get Learnkali’s stories in your inbox

Join Medium for free to get updates from this writer.

The deathstar workload runs an HTTP service on TCP port 80 and is exposed through a Kubernetes Service. The example actually runs two Death Star pod replicas behind that Service.

The two other workloads represent clients:

  • TIE Fighter — represents an Empire ship. It has the label org=empire.
  • X-Wing — represents an Alliance ship. It has the label org=alliance.
  • Death Star — represents the service being protected. It has the labels org=empire and class=deathstar.

The names make the example a little more fun, but the actual purpose is to demonstrate Kubernetes network policies.

Creating a L3/L4 policy

I created a CiliumNetworkPolicy that allowed traffic to the Death Star from endpoints belonging to the Empire.

The important part looked like this:

endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP

In simple terms, the policy means:

Allow Empire workloads to access the Death Star over TCP port 80.

The interesting part was testing both sides of the rule.

From the TIE Fighter:

kubectl exec -it tiefighter -- \
curl -s -XPOST \
deathstar.default.svc.cluster.local/v1/request-landing
Press enter or click to view image in full size
Successful request from tiefighter

The request succeeded.

Then I tested from the X-Wing:

kubectl exec -it xwing -- \
curl -v \
http://deathstar.default.svc.cluster.local/v1/
Press enter or click to view image in full size
Blocked request from xwing

This request was blocked.

That was a useful moment because it demonstrated something more important than simply writing a YAML file.

I could actually see the policy changing the behavior of the network.

Understanding L3/L4 vs L7

The first policy was working at the L3/L4 level.

That basically lets us make decisions around things like:

  • Source identity
  • Destination identity
  • Protocol
  • Port

But what if I wanted to allow TCP port 80 while only allowing certain HTTP requests?

That’s where L7 policies become useful.

Going one layer deeper with HTTP policies

I modified the policy to include HTTP rules:

Press enter or click to view image in full size
rules:
http:
- method: "GET"
path: "/v1/"

Now the policy isn’t just looking at the fact that traffic is going to TCP port 80.

It can understand the HTTP request itself.

For example:

GET /v1/

can be treated differently from:

POST /v1/

This was where Envoy became relevant.

Cilium can use its Envoy integration to inspect application-layer traffic and enforce L7 policies.

The progression made sense:

L3/L4
Who can connect?
What port?
What protocol?
L7
What application request?
What HTTP method?
What path?

For me, this was one of the most useful concepts from the lab.

DNS and FQDN-Based Policies

The next part I explored was restricting outbound traffic using DNS names.

Cilium’s FQDN-based policies can restrict outbound connections to IP addresses learned from DNS responses for approved domain names. DNS policy rules can separately control which DNS queries are allowed. This provides more flexibility than maintaining a static list of destination IPs, although it depends on DNS traffic being observed and handled as expected.

For example, instead of allowing a pod to access arbitrary external IP addresses, I could create a rule allowing access to:

api.github.com

using:

toFQDNs:
- matchName: "api.github.com"

This is useful because IP addresses aren’t necessarily stable.

A service such as:

api.github.com

can resolve to different IP addresses over time.

Using an FQDN-based policy lets the security rule be based on the destination’s domain name rather than hardcoding an IP address.

Conceptually:

Press enter or click to view image in full size
Application
│
├── api.github.com ✓
│
├── approved-api.com ✓
│
└── unknown-site.com ✗

This gives much better control over outbound communication.

Final Thoughts

This first hands-on experience helped me connect several Kubernetes networking concepts that I had previously understood mostly in theory. Setting up Cilium was a good starting point, but the real learning came from inspecting its eBPF maps, creating network policies, and testing how those policies affected actual traffic.

The Star Wars example was particularly useful because it showed how network security can go beyond simply allowing or blocking connections. With Cilium, I could control communication based on workload identity, ports, HTTP requests, and even DNS names. Seeing the difference between a successful request and one blocked by a policy made these concepts much easier to understand.

Of course, this is only the beginning. I still want to explore how Cilium handles network traffic internally, how Hubble helps troubleshoot connectivity issues, and how these capabilities can be applied to more realistic Kubernetes environments.

For anyone learning Kubernetes networking, I’d recommend getting hands-on rather than relying only on diagrams and documentation. Even a small local cluster can teach you a lot when you start experimenting, breaking things, and figuring out why they behave the way they do.

This was my first step into Cilium and eBPF, and I’m looking forward to digging deeper in the next lab.

--