Skip to content
HoginHogin
Go back

Linkerd in the ambient era: why the lightest mesh still matters when everyone goes sidecar-less

10 мин чтения

In 2025–2026 the service mesh went through a paradigm shift: Istio moved everyone to ambient, Cilium spliced itself into the traffic path via eBPF, and the word “sidecar” became an insult in architecture discussions. Linkerd never dropped the sidecar — and never died: the 2.17 stable branch delivered federated services, egress metrics and rate limiting, while edge releases keep shipping almost weekly in 2026. The honest question: when is a megabyte-scale Rust proxy in the pod still the right answer, and when is it time to go ambient.

Why now: 2.17-stable closed the three big complaints about Linkerd — “multicluster means a separate extension”, “egress is a black hole”, “there is no rate limiting” — and Buoyant moved the project to a sustainability model, which the industry has spent two years misreading as “Istio won”. While everyone buries it, it keeps releasing.

Table of contents

Open Table of contents

Three generations of mesh, briefly

The first generation was a shared proxy per host: Linkerd 1.x, a heavy JVM monster, died first. The second was a sidecar in every pod: Istio put Envoy next to the app, Linkerd 2 shipped its own proxy rewritten from Scala to Rust precisely for the weight. The third is “sidecar-less”: Istio ambient (ztunnel per node plus waypoint for L7 — covered separately) and Cilium (eBPF in the kernel plus Envoy per node — also covered).

The main thing ambient made obvious: “sidecar-less” does not mean “no proxy”. The proxy just moves — out of the pod onto the node, and for L7 policies also into a waypoint next to the service. The argument was never “proxy or not”, it was “where do you pay”: RAM in every pod, or an agent on every node.

What arrived in 2.17

Not a revolution — three old gaps closed, plus a new release model:

One corollary of this model: the project is financed through Buoyant support, not through fashion. “Linkerd is dead” is the most durable myth of the last two years.

Footprint: megabytes of Rust vs per-node agents

The three data plane layouts of 2026:

The three data plane layouts: who eats what

Arithmetic for a node with 30 pods: classic Istio — 30 × 150 MB ≈ 4.5 GB on sidecars alone; Linkerd — ~450 MB for the same pods; ambient — tens of MB per node, but every service that needs L7 policies adds a waypoint. The conclusion is not “who eats less” but where you are willing to pay: Linkerd — slightly more RAM for the simplicity of a pod → pod model and proxy isolation inside the pod; ambient — less RAM at the cost of an extra network hop, a second entity in your diagnostics, and a waypoint matrix for L7.

On small clusters — tens of nodes, tens of services — absolute numbers are small for everyone, and the choice is driven not by RAM but by how many entities you can explain to the on-call engineer.

mTLS and policy without a zoo

mTLS is on by default for every meshed pod: each pod gets a certificate carrying its ServiceAccount identity, rotation is automatic, and there is one trust root per cluster (or one shared across several — for federated services).

Policy is built around identity, not IP addresses. The Server CRD describes what you protect (pods plus a port), ServerAuthorization — who is allowed in. Until a Server exists, meshed pods talk to each other over mTLS without a single line of configuration; the moment a Server claims a port, all traffic without an mTLS identity is cut off. The default “mesh → mesh allowed, everything else denied” covers most real policies, and it does not need a matrix of AuthorizationPolicy objects per service pair, which is how it tends to go in Istio.

Policy: mTLS identity instead of IPs

For HTTP-level rules — path, method, headers — there is a first-party HTTPRoute from policy.linkerd.io targeting a Server, and in 2.17 an AuthorizationPolicy on top of routes. But that is an option, not a mandatory layer.

Comparison in one table

Linkerd 2.17Istio AmbientCilium mesh
Data planeRust proxy in every podztunnel per node + waypoint for L7eBPF in the kernel + Envoy per node
L7 policyHTTPRoute (policy.linkerd.io)HTTPRoute via waypointGateway API + CiliumNetworkPolicy
RAM (node, 30 pods)~450 MB in sidecarstens of MB/node + waypoint per servicenearly 0 for eBPF + Envoy per node
mTLSout of the box, identity = ServiceAccountout of the box in ztunnelvia SPIRE/cert-manager — enable and configure
Multiclusterfederated services (2.17)east-west gatewayclustermesh
Rate limitinglocal, in HTTPRoute (2.17)on waypoint/EnvoyEnvoy configs
Entry barrierone eveningweeksin between, depends on appetite for living in the kernel

