Dash0 Raises $110M Series B at $1B Valuation

  • 11 min read

What Is Ingress and Egress in Kubernetes?

The confusion here usually comes from "ingress" meaning two different things. There's the Kubernetes Ingress resource, which routes external HTTP/HTTPS traffic into your cluster, and then there's ingress as a direction — any traffic entering the cluster from outside. Egress is the other direction. It has no dedicated resource; you control it through NetworkPolicy rules.

The conceptual layer: ingress and egress as traffic directions

Ingress traffic is anything entering the cluster from the outside world: a browser request, a webhook payload, a deployment pipeline. Pods are isolated by default. Nothing reaches them until you explicitly expose them.

Egress is the reverse. Traffic leaves the cluster when your pods reach out to something: a service calling a payment API, a backend querying an external database. By default, pods are not isolated for egress. They can reach anything on the network unless you restrict them.

That asymmetry is where most teams slip up. Ingress requires configuration to open access; egress requires configuration to close it. Most teams configure ingress carefully and leave egress wide open.

The Kubernetes Ingress resource

The Ingress API object manages external HTTP and HTTPS access to services inside your cluster. Instead of giving each service its own load balancer, an Ingress routes traffic to multiple services from a single entry point based on hostnames and URL paths.

To use it, you need the Ingress resource (which defines the routing rules) and an Ingress controller that reads those rules and configures the actual proxy. Kubernetes ships the API but not the controller. You install one separately. NGINX, Envoy, and Traefik are common choices.

Here's a basic Ingress routing traffic to two services by URL path:

yaml
123456789101112131415161718192021222324
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: users-service
port:
number: 80
- path: /orders
pathType: Prefix
backend:
service:
name: orders-service
port:
number: 80

Once applied, run kubectl describe ingress app-ingress to verify the rules loaded and the controller assigned an external address:

123456789101112
Name: app-ingress
Namespace: default
Address: 34.120.0.1
Rules:
Host Path Backends
---- ---- --------
api.example.com /users users-service:80
/orders orders-service:80
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 10s nginx-ingress-controller Scheduled for sync

If the Address field is still empty after a couple of minutes, the controller hasn't picked up the resource. Check its logs for sync errors and mismatched ingressClassName values:

1
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx

The Ingress API is frozen, and ingress-nginx is gone

The Kubernetes project has frozen the Ingress API. No new features are coming. The recommended path for new clusters is Gateway API, which replaces Ingress with a more expressive set of resources: Gateway, HTTPRoute, GRPCRoute, and others. Gateway API v1.0 became generally available in 2023 and is where all active development is happening.

More urgently: ingress-nginx was retired in March 2026. It no longer receives bug fixes or security patches. If your cluster depends on it, the official ingress-nginx migration guide covers the transition, and the ingress2gateway tool handles most of the resource conversion automatically. Envoy Gateway, Traefik, and kgateway (Cloud Native Computing Foundation sandbox) are common replacements. If you're migrating off ingress-nginx, Dash0's guide to observing Kubernetes ingress controllers with OpenTelemetry covers what to instrument and what to watch during the transition.

To be clear: the Ingress API itself is not being removed. If you have existing Ingress resources running on a controller that isn't ingress-nginx, there's no immediate pressure to change anything.

Controlling egress with NetworkPolicy

There's no Egress API object. Egress is managed through NetworkPolicy resources. By default, a pod in a namespace with no policies can reach any destination, internal or external. For anything handling sensitive data, that default is a liability.

A NetworkPolicy restricting a backend pod to only reach a payment API over HTTPS looks like this:

yaml
123456789101112131415161718
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-egress
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 203.0.113.0/24 # Payment provider IP range
ports:
- protocol: TCP
port: 443

Once applied, those backend pods can only connect to that CIDR (Classless Inter-Domain Routing) block on port 443. Everything else is dropped. You can combine ingress and egress rules in the same NetworkPolicy if you need to control both directions for the same pod set.

To deny all outbound traffic from a pod, add an Egress policyType with no egress entries. The absence of rules is intentional:

yaml
1234567
spec:
podSelector:
matchLabels:
app: restricted-service
policyTypes:
- Egress
# No egress rules = all outbound blocked

One thing to sort out before writing any of this: NetworkPolicy enforcement requires a CNI (Container Network Interface) plugin that supports it. Calico and Cilium both do. Flannel on its own does not — apply a NetworkPolicy on a Flannel-only cluster and it will be silently ignored, with no indication anything is wrong. (Canal, a Flannel + Calico combination, does enforce policies.)

Common pitfalls

The naming collision. When someone says "ingress isn't working," they might mean the Ingress API object, the controller process, or just inbound traffic in general. NetworkPolicy resources add another layer: they use ingress and egress as rule type labels inside policyTypes. When debugging, be specific about which layer you mean. "The nginx-ingress-controller pod isn't syncing" is a useful problem statement; "ingress is broken" is not.

Egress being open by default. Most networking guides focus on opening ingress access, so egress ends up an afterthought until it isn't. A compromised pod can freely call external infrastructure if no egress policies are in place. Egress policies belong in your baseline cluster setup, not added retroactively after an incident.

NetworkPolicy rules are additive, not ordered. If multiple policies select the same pod, the allowed traffic is the union of all matching rules. There's no explicit deny that overrides an allow from another policy. If access isn't being blocked the way you expect, check whether a broader policy in the same namespace is quietly permitting it.

IP rewriting at the load balancer. Some cloud load balancers rewrite the source IP of incoming traffic before it reaches the cluster. When that happens, ingress NetworkPolicy rules that filter on source IP may not work as expected: the policy sees the load balancer's IP, not the original client's. This varies by cloud provider and CNI. Verify source IP preservation in your provider's docs before relying on it.

Final thoughts

Ingress and egress are the two sides of the cluster boundary. Ingress gets more attention because it's what makes applications accessible, but egress is where you'll get burned if you're not paying attention. An uncontrolled egress path is a data exfiltration risk, and it's a much easier problem to prevent than fix after the fact.

If you're building new infrastructure, use Gateway API instead of the frozen Ingress API. Write egress NetworkPolicy rules early. And then actually verify that your policies behave the way you expect once load balancers and CNI plugins are in the mix, because rules that look correct in YAML don't always survive contact with production infrastructure.

Dash0's Kubernetes monitoring shows you actual traffic crossing your cluster boundary alongside pod health, resource metrics, logs, and distributed traces, so unexpected external connections show up in your dashboards before they show up in an incident report. Start a free trial to see your cluster's metrics, logs, and traces in one place. No credit card required.