Linkerd doesn’t actually "trace" requests in the distributed tracing sense of OpenTelemetry or Jaeger; it inspects the metadata of requests as they traverse the service mesh.
Let’s watch Linkerd’s tap command in action. Imagine you have a frontend service that calls an api service.
First, make sure you have Linkerd installed and your services are running.
# Deploy sample applications (if you don't have any)
# kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd2/main/examples/voting-app/voting-app.yml
# You'll need to have your kubectl context set to the namespace where your apps are running.
# Let's assume they're in the 'default' namespace.
Now, let’s tap requests from the frontend pod to the api pod.
# Find the frontend pod name
FRONTEND_POD=$(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}')
# Tap the requests
# This command will stream requests from the frontend pod.
# We're filtering for requests *to* the api service in the 'default' namespace.
linkerd tap -n default $FRONTEND_POD --to api.default.svc.cluster.local
You’ll see output like this, streaming in real-time:
response {
grpc: {
grpc_status: OK
}
http: {
status: <nil>
}
duration: 610µs
source: default/frontend-79b7d59c7d-abcde
destination: default/api-7f8c9d0e1f-xyz
request_id: 12345678-abcd-ef01-2345-6789abcdef01
authority: api.default.svc.cluster.local
path: /
method: GET
tls: {
verifies: true
đời: mTLS
}
}
This output is rich. It shows:
sourceanddestination: Which pods are talking.authority,path,method: The request details.duration: How long the request took from the perspective of the proxy.tls: Crucially, it confirmsmTLSis active and verified.
The problem this solves is the "it works on my machine" syndrome in distributed systems. When you have multiple services, especially in Kubernetes, understanding the flow of traffic, its latency, and whether it’s secured can be incredibly difficult. linkerd tap gives you a direct window into that communication, bypassing application-level logging for a raw view.
Internally, Linkerd injects a lightweight proxy (written in Rust) into each pod. This proxy intercepts all inbound and outbound traffic. The linkerd tap command connects to the proxy of the specified pod, instructing it to mirror and stream the relevant metadata of requests it’s handling. It’s not tracing end-to-end; it’s observing the conversation at each hop through the proxy.
The levers you control are primarily through filtering and the target pod. You can tap:
- A specific pod (
$FRONTEND_POD): As shown above. - All pods in a namespace (
-n default):linkerd tap -n default --all - Traffic to a specific service (
--to api.default.svc.cluster.local): Filters the stream to only show requests destined forapi. - Traffic from a specific deployment (
--from deploy/frontend): Filters the stream to only show requests originating from thefrontenddeployment. - Specific ports (
--port 8080): To narrow down to a particular application port.
The tls field is key. When you see đời: mTLS, it means Linkerd’s automatic mutual TLS is active and successfully established for that specific request. If it shows đời: tls_not_provided, it means mTLS is not enabled for that specific communication path, or the destination pod isn’t meshed.
What most people don’t realize is that linkerd tap can also show you response details if you add the --from flag to specify the destination pod. For example, linkerd tap -n default $FRONTEND_POD --to api.default.svc.cluster.local --from api-pod-name would show you the response from the API back to the frontend, including its status code and duration. This gives you a more complete picture of the request/response cycle for a single hop.
The next thing you’ll likely want to inspect is the aggregated metrics for these requests, which you can see using linkerd stat.