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.