Kubernetes allows applications to run across multiple pods, services, and namespaces. By default, pods can often communicate with other pods across the cluster, provided the network environment permits it.
This is convenient when you are getting started, but it can create security risks in production. For example, a frontend application may need to access a backend API, while the database should accept connections only from that backend.
Kubernetes NetworkPolicies help control this communication. They let you define which network traffic is allowed into selected pods and which traffic those pods can send out.
In this article, we will create a practical NetworkPolicy setup, restrict access between frontend, backend, and database pods, and explain how to verify that the rules work as expected.
What Is a Kubernetes NetworkPolicy?
A NetworkPolicy is a Kubernetes resource that defines network traffic rules for a selected group of pods.
You can use it to control:
- Ingress traffic, which enters selected pods.
- Egress traffic, which leaves selected pods.
- Traffic between pods in different namespaces.
- Traffic to or from selected IP address ranges, where supported by the policy rules and network implementation.
NetworkPolicies work with pod labels, namespace labels, ports, and IP address ranges. They are not traditional firewall rules attached directly to a node.
An important detail is that creating a NetworkPolicy does not guarantee that traffic will be restricted. Your cluster's network plugin must support and enforce NetworkPolicies. If it does not, the policy resource may exist without providing the expected isolation.
How NetworkPolicy Rules Work
Kubernetes NetworkPolicies use a combination of pod selection and traffic rules.
| Setting | Purpose |
|---|
podSelector | Selects the pods to which the policy applies |
policyTypes | Specifies whether the policy controls ingress, egress, or both |
ingress | Defines allowed incoming traffic |
egress | Defines allowed outgoing traffic |
namespaceSelector | Selects namespaces using their labels |
port | Restricts traffic to a particular destination port |
NetworkPolicies are additive. If multiple policies select a pod, the allowed traffic is the combined set of traffic permitted by those policies for the relevant direction.
Once a pod is isolated for ingress or egress, traffic in that direction is denied unless an applicable policy allows it. Traffic in a direction for which the pod is not isolated remains subject to the cluster's existing networking behavior.
Step 1: Create Separate Namespaces
For this example, we will separate the application into three namespaces:
- frontend: contains the user-facing application.
- backend: contains the API.
- database: contains the database workload.
Create the namespaces:
apiVersion: v1
kind: Namespace
metadata:
name: frontend
---
apiVersion: v1
kind: Namespace
metadata:
name: backend
---
apiVersion: v1
kind: Namespace
metadata:
name: database
Save this as namespaces.yaml and apply it:
kubectl apply -f namespaces.yaml
Namespaces help organize workloads, but they do not automatically block network communication between them. NetworkPolicies are needed when you want to restrict pod traffic.
Step 2: Deploy Sample Pods
Next, create a simple pod in each namespace. These pods are for demonstrating network rules, not for running a production application.
apiVersion: v1
kind: Pod
metadata:
name: frontend
namespace: frontend
labels:
app: frontend
spec:
containers:
- name: web
image: nginx:stable
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: backend
namespace: backend
labels:
app: backend
spec:
containers:
- name: api
image: nginx:stable
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: database
namespace: database
labels:
app: database
spec:
containers:
- name: db
image: nginx:stable
ports:
- containerPort: 5432
Save the file as sample-pods.yaml and apply it:
kubectl apply -f sample-pods.yaml
The database pod uses NGINX only to provide a simple test endpoint. It is not a real database and does not listen on port 5432 just because that port is declared in the pod specification.
In a real deployment, replace these demonstration pods with the appropriate application workloads and actual listening ports.
Step 3: Allow Frontend Traffic to the Backend
Suppose the frontend needs to call the backend API on TCP port 8080. The backend should accept traffic from pods labeled app=frontend in the frontend namespace.
Create this policy in the backend namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: backend
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: frontend
podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Save it as allow-frontend-to-backend.yaml and apply it:
kubectl apply -f allow-frontend-to-backend.yaml
This policy selects backend pods labeled app=backend and allows incoming TCP traffic on port 8080 from frontend pods labeled app=frontend.
The namespaceSelector and podSelector are in the same item under from. That means both conditions must match: the source pod must have the required label and belong to the selected namespace.
The policy does not create a listener on port 8080. The backend application must actually listen on that port for connections to succeed.
Step 4: Restrict Access to the Database
Now suppose only backend pods should connect to the database on TCP port 5432.
Create a policy in the database namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend-to-database
namespace: database
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: backend
podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 5432
Apply the policy:
kubectl apply -f allow-backend-to-database.yaml
The policy allows matching backend pods to connect to the selected database pods on TCP port 5432.
Other incoming connections to those database pods are denied by this policy only if the pods are isolated for ingress and no other applicable NetworkPolicy permits the traffic.
For a secure design, review all policies that select the database pods. Since policies are additive, another policy that allows broader traffic could also permit connections.
Step 5: Deny Unwanted Incoming Traffic
A common security requirement is to deny incoming traffic by default and then allow only the connections that are needed.
You can create a default-deny ingress policy in a namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: database
spec:
podSelector: {}
policyTypes:
- Ingress
This policy selects all pods in the database namespace and isolates them for ingress. No incoming connections are permitted unless another applicable NetworkPolicy allows them.
Because policies are additive, the allow-backend-to-database policy can still permit the intended backend traffic.
Be careful when applying default-deny policies to existing namespaces. They can break application traffic, health checks, monitoring, or other required connections if the necessary allow rules are missing.
Also, default-deny ingress does not restrict outgoing connections. Egress needs its own policy.
Step 6: Control Outgoing Traffic with Egress Policies
Ingress policies control traffic entering selected pods. Egress policies control traffic leaving selected pods.
For example, a backend application might need to connect to the database but should not be allowed to send traffic to arbitrary destinations.
An egress policy can allow the backend pods to connect to the database namespace on port 5432:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-egress-to-database
namespace: backend
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: database
podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
This isolates selected backend pods for egress and allows the specified database traffic.
Once egress isolation is enabled, other outgoing traffic is denied unless another applicable policy allows it. The backend may also need access to DNS, authentication services, external APIs, or telemetry endpoints.
If those dependencies are required, add explicit rules for them. Otherwise, the application may be unable to resolve service names or contact external systems.
Step 7: Verify That the Policies Work
After applying the policies, check that the resources exist:
kubectl get networkpolicy -A
Inspect a specific policy:
kubectl describe networkpolicy allow-backend-to-database \
-n database
Check the pod labels and namespace assignments:
kubectl get pods -n backend --show-labels
kubectl get pods -n database --show-labels
You can also inspect the standard namespace labels:
kubectl get namespace frontend --show-labels
kubectl get namespace backend --show-labels
kubectl get namespace database --show-labels
For an actual connectivity test, use pods that have a suitable network diagnostic tool and an application listening on the target port.
For example, if the backend contains a shell and curl, you might test an HTTP endpoint from the backend namespace. The destination should be the database service or pod endpoint appropriate to your environment, and the test protocol must match the application.
Do not rely on the sample NGINX database pod to prove connectivity on port 5432. It does not run a PostgreSQL server.
If a connection fails, check the NetworkPolicy rules, pod and namespace labels, actual listening ports, routing, service configuration, and network plugin behavior.
Common Mistakes to Avoid
Assuming namespaces block traffic automatically
Namespaces provide organization, not automatic network isolation. Use NetworkPolicies and verify that your network plugin enforces them.
Selecting the wrong pods
A policy affects only the pods matched by its podSelector. If labels do not match, the intended policy may not protect the workload.
Misunderstanding selector logic
A namespaceSelector and podSelector in the same item under from or to must both match. Separate items in the list represent alternative allowed sources or destinations.
Forgetting that policies are additive
A restrictive policy does not cancel an allow rule from another applicable policy. Review all policies that select the same pods.
Blocking DNS or required services
Default-deny egress policies can prevent DNS lookups and outbound API calls. Add rules for the required destinations and ports.
Assuming the port declaration starts a service
A NetworkPolicy permits or denies traffic; it does not configure the application to listen on a port. The container and service must use the correct ports.
Ignoring network plugin support
The existence of a NetworkPolicy object does not prove that traffic is being enforced. Confirm that the installed networking implementation supports NetworkPolicies.
Best Practices for Production Clusters
- Separate workloads by namespace where it helps define clear security boundaries.
- Use stable, meaningful pod labels in NetworkPolicies.
- Start with default-deny policies where appropriate, then allow only required traffic.
- Review both ingress and egress requirements.
- Permit necessary DNS, monitoring, health-check, and external service traffic.
- Keep policies under version control and review them with application changes.
- Test allowed and denied connections before production deployment.
- Audit all policies that select sensitive workloads.
- Confirm network plugin support and enforcement.
- Combine network isolation with authentication, authorization, and application-level security.
Summary
Kubernetes NetworkPolicies let you control communication between selected pods using labels, namespaces, ports, and traffic direction.
By allowing frontend traffic to the backend and restricting database access to backend workloads, you can reduce unnecessary communication inside a cluster.
A reliable implementation requires more than applying YAML. Check selector logic, account for additive policies, allow required dependencies, and verify actual network behavior. When used carefully, NetworkPolicies provide an important layer of network isolation for Kubernetes applications.
Join the conversation! Your thoughts help the community grow.