Kubernetes NetworkPolicies with Cilium and Hubble: Enforce Segmentation and Observe Traffic
Kubernetes NetworkPolicies are API objects used to control which ingress and egress connections are allowed for pods. In a default Kubernetes setup, networking is broadly open: pods can usually communicate with other pods, and many workloads can also access external destinations. NetworkPolicies provide the rules for segmentation, but whether those rules are actually enforced depends entirely on the CNI in use. Cilium enforces policies with eBPF directly in the kernel and also brings an important capability that standard implementations often miss: observability. Without flow-level insight, configuration mistakes can remain invisible, and security reviews may lack the evidence needed to confirm that policies are working as intended.
Cilium combined with eBPF improves both enforcement and visibility. Instead of relying on userspace iptables chains, policies are enforced in the kernel datapath, which enables efficient filtering and also supports layer 7 controls such as HTTP method and path matching. Hubble provides detailed flow-level telemetry so that policy mistakes can be identified before they impact live workloads. In managed Kubernetes environments, Cilium can be deployed as the CNI and Hubble can then be used to validate and troubleshoot policy rollouts.
This tutorial explains how to create enforceable network segmentation with traffic observability in Kubernetes: install Cilium, deploy a multi-service sample application, apply standard and advanced policies, and use Hubble to confirm behavior and troubleshoot issues. You will configure controls for pod-to-pod communication and implement a zero-trust networking model appropriate for multi-tenant or compliance-sensitive environments.
Key Takeaways
- Kubernetes NetworkPolicies remain allow-all by default unless a policy-aware CNI enforces them; without that enforcement, all traffic is permitted.
- Cilium uses eBPF for efficient packet filtering in the kernel, which lowers overhead and enables both L3/L4 and L7 policy enforcement.
- Hubble exports flow logs and metrics from Cilium’s eBPF hooks with visibility at namespace and service level.
- Layer 7 enforcement, such as allowing
GET /healthwhile denyingPOST /admin, is possible with CiliumNetworkPolicy and is not part of standard NetworkPolicies. - Zero-trust networking is achievable in managed Kubernetes environments through explicit ingress and egress rules combined with namespace isolation.
- Observability helps prevent silent policy mistakes; Hubble makes it possible to verify rules before and after enforcement and supports security auditing.
Conceptual Foundation
What Are Kubernetes NetworkPolicies?
NetworkPolicies are namespace-scoped rules, or rules that target selected pods, which define ingress traffic (who may send traffic to selected pods) and egress traffic (where selected pods may send traffic). They are declarative: you specify the permitted peers and ports, while the CNI performs the enforcement. If no policy selects a pod, behavior depends on the implementation. With Cilium, pods that are not selected by any policy generally allow all traffic until a default-deny rule is introduced and then a specific allow-list is added.
Ingress versus egress: ingress rules apply to traffic directed toward the selected pods, while egress rules apply to traffic leaving those pods. For segmentation, the usual starting point is a “deny all ingress” policy followed by explicit allows for selected namespaces or pod labels.
Namespace isolation versus pod-level isolation: you can target every pod in a namespace with one policy, or use pod selectors to isolate specific workloads such as backend services only. Multi-tenant environments often rely on namespace-level isolation together with explicitly allowed ingress from an API gateway or ingress namespace.
How Cilium Differs from Other CNIs
Cilium supports the Kubernetes NetworkPolicy API and also introduces its own CiliumNetworkPolicy CRD for layer 7 rules and additional extensions. Unlike many CNIs that depend on iptables or userspace proxies, Cilium runs eBPF programs inside the Linux kernel to both filter and observe traffic. This provides:
- eBPF datapath: Packet processing and policy evaluation occur in the kernel, reducing context switches and maintaining predictable performance at scale.
- L7 awareness: Rules can match HTTP/gRPC methods, paths, and headers using CiliumNetworkPolicy.
- Built-in observability: Hubble consumes flow and metrics data derived from eBPF through Cilium agents.
What Is eBPF and Why Is It Used?
eBPF (extended Berkeley Packet Filter) is a kernel technology that allows sandboxed programs to run on events such as packet arrival or socket activity. Cilium translates NetworkPolicy and CiliumNetworkPolicy objects into eBPF programs that execute on every node. Packets are classified in the kernel and then allowed or dropped, so simple L3/L4 policy decisions do not require userspace processing. That is why eBPF-based network policies scale well and why eBPF networking is used for both enforcement and visibility in Kubernetes.
Why Observability Is Often Missing with Standard NetworkPolicies
The standard Kubernetes NetworkPolicy API does not define observability. Many CNIs can enforce allow and deny behavior but do not expose which flows were denied or the reason for the decision. Operators may introduce “deny all” policies and then spend time debugging broken connectivity without a clear view of traffic. Hubble fills this gap by exporting flow logs and metrics from Cilium’s eBPF hooks, allowing you to connect observed traffic behavior to the policy rules that caused it.
| Feature | Flannel | Calico | Cilium |
|---|---|---|---|
| eBPF | No | No | Yes |
| NetworkPolicy enforcement | No | Yes | Yes |
| L7 policy | No | Limited | Yes |
| Built-in observability | No | No | Hubble |
Architecture Overview: Managed Kubernetes with Cilium
In this design, all pod traffic flows through the Cilium eBPF datapath running on each node. Hubble collects flow information from the same eBPF hooks that Cilium uses for policy enforcement, so both enforcement and visibility operate through one datapath without introducing extra overhead.
- Cilium on managed Kubernetes: Some managed Kubernetes offerings do not include Cilium by default. It is typically installed through Helm and, when used as the cluster CNI, it replaces the platform’s default CNI. Refer to the Cilium installation guidance that matches your Kubernetes version. After installation, all pod traffic is governed by eBPF-based policy enforcement.
- Packet inspection: Packets are examined in the kernel. L3/L4 rules are enforced through eBPF, while L7 rules with CiliumNetworkPolicy require HTTP parsing in the datapath.
- Hubble: Hubble Relay, and optionally the UI, aggregates flow data from Cilium agents so that you can run
hubble observeand filter by namespace, label, or verdict such as allowed or dropped. - Multi-service example: A common layout includes a frontend namespace, a backend namespace, a database namespace, and an observability namespace. In this design, the frontend may contact the backend only on explicitly allowed ports, the backend may reach only the database, and the observability namespace may scrape or query as required. Default-deny combined with explicit ingress and egress rules provides service isolation and network segmentation.
graph TD
FE[frontend namespace<br/>app=frontend] -->|port 80 allowed| BE[backend namespace<br/>app=backend]
BE -->|port 6379 allowed| DB[database namespace<br/>app=database]
OBS[observability namespace] -.->|scrape metrics| BE
OBS -.->|scrape metrics| FE
EXT[external / internet] -. blocked by egress policy .-> BE
EXT -. blocked by egress policy .-> DB
style EXT fill:#f66,color:#fff
Default-deny ingress is enforced per namespace. Solid arrows indicate explicitly permitted traffic paths. Dotted lines indicate traffic paths that are blocked.
Prerequisites
- A managed Kubernetes cluster.
kubectlconfigured for the cluster.- Helm, for installing Cilium.
- Basic familiarity with Kubernetes and namespaces.
- Cilium already installed, or planned as the CNI, along with namespaces for a sample application such as
frontend,backend, anddatabase.
Step-by-Step Implementation
Step 1 – Install or Enable Cilium on the Cluster
Add the Cilium Helm repository and install Cilium with Hubble enabled. Replace the Cilium version with a release that is supported for your Kubernetes version.
Warning: Installing Cilium as the active CNI on a cluster that already runs workloads will interrupt pod networking. During the CNI transition, pods lose network connectivity until Cilium agents become fully Ready on every node. For production environments, cordon and drain nodes one at a time and confirm Cilium readiness on each node before continuing. For new clusters without workloads, the transition can safely be performed in a single pass. If you are testing, use a dedicated cluster for this tutorial.
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium --version 1.16.2 \
--namespace kube-system \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true
Expected output:
"cilium" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "cilium" chart repository
Update Complete.
NAME: cilium
LAST DEPLOYED: Sat Feb 21 10:00:00 2026
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
Wait for the rollout to complete:
kubectl -n kube-system rollout status ds/cilium --timeout=300s
Expected output:
daemon set "cilium" successfully rolled out
Install cilium-cli:
The cilium status command requires the separate cilium-cli binary, which is not installed automatically with the Helm deployment. Install it now:
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
curl -L --fail --remote-name-all \
https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz
sudo tar xzvf cilium-linux-${CLI_ARCH}.tar.gz -C /usr/local/bin
Note: On macOS, replace linux-amd64 with darwin-amd64.
Verify Cilium and confirm the CNI is active:
kubectl get pods -n kube-system -l k8s-app=cilium
cilium status
Expected output for kubectl get pods:
NAME READY STATUS RESTARTS AGE
cilium-4xk2p 1/1 Running 0 2m
cilium-9rtzq 1/1 Running 0 2m
cilium-vbn3w 1/1 Running 0 2m
Expected output for cilium status:
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode)
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
KVStore: Ok
Kubernetes: Ok 1.29+ (v1.29.0) [linux/amd64]
NodeMonitor: Listening for events on 4 CPUs
Cilium health daemon: Ok
IPAM: IPv4: 5/254 allocated
Make sure all Cilium pods are Ready and that cilium status confirms the datapath and policy mode. If you do not want to install cilium-cli, you can use this alternative:
kubectl exec -n kube-system ds/cilium -- cilium status
This completes the baseline setup for Cilium on a managed Kubernetes cluster.
Step 2 – Deploy a Multi-Service Demo Application
Create the namespaces, label them so the allow policy in Step 3 can match them correctly, deploy the pods, and create Services so that DNS resolution works. Nginx listens on port 80. Do not apply any NetworkPolicy yet; the initial connectivity baseline should remain allow-all.
kubectl create namespace frontend
kubectl create namespace backend
kubectl create namespace database
kubectl label namespace frontend name=frontend
kubectl label namespace backend name=backend
kubectl label namespace database name=database
kubectl create deployment frontend -n frontend --image=nginx
kubectl label deployment frontend -n frontend app=frontend
kubectl create deployment backend -n backend --image=kennethreitz/httpbin
kubectl label deployment backend -n backend app=backend
kubectl create deployment db -n database --image=redis:alpine
kubectl label deployment db -n database app=database
kubectl expose deployment frontend -n frontend --port=80 --target-port=80
kubectl expose deployment backend -n backend --port=80 --target-port=80
Note: The backend uses kennethreitz/httpbin, which exposes routes such as /get, /post, and /anything. This is necessary for accurate layer 7 validation in Step 4. A plain Nginx image does not provide /health or /admin endpoints and would therefore return 404 instead of the expected HTTP results.
Confirm that the pods are Ready in each namespace before moving on:
kubectl get pods -n frontend
kubectl get pods -n backend
kubectl get pods -n database
Expected output (pod names will be different):
NAME READY STATUS RESTARTS AGE
frontend-6d4b7f9c8d-xk2p9 1/1 Running 0 30s
NAME READY STATUS RESTARTS AGE
backend-7f8b9d6c4d-p2r4x 1/1 Running 0 30s
NAME READY STATUS RESTARTS AGE
db-5c6d7e8f9a-m3n4o 1/1 Running 0 30s
Next, verify baseline connectivity from the frontend namespace:
kubectl run curl-test --rm -it --restart=Never \
-n frontend \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" http://backend.backend.svc.cluster.local
Expected output:
200
A 200 response confirms that connectivity is working before any NetworkPolicy is applied. If you receive a connection error, check whether the backend Service exists:
kubectl get svc -n backend
Expected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend ClusterIP 10.245.78.123 <none> 80/TCP 1m
Warning: Do not apply a default-deny policy to a production namespace before using hubble observe to capture existing traffic patterns. Applying default-deny immediately interrupts all ingress, including health checks, service-to-service traffic, and monitoring scrapes. Complete Step 5 in a staging cluster first, observe active flows, and build your allow-list before enforcing deny rules on production workloads.
Step 3 – Apply a Basic Kubernetes NetworkPolicy
Apply a default-deny ingress policy in the namespace you want to isolate, such as backend:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: backend
spec:
podSelector: {}
policyTypes:
- Ingress
Save the policy to a file and apply it:
kubectl apply -f default-deny-ingress.yaml
Expected output:
networkpolicy.networking.k8s.io/default-deny-ingress created
Then allow ingress only from the frontend namespace, or from a more specific selector if desired:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend
namespace: backend
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: frontend
ports:
- protocol: TCP
port: 80
Save the policy to a file and apply it:
kubectl apply -f allow-from-frontend.yaml
Expected output:
networkpolicy.networking.k8s.io/allow-from-frontend created
This allow rule matches namespaces that carry the label name=frontend, which you added in Step 2.
After the default-deny policy is applied, verify that ingress from the default namespace is blocked:
kubectl run curl-test --rm -it --restart=Never \
-n default \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" http://backend.backend.svc.cluster.local
Expected output:
000
A 000 response indicates that the connection was refused or timed out, confirming that the default-deny policy is in effect.
Then apply the allow-from-frontend policy. Verify that traffic from the frontend namespace is now allowed:
kubectl run curl-test --rm -it --restart=Never \
-n frontend \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" http://backend.backend.svc.cluster.local
Expected output:
200
A 200 response confirms that traffic from the frontend namespace can reach the backend. A curl test from the default namespace should still return 000.
Step 4 – Apply CiliumNetworkPolicy for Advanced Layer 7 Control
Use CiliumNetworkPolicy when you need layer 7 HTTP filtering. The following example allows GET /get and GET /anything/.* while blocking other HTTP methods such as POST /post for backend pods.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: backend-http-rules
namespace: backend
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: frontend
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "GET"
path: "/get"
- method: "GET"
path: "/anything/.*"
Save the policy to a file and apply it:
kubectl apply -f backend-http-rules.yaml
Expected output:
ciliumnetworkpolicy.cilium.io/backend-http-rules created
This level of control, matching method and path, is not available with standard Kubernetes NetworkPolicies. It offers a service-mesh-style capability without requiring a full service mesh deployment.
The label key k8s:io.kubernetes.pod.namespace is a Cilium-specific identifier that maps to the Kubernetes pod namespace. It is not the same as the namespaceSelector field in a standard NetworkPolicy. In CiliumNetworkPolicy fromEndpoints, namespaces are referenced using this internal key rather than a label from the namespace object. To target another namespace, replace the value frontend with the required namespace name. To inspect the labels Cilium attaches to pod identities, run:
kubectl exec -n kube-system ds/cilium -- cilium endpoint list
Expected output (trimmed):
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS
1234 Enabled Disabled 16385 k8s:app=backend
k8s:io.kubernetes.pod.namespace=backend
5678 Enabled Disabled 16386 k8s:app=frontend
k8s:io.kubernetes.pod.namespace=frontend
This lists all endpoints together with the identity labels Cilium assigned, including k8s:io.kubernetes.pod.namespace.
To validate the L7 policy, test one allowed path and one blocked method from the frontend namespace:
# Allowed: GET /get from frontend
kubectl run curl-test --rm -it --restart=Never \
-n frontend \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" \
http://backend.backend.svc.cluster.local/get
Expected output:
200
# Blocked: POST /post from frontend
kubectl run curl-test --rm -it --restart=Never \
-n frontend \
--image=curlimages/curl \
-- curl -s -o /dev/null -w "%{http_code}" -X POST \
http://backend.backend.svc.cluster.local/post
Expected output:
403
A 403 response confirms that Cilium is enforcing method-based access control at layer 7. The GET request to /get is allowed by the L7 rule set, while the POST request to /post is not in the allow-list and is therefore dropped by Cilium’s HTTP proxy. Hubble will report these as ALLOWED and DROPPED flows.
Step 5 – Enable and Use Hubble for Observability
Hubble Relay runs inside the cluster, but you need the Hubble CLI in order to query it. Install the CLI before running any hubble commands:
HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
curl -L --fail --remote-name-all \
https://github.com/cilium/hubble/releases/download/${HUBBLE_VERSION}/hubble-linux-amd64.tar.gz
tar xzvf hubble-linux-amd64.tar.gz
sudo mv hubble /usr/local/bin/
hubble version
Expected output:
hubble v0.13.0 compiled with go1.21.5 on linux/amd64
Note: On macOS, replace linux-amd64 with darwin-amd64. Confirm that the binary is available with hubble version before continuing.
Next, port-forward the Hubble Relay service:
kubectl port-forward -n kube-system svc/hubble-relay 4245:4245
Expected output:
Forwarding from 127.0.0.1:4245 -> 4245
Forwarding from [::1]:4245 -> 4245
Keep this terminal open. Open a second terminal for the following commands.
In the second terminal, set the server address and start observing traffic:
export HUBBLE_SERVER=localhost:4245
hubble observe --since 1m --namespace backend
To see only dropped flows:
hubble observe --since 1m --verdict DROPPED --namespace backend
Example output, which may vary in format:
Sep 15 10:01:00.123: frontend/frontend -> backend/backend:80 (HTTP) ALLOWED
Sep 15 10:01:00.456: default/curl-xxx -> backend/backend:80 (HTTP) DROPPED
Hubble shows which flows were permitted and which were denied. Use this information to verify that policies behave as expected and to debug connectivity issues such as an incorrect namespace label or a wrong port.
Real-World Multi-Tenant Scenario
In a shared cluster, Tenant A and Tenant B each run in their own namespace. An API gateway, or ingress namespace, serves as the only entry point from outside. Internal databases run in a dedicated namespace.
Namespace-level isolation: Each tenant namespace has a default-deny ingress policy. Ingress is allowed only from the API gateway or ingress controller namespace, with explicit port rules.
Egress: Tenant workloads receive egress rules that allow only the external APIs they truly need, such as DNS or package registries, as well as required internal services such as the database namespace. This limits lateral movement and reduces blast radius. For DNS, allow UDP port 53 to the namespace where CoreDNS runs, often kube-system. Example egress policy for a tenant namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: tenant-a
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
Zero trust: No namespace is trusted by default. Every permitted traffic path must be explicitly allow-listed. Hubble flow logs provide an audit trail for every connection attempt across tenant boundaries.
Troubleshooting
Policy Not Enforced
Symptom: Traffic is still allowed unexpectedly, or blocked unexpectedly, after a NetworkPolicy has been applied.
Likely causes:
- The
podSelectordoes not match the intended pods. - The
namespaceSelectorlabel does not match the namespace. - The policy was applied in the wrong namespace.
Step-by-step fixes:
Verify that the policy exists in the correct namespace:
kubectl get networkpolicy -n backend
Expected output:
NAME POD-SELECTOR AGE
allow-from-frontend <none> 5m
default-deny-ingress <none> 6m
Inspect the policy selectors:
kubectl describe networkpolicy -n backend
Confirm the pod labels:
kubectl get pods -n backend --show-labels
Expected output:
NAME READY STATUS RESTARTS AGE LABELS
backend-7d6b9f8c4d-p2r4x 1/1 Running 0 10m app=backend,pod-template-hash=7d6b9f8c4d
Confirm the namespace labels:
kubectl get namespace frontend --show-labels
Expected output:
NAME STATUS AGE LABELS
frontend Active 15m kubernetes.io/metadata.name=frontend,name=frontend
Check Cilium status:
cilium status
Cilium Pods CrashLoop or Are Not Ready
Symptom: Cilium pods in kube-system are not Ready or are restarting repeatedly.
Likely causes:
- The kernel does not support the required eBPF features.
- Another CNI is conflicting with Cilium.
- The nodes do not have enough resources.
Step-by-step fixes:
Inspect the Cilium pod logs:
kubectl logs -n kube-system -l k8s-app=cilium
Verify that all Cilium DaemonSet pods are running:
kubectl get ds cilium -n kube-system
Expected output:
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
cilium 3 3 3 3 3 <none> 20m
Confirm that no other CNI is active and that the nodes meet the kernel requirements for eBPF.
Hubble Is Not Showing Any Flows
Symptom: hubble observe returns no output.
Likely causes:
- Hubble Relay is not running.
- Port-forwarding is not active.
- No traffic occurred during the selected observation window.
Step-by-step fixes:
Verify Hubble Relay:
kubectl get pods -n kube-system -l k8s-app=hubble-relay
Expected output:
NAME READY STATUS RESTARTS AGE
hubble-relay-5d8b9f7c6d-xk2p9 1/1 Running 0 15m
Make sure port-forwarding is still active:
kubectl port-forward -n kube-system svc/hubble-relay 4245:4245
Generate test traffic and run observation again:
hubble observe --since 1m
DNS or External API Calls Fail After Policy Enforcement
Symptom: Pods can no longer resolve DNS or reach external APIs after egress policies are applied.
Likely causes:
- The egress rules do not permit traffic to kube-dns or CoreDNS.
- External destinations were not explicitly allowed.
Step-by-step fixes:
Confirm that DNS traffic is being dropped:
hubble observe --since 1m --verdict DROPPED
Add an egress rule that allows DNS on UDP/TCP port 53 to the namespace where kube-dns runs.
Add explicit egress rules for required external IPs, or use Cilium FQDN policies where appropriate.
For low-level per-packet inspection on a specific node, run cilium monitor inside a Cilium pod. This produces a raw event stream from the eBPF datapath, which is more detailed than Hubble but also harder to filter:
kubectl exec -n kube-system ds/cilium -- cilium monitor --type drop
The --type drop flag restricts the output to dropped packets only. Use this when hubble observe does not provide enough detail for a particular flow. For most policy troubleshooting, hubble observe --verdict DROPPED is faster and easier to work with:
hubble observe --since 1m --verdict DROPPED
Together, these two tools cover the full range of policy debugging needs: Hubble for cluster-wide flow filtering, and cilium monitor for node-level packet inspection when deeper detail is required.
FAQs
What are Kubernetes NetworkPolicies?
They are API resources that define which ingress and egress traffic is allowed for selected pods. Enforcement is handled by the CNI; Cilium enforces these rules in the kernel through eBPF.
How does Cilium differ from other CNIs?
Cilium uses eBPF for both datapath processing and policy enforcement, supports layer 7 HTTP policy through CiliumNetworkPolicy, and includes built-in observability through Hubble. Many other CNIs rely on iptables and do not provide L7 enforcement or flow visibility.
What is eBPF and why is it used in Kubernetes?
eBPF is a kernel mechanism that allows safe and efficient programs to run on networking and other system events. Cilium uses it to enforce NetworkPolicies and CiliumNetworkPolicies in the kernel, which reduces overhead and enables both L7 control and observability.
What is Hubble in Cilium?
Hubble is the observability component of Cilium. It gathers flow and metrics information from Cilium agents through eBPF and exposes that data so you can inspect allowed and dropped traffic, for example with hubble observe or Hubble UI.
How do I visualize traffic in a Kubernetes cluster?
With Cilium and Hubble, you can use hubble observe with filters such as namespace, label, or verdict, or deploy Hubble UI to inspect flows visually. This provides L3/L4 visibility and, where L7 policy is enabled, HTTP visibility as well, without requiring a service mesh.
Can Cilium enforce layer 7 policies?
Yes. CiliumNetworkPolicy supports L7 rules such as HTTP method and path matching. Standard Kubernetes NetworkPolicy covers only L3/L4.
How do NetworkPolicies improve security?
They limit which pods can communicate with one another and which workloads may access the internet. When combined with default-deny and explicit allow-lists, they provide segmentation, reduce lateral movement, and align well with zero-trust and compliance-focused security models.
Is Cilium supported on managed Kubernetes?
Cilium can be installed on many managed Kubernetes platforms either as the active CNI or to enable advanced capabilities such as Gateway API support and L7 policy. Always confirm compatibility with your Kubernetes version and the requirements of your provider.
Conclusion
Kubernetes NetworkPolicies by themselves are not sufficient unless a CNI enforces them, and without observability it is difficult to verify that they behave correctly. Cilium provides efficient, eBPF-based enforcement, while Hubble supplies the visibility needed to validate every policy change before and after rollout. In managed Kubernetes environments, you can implement zero-trust networking and service isolation by introducing segmentation in stages: first observe baseline traffic with Hubble, then apply default-deny and explicit allow-lists, and finally add CiliumNetworkPolicy where layer 7 control is required.