The fork is simple: maximum functionality and heterogeneous policies — ambient; if the cluster already runs Cilium and it is your only CNI — its mesh is nearly free; minimal weight and the simplest story to tell — Linkerd.

What you need to run Linkerd on a test cluster

Prerequisites: a test cluster (kind/k3s), Helm 3, the step tool for generating test identity certificates (production uses cert-manager or a corporate PKI), and a demo app of one Deployment plus one Service.

1. Certificates and install. Linkerd lands as two Helm charts plus the viz extension for metrics:

step certificate create root.linkerd.cluster.local ca.crt ca.key \
  --profile root-ca --no-password --insecure
step certificate create identity.linkerd.cluster.local issuer.crt issuer.key \
  --profile intermediate-ca --no-password --insecure \
  --ca ca.crt --ca-key ca.key

helm repo add linkerd https://helm.linkerd.io/stable && helm repo update
helm install linkerd-crds linkerd/linkerd-crds \
  --namespace linkerd --create-namespace --wait
helm install linkerd-control-plane linkerd/linkerd-control-plane \
  --namespace linkerd \
  --set-file identityTrustAnchorsPEM=ca.crt \
  --set-file identity.issuer.tls.crtPEM=issuer.crt \
  --set-file identity.issuer.tls.keyPEM=issuer.key
helm install linkerd-viz linkerd/linkerd-viz \
  --namespace linkerd-viz --create-namespace

2. The “before” measurement. Deploy the app as usual and capture its consumption:

kubectl -n demo apply -f app.yaml
kubectl -n demo top pods

3. Mesh. Injection is one namespace annotation and a restart:

kubectl annotate ns demo linkerd.io/inject=enabled
kubectl -n demo rollout restart deploy/web

A linkerd-proxy container appears next to every app container. Re-run kubectl -n demo top pods — that is your “after”: a typical delta of 10–20 MB RSS and negligible CPU per pod.

4. Policy. A Server plus a ServerAuthorization for one service:

apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
  name: web
  namespace: demo
spec:
  podSelector:
    matchLabels: { app: web }
  port: http
---
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
  name: web
  namespace: demo
spec:
  server:
    name: web
  client:
    meshUsers:
      - demo.gateway   # namespace.serviceaccount

That is it: the web port is open only to meshed clients with the demo/gateway identity.

5. Rate limit (2.17 experiment). The limit lives in an HTTPRoute rule targeting the same Server — the syntax is experimental, check the docs for your version:

apiVersion: policy.linkerd.io/v1alpha1
kind: HTTPRoute
metadata:
  name: web-route
  namespace: demo
spec:
  targetRefs:
    - { kind: Server, name: web }
  rules:
    - matches:
        - { path: { type: PathPrefix, value: / } }
      filters:
        - type: RateLimitFilter   # local token bucket in the proxy
          rateLimit:
            type: Local
            local:
              tokenBucket: { maxTokens: 20, tokensPerFill: 10, fillInterval: 1s }

How to verify it works

linkerd check
linkerd viz stat deploy -n demo
linkerd viz edges deploy -n demo
kubectl run probe --rm -it --image=curlimages/curl --restart=Never -- \
  curl -sS http://web.demo.svc.cluster.local:8080/

If it still answers from outside the mesh, the Server is not selecting the port (check port: http — the port name in the Deployment must match).

Pitfalls

Bottom line

Ambient did not kill Linkerd — it retired the sidecar argument. While the choice was “Envoy at 150 MB in every pod” or “nothing”, sidecar-less looked like the only option; now the choice is honest: a per-node agent with ztunnel and waypoint, or a megabyte-scale Rust proxy with a pod → pod model. For small and medium clusters Linkerd remains the cheapest way to get mTLS, per-service metrics and a sane policy model in one evening, and 2.17 closed the multicluster and egress gaps. The price is a sidecar in every pod and a bet on a quieter project. Hundreds of nodes with strict L7 policies everywhere — look at ambient or Cilium; tens of nodes — first count what exactly you are saving.


Share this post:

Next Post
Tailscale Kubernetes Operator: a private cluster without public IPs — ClusterIP straight into your tailnet