This post teaches the Cilium policy model with clear scenarios and annotated YAML. It matches the style of practical technical blogs, explanation first and code second, with links to the official docs where you will want deeper detail.
Why Cilium policy
Kubernetes’ built-in NetworkPolicy objects define which pods can communicate using label-based rules at the IP and port level. This provides basic isolation, but it stops short of deeper visibility or intent-based control.
Cilium builds on this foundation by introducing security identities derived from labels. These identities represent workloads consistently across nodes and are enforced directly in the kernel using eBPF. Because enforcement happens in the datapath, policies remain accurate and efficient even as workloads scale or IPs change.
Beyond IP and port filtering, Cilium understands application context such as DNS names and HTTP methods and paths. This makes it possible to express policies in human terms — for example, “allow only GET requests on /health from pods with role=frontend” or “allow egress only to api.partner.com.”
Together, these capabilities create a single, consistent model for enforcing and observing network behavior across all workloads. This post walks through that model step by step, with practical YAML examples you can apply to your own environment.
For further reading, see the official Cilium policy overview for the complete language reference and selector options.
Mental model
Every policy answers four things. Where to enforce, which direction to guard, who may talk, and whether to apply checks at the application layer.
These are the top things to keep in mind when defining a Cilium Network Policy.
- Subject, choose pods with
endpointSelectoror nodes withnodeSelector - Direction, if a selected subject has an
ingresslist then ingress becomes default deny for that subject, the same idea applies foregress - Peers, choose with
fromEndpoints,toEndpoints,toEntities,toCIDRSet,toFQDNs,toServices - Application layer, add optional
rules:undertoPortsfor HTTP or DNS
Language details are in the Cilium policy language.
We typically refer to the security policies implemented in Cilium holistically as “Cilium Network Policy”. However when you dive into using them in your platform, you will find there is in fact two types of policy configuration to be aware of. Essentially most of the information in this post is true for both types. But just keep in mind the following;
- CiliumNetworkPolicy (CNP) is the namespaced policy object you apply to control traffic for pods within a single namespace.
CiliumClusterwideNetworkPolicy (CCNP) is the cluster-scoped version. It uses the same language and selectors but applies across all namespaces, which is useful for node policies, global DNS interception, or rules that span multiple teams.
What a Cilium endpoint is
Every pod (and any process that Cilium manages traffic for) is represented inside Cilium as an endpoint. An endpoint is essentially Cilium’s view of a workload: its labels, Security Identity, policies, and network state.
When you write a policy with an endpointSelector, you’re telling Cilium “apply this rule to the endpoints whose labels match this selector.” Cilium uses that to program the eBPF datapath on the node where each endpoint lives.
You can see endpoints on a node with:
kubectl -n kube-system exec -ti ds/cilium -- cilium endpoint list
-
ENDPOINT: This is Cilium’s internal endpoint ID on that node. In this case,
24. You’ll use this ID if you runcilium endpoint get 24for detailed info. -
POLICY (ingress / egress): Shows whether policy enforcement is active on this endpoint. “Disabled” means there are no Cilium policies selecting it yet for that direction, so all traffic is allowed. Once you create a CiliumNetworkPolicy with an
ingressoregresssection matching this endpoint, this field will flip to “Enabled”. -
IDENTITY: The numeric Security Identity assigned to the set of identity-relevant labels for this endpoint (
69014here). Cilium uses this number in the datapath to represent the workload. -
LABELS (source:key=value): The full list of labels that Cilium knows for this endpoint. The prefix shows where the label came from (
k8s:means Kubernetes label). These are the labels you match on in your policies. In the example, it includesapp=minio, the namespace, the service account, and some Helm-related labels. -
IPv4 / IPv6: The IP addresses currently assigned to that pod. Notice you never use them directly in your policies; Cilium maps them to the Security Identity automatically. Note: there is the ability to specify CIDR-based filtering in a Cilium Network Policy as well, but this is recommended not to be used to for filtering when it comes to Pod traffic inside the cluster.
-
STATUS: Shows the endpoint’s state from Cilium’s perspective (“ready” means it’s healthy and being managed).
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
ENFORCEMENT ENFORCEMENT
24 Disabled Disabled 69014 k8s:app.kubernetes.io/managed-by=Helm 10.0.0.247 ready
k8s:app=minio
k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=minio
k8s:io.cilium.k8s.policy.cluster=kind
k8s:io.cilium.k8s.policy.serviceaccount=quickstart-sa
k8s:io.kubernetes.pod.namespace=minio
k8s:v1.min.io/console=quickstart-console
k8s:v1.min.io/pool=ss-0
This view is invaluable when troubleshooting, and we’ll cover this towards the end of the blog post.
Labels and Security Identity
Continue reading Cilium Network Policies, from first principles to production