The Linkerd data plane proxy, a tiny but mighty Rust program, doesn’t just sit there; it actively rewrites network traffic for your application to enable crucial service mesh features.
Let’s see it in action. Imagine a simple client pod talking to a server pod in Kubernetes. Without Linkerd, the client’s curl command would directly hit the server’s IP and port.
# Client pod exec
kubectl exec -it my-client-pod -- curl http://my-server-service:8080/hello
Now, let’s inject the Linkerd proxy into both pods. The linkerd inject command annotates your deployment, telling the Kubernetes admission controller to add the proxy container.
# Snippet from a Deployment after 'linkerd inject'
spec:
template:
spec:
containers:
- name: my-app
# ... app config ...
- name: linkerd-proxy
image: public.registry.services.linkerd.io/linkerd2/proxy:stable-2.13.3
ports:
- containerPort: 4140 # Default proxy outbound port
- containerPort: 4191 # Default proxy admin port
# ... other proxy config ...
After injection, the curl command from the client pod still looks like it’s going directly to http://my-server-service:8080/hello.
# Client pod exec (same command)
kubectl exec -it my-client-pod -- curl http://my-server-service:8080/hello
But here’s the magic: the traffic doesn’t go directly to the server pod’s IP. Instead, it’s intercepted. The client pod’s network namespace is configured via iptables (handled by the proxy’s init container) to redirect all outbound traffic destined for port 8080 to the linkerd-proxy container on port 4140.
The linkerd-proxy then inspects this request. It knows, through Linkerd’s control plane, that my-server-service is actually an instance of a service that should have observability and resilience features. The proxy establishes a new connection to the actual server pod’s IP and port (which it discovers via Kubernetes DNS and its own internal service discovery mechanisms). On this new connection, it forwards the original request, but now with Linkerd’s metadata (like trace IDs and TLS encryption).
The server pod’s linkerd-proxy receives this traffic on its inbound port (4140 by default). It strips the Linkerd metadata, forwards the original request to the application container on its actual application port (e.g., 8080), and then does the same in reverse for the response.
This redirection is orchestrated by iptables rules injected by the proxy’s init container when the pod starts. These rules intercept traffic based on destination IP and port and send it to the proxy’s listening ports.
The core problem Linkerd solves with this sidecar model is decoupling application concerns from network concerns. Instead of embedding libraries for metrics, tracing, retries, and mTLS into every application, Linkerd injects a uniform proxy that handles these uniformly across all services. This means you can adopt these features without modifying your application code.
The proxy’s internal configuration is dynamic and pushed from the Linkerd control plane. It doesn’t have static configuration files for service discovery or routing. Instead, it communicates with the control plane’s controller component to learn about services, endpoints, and policies. The proxy exposes an admin interface on port 4191 which provides detailed metrics, configuration status, and diagnostic information, accessible via kubectl port-forward.
# Port-forward to the proxy's admin port on a pod
kubectl port-forward -n linkerd deploy/my-app-deployment 4191:4191
# Then access via browser or curl
curl http://localhost:4191/metrics
This allows you to see exactly what the proxy is doing, including connection counts, latency metrics, and TLS status for connections it’s managing.
A common point of confusion is how the proxy determines the actual destination IP for a service. It leverages Kubernetes’ DNS service discovery. When the client proxy sees a request for my-server-service:8080, it performs a DNS lookup for my-server-service.my-namespace.svc.cluster.local. The result of this lookup is a list of IP addresses for the server pod’s endpoints. The Linkerd proxy then selects one of these endpoints and establishes a direct TCP connection.
The most surprising thing about the Linkerd proxy’s operation is its ability to perform mutual TLS (mTLS) encryption for all traffic flowing between services without any application-level configuration. The proxy automatically obtains TLS certificates from the Linkerd control plane and uses them to encrypt all outbound connections it initiates and decrypts all inbound connections it receives. This security layer is established transparently, meaning your application code doesn’t need to be aware of mTLS at all; it just sends and receives plain network traffic.
Once all services have the proxy injected and are communicating successfully, the next challenge is often configuring advanced traffic management policies like retries, timeouts, and canary deployments.