Cilium Tutorial: eBPF Networking, Policy, and Observability

Install Cilium, lock down pod traffic with identity-based L7 policy, and watch every flow with Hubble - all backed by eBPF in the Linux kernel.

Get started - freeLive cloud workspace in your browser · nothing to install
#cilium#kubernetes#ebpf#network-policy#hubble

What is Cilium?

Cilium is an open-source networking, security, and observability layer for Kubernetes built on eBPF, a Linux kernel technology that lets you run sandboxed programs inside the kernel without changing kernel source or loading modules. As a CNI plugin, Cilium gives every pod an IP and routes traffic between them, but its real value is what it does on top of that: identity-based security policy spanning L3 through L7, high-performance service load balancing that can replace kube-proxy, and deep network visibility through Hubble. It is a graduated CNCF project, and several managed Kubernetes services use it as their networking layer.

The defining idea is that Cilium attaches eBPF programs to kernel hooks - at the network device, the socket layer, and traffic-control ingress/egress - so packet processing, policy enforcement, and metrics collection happen in the kernel fast path. Traditional CNIs lean on iptables, where rules are evaluated as a linear chain that grows with the number of Services and pods. Cilium replaces that with eBPF hash-map lookups that stay roughly constant regardless of cluster size.

How Cilium works (key concepts)

The concept that makes Cilium different from IP-based networking is identity. When a pod starts, Cilium derives a numeric security identity from its labels. Every other pod sharing the exact same set of relevant labels gets the same identity. Policy is then expressed and enforced in terms of these identities, not IP addresses. This matters because pod IPs are ephemeral - they churn on every restart and reschedule - whereas a workload's labels are stable. Enforcing on identity means a rule like "frontend may talk to backend" keeps working through rollouts and scaling without rewriting any addresses.

A few other concepts are worth knowing before you start:

Installing Cilium

The cleanest way to install Cilium is the cilium CLI, which wraps the Helm chart, picks sensible defaults for your platform, and gives you status and test commands. First install the CLI binary. The snippet below pins to the published stable version and selects the right CPU architecture:

CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all \
  https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}

With the CLI on your PATH, install Cilium into the cluster your current kubeconfig points at, then wait for it to come up and run the built-in connectivity test. The kube-proxy replacement setting is optional. It is meant for clusters created without kube-proxy, and in that case Cilium also needs the API server address (k8sServiceHost and k8sServicePort) - check the Cilium docs for your platform:

# Install with kube-proxy replacement enabled
cilium install --set kubeProxyReplacement=true

# Block until the control plane and agents are Ready
cilium status --wait

# Run the end-to-end connectivity test suite (creates a temp namespace, cleans up)
cilium connectivity test

cilium status is the command you will return to most - it reports the agent DaemonSet, the operator, and which features (Hubble, kube-proxy replacement, encryption) are active. The connectivity test spins up client and server pods and validates pod-to-pod, pod-to-service, and policy paths, so it is a good smoke test after any networking change.

Hands-on: an L7 HTTP policy with Hubble visibility

Here is the main use case. Suppose a backend pod exposes an API on port 80 and you want the frontend to call only GET requests under /api/v1/, while everything else, including any write method, is denied. Native NetworkPolicy cannot express that; a CiliumNetworkPolicy can. Save this as l7-policy.yaml:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: frontend-to-backend-l7
spec:
  endpointSelector:
    matchLabels:
      app: backend
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: frontend
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/api/v1/.*"

Note what is being selected: endpointSelector and fromEndpoints match on pod labels (app: backend, app: frontend), which become Cilium identities - no IPs anywhere. The rules.http block is the L7 layer; the path value is a regular expression, so /api/v1/.* matches any sub-path under /api/v1/. Apply it the usual way:

kubectl apply -f l7-policy.yaml

Now turn on Hubble to watch the verdicts in real time. The hubble CLI is a separate binary - install it from the Cilium docs first. Then enable Hubble and use either the CLI or the UI:

# Enable Hubble (add --ui for the graphical service map)
cilium hubble enable --ui

# In one terminal, open the relay port-forward
cilium hubble port-forward &

# In another, stream the flows for the backend pod
hubble observe --pod backend --follow

# Or show only the denied requests
hubble observe --pod backend --verdict DROPPED --follow

Send a GET /api/v1/users from the frontend and Hubble logs it as FORWARDED with the HTTP detail attached. Send a POST or a request to /admin, and the same flow appears as DROPPED with the policy verdict. The client gets an HTTP 403 response and the request never reaches your application, because the Envoy proxy that Cilium manages blocked it. This short cycle - write a policy, apply it, watch the verdict - makes Cilium policy debugging much easier than reading iptables chains.

When to use Cilium

Reach for Cilium when your security or scale requirements outgrow basic L3/L4 filtering. If you need to allow specific HTTP paths or methods, restrict egress by DNS name (for example, "this pod may only reach api.stripe.com"), or gate Kafka topics and gRPC services, the CiliumNetworkPolicy CRD covers cases that native NetworkPolicy cannot express. It is also a strong choice on large clusters where kube-proxy's iptables rules become a performance and troubleshooting burden; Cilium's eBPF kube-proxy replacement keeps Service routing fast and predictable as the cluster grows.

Cilium is equally compelling when observability is the goal. Hubble gives you a live service-dependency map and a searchable flow log - including policy drops - without sidecars or code changes, which is very useful when you are writing a zero-trust policy and need to confirm exactly what is and is not allowed. It pairs naturally with transparent encryption (WireGuard or IPsec) when you must encrypt pod-to-pod traffic across nodes.

The main trade-offs are operational. Cilium expects a reasonably modern kernel for its full eBPF feature set, and the breadth of features means more concepts to learn than a minimal CNI like Flannel. If your needs are genuinely limited to flat pod networking with no policy, a simpler CNI may be enough. But for production clusters that care about least-privilege networking, performance at scale, and real visibility, Cilium is one of the most common choices.

Frequently asked questions

What is Cilium?

Cilium is an open-source, eBPF-based Container Network Interface (CNI) for Kubernetes. It provides pod networking, identity-based L3-L7 network policy, load balancing, and observability by programming the Linux kernel directly with eBPF instead of relying on iptables. It is a graduated CNCF project.

What is the difference between a NetworkPolicy and a CiliumNetworkPolicy?

The standard Kubernetes NetworkPolicy only filters at L3/L4 (IPs, ports, protocols). A CiliumNetworkPolicy (CRD) extends this to L7 - it can allow or deny specific HTTP methods and paths, gRPC calls, Kafka topics, and DNS names, and it selects workloads by Cilium identity rather than by IP address.

Does Cilium need kube-proxy?

No. Cilium can fully replace kube-proxy with its eBPF-based implementation of Service load balancing. You enable it by installing with kube-proxy replacement turned on, which removes the per-node iptables rule churn that kube-proxy generates at scale.

What is Hubble?

Hubble is Cilium's observability layer, built on the same eBPF datapath. It shows real-time network flows, service dependencies, and policy verdicts (allowed/dropped) without code changes or sidecars. You access it through the hubble CLI or the Hubble UI service map.

Do real production work, free. Live cloud workspaces in your browser - every project you complete lands on your portfolio.

Get started - free
Get started - free