What a service mesh actually gives you
- mTLS — every service-to-service connection inside the cluster is automatically encrypted and mutually authenticated. No app code changes; the sidecars handle it. Tells you definitively who is calling what.
- Retries + timeouts — per-route retry budgets and per-call timeouts configured by CRD, not by editing each app's HTTP client.
- Uniform L7 metrics — the proxies emit per-route success-rate, P50/P95/P99 latency, request volume. One Grafana dashboard covers every service in the cluster.
- Traffic splitting — route 5% of traffic to a canary version, 95% to stable, observed via the same metrics. Promote based on success rate.
- mTLS-based authorization — per-server "only these clients may call me" policies enforced by the proxy, not the app.
Install
# Install the CLI on your workstation
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install-edge | sh
export PATH=$PATH:$HOME/.linkerd2/bin
linkerd version
# Pre-flight check (does your cluster meet requirements?)
linkerd check --pre
# Install the control plane (two steps: CRDs first, then control plane)
linkerd install --crds | kubectl apply -f -
linkerd install | kubectl apply -f -
# Verify
linkerd check
~6 pods come up in the linkerd namespace. The whole control plane uses ~250 MB RAM — an order of magnitude less than Istio's.
Add the extensions
# Viz: Grafana + Prometheus + the Linkerd dashboard
linkerd viz install | kubectl apply -f -
linkerd viz check
# Multicluster: for federated mesh across multiple K8s clusters
linkerd multicluster install | kubectl apply -f -
# Jaeger: distributed tracing
linkerd jaeger install | kubectl apply -f -
The Linkerd dashboard:
linkerd viz dashboard &
# Opens http://localhost:50750 — per-namespace health, per-service traffic, live tap of any service's requests
Inject the mesh into your app
Annotate a namespace or individual deployment with linkerd.io/inject: enabled; the admission webhook adds the proxy sidecar at pod-create time:
kubectl annotate namespace myapp linkerd.io/inject=enabled
kubectl rollout restart deployment -n myapp
After restart, each pod has 2 containers (your app + linkerd2-proxy) and immediately:
- mTLS to other meshed services in the cluster (auto-negotiated; PKI handled by Linkerd's identity service)
- Metrics emitted via Prometheus exporter
- Discoverable in the Viz dashboard
Apps don't change. localhost:80 from inside the pod still works; the proxy is transparent on outbound + inbound.
The first metrics: linkerd viz
# Per-deployment success rate + RPS + latency
linkerd viz stat deploy -n myapp
# Live tap of requests (see actual req/resp metadata)
linkerd viz tap deploy/api -n myapp
# Routes view (HTTP path-level)
linkerd viz routes deploy/api -n myapp
The tap command in particular: every request entering / leaving the proxied pod is streamable in real time, with status code, latency, peer identity. No application instrumentation needed.
Traffic split: canary deploys
apiVersion: split.smi-spec.io/v1alpha1
kind: TrafficSplit
metadata:
name: api-rollout
namespace: myapp
spec:
service: api
backends:
- service: api-stable
weight: 950
- service: api-canary
weight: 50 # 5% to canary
Or use the more modern Gateway API with the HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-routing
spec:
parentRefs:
- name: api
rules:
- backendRefs:
- name: api-stable
weight: 95
- name: api-canary
weight: 5
Combined with the Linkerd dashboard, you watch the canary's success rate + latency in real time; promote (shift weights) or rollback based on numbers, not gut feel.
Server-level authorization
apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
name: api-admin
namespace: myapp
spec:
podSelector: { matchLabels: { app: api } }
port: 8080
proxyProtocol: HTTP/2
---
apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
name: api-admin-allow
namespace: myapp
spec:
targetRef:
group: policy.linkerd.io
kind: Server
name: api-admin
requiredAuthenticationRefs:
- group: policy.linkerd.io
kind: MeshTLSAuthentication
name: admin-allowed
Only meshed pods presenting the right mTLS identity (defined in the MeshTLSAuthentication) can reach api:8080. Misconfigured clients get rejected at the proxy — the app never even sees the request.
mTLS is automatic
The Linkerd identity service operates as a built-in CA, issues short-lived (default 24h) certs to every proxy at pod startup, rotates them on schedule. Every meshed-to-meshed connection in the cluster is TLS 1.3 with mutual auth. The dashboard shows which connections are TLS-protected; for connections to non-meshed external services (databases, public APIs), they remain plaintext unless you explicitly configure egress TLS.
What Linkerd doesn't do
- Layer 7 routing across protocols — HTTP/1, HTTP/2, gRPC are first-class; arbitrary TCP-protocol routing is more limited than Istio (no full Envoy filter chains).
- Ingress — Linkerd assumes you're using an Ingress controller (Traefik, NGINX, Cilium Gateway) for the cluster's edge. The mesh is east-west; the ingress is north-south.
- Heavy traffic-shaping (advanced rate limits, mirror-this-percent-to-that-service for load testing) — partial support; for the full toolbox, Istio still wins.
Linkerd vs Istio in 2026
- Linkerd — smaller, simpler, faster proxy (Rust), simpler config. CNCF-graduated. The right default.
- Istio Ambient Mesh (the 1.20+ sidecar-less mode) is the most credible Istio path; uses ztunnel + waypoint proxies. Smaller footprint than classic Istio. Still more conceptual surface than Linkerd; pick when you need its richer L7 features.
- Cilium Service Mesh (see Cilium tutorial) — eBPF-based; integrates with Cilium CNI. Compelling if you're already on Cilium for networking.
For "we want service-mesh benefits without a six-month adoption process," Linkerd is the most direct path in 2026.