Architecture¶
Overview¶
Linkerd is an ultralight, security-first service mesh for Kubernetes. Its data plane uses linkerd2-proxy, a Rust-based micro-proxy designed specifically for the service mesh use case. The control plane runs as a small set of Kubernetes deployments. Linkerd prioritizes simplicity, minimal resource footprint, and automatic mTLS by default.
See also: index, service-mesh/linkerd/explanation, service-mesh/linkerd/how-to-guides
1. Control Plane Components¶
graph TB
subgraph "Control Plane (linkerd namespace)"
DEST["destination<br/>Service discovery & routing"]
IDENTITY["identity<br/>Certificate authority (mTLS)"]
INJECTOR["proxy-injector<br/>Mutating admission webhook"]
HEARTBEAT["heartbeat<br/>Health & usage reporting"]
WEB["web<br/>Dashboard UI"]
end
subgraph "Configuration"
K8s["Kubernetes API"]
SP["Service Profiles<br/>(linkerd.io/v1alpha2)"]
end
subgraph "Data Plane"
PROXY_A["linkerd2-proxy<br/>(Rust, per-pod sidecar)"]
PROXY_B["linkerd2-proxy<br/>(Rust, per-pod sidecar)"]
end
K8s -->|watches Services, Endpoints| DEST
SP -->|retries, timeouts, routing| DEST
DEST -->|service discovery info| PROXY_A
DEST -->|service discovery info| PROXY_B
IDENTITY -->|issues TLS certs| PROXY_A
IDENTITY -->|issues TLS certs| PROXY_B
K8s -->|pod creation events| INJECTOR
INJECTOR -->|injects proxy + init containers| PROXY_A
INJECTOR -->|injects proxy + init containers| PROXY_B
PROXY_A <-->|automatic mTLS| PROXY_B
destination Service¶
The destination component is the equivalent of a service discovery and routing engine in Linkerd:
- Watches Kubernetes Services, Endpoints, and ServiceProfile resources
- Provides dynamic service discovery information to linkerd2-proxy instances via gRPC
- Supplies retry budgets, timeouts, and load balancing configuration from Service Profiles
- Handles traffic splitting for canary and blue/green deployments
- Returns endpoint metadata including zone information for topology-aware routing
identity Controller¶
The identity component is the built-in certificate authority of Linkerd:
- Issues TLS certificates to every linkerd2-proxy instance using a trust anchor chain
- Certificate identity is derived from the Kubernetes Service Account of the pod
- Identity format:
<service-account>.<namespace>.serviceaccount.identity.linkerd.cluster.local - Manages automatic certificate rotation with a configurable lifetime (default 24 hours)
- Root of trust is the trust anchor certificate (ECDSA, provisioned during install)
proxy-injector¶
The proxy-injector is a Kubernetes Mutating Admission Webhook that automatically injects the linkerd2-proxy sidecar into pods:
- Triggered by the
linkerd.io/inject: enabledannotation on namespaces or pods - Adds the
linkerd2-proxycontainer and alinkerd-initcontainer (iptables setup) to the pod spec - Configures proxy startup parameters: destination service address, identity service address, proxy metrics port
- Supports annotation-based configuration for proxy resources (CPU, memory limits) and log levels
heartbeat¶
A CronJob-like component that periodically reports cluster health and anonymized usage data back to Buoyant (can be disabled). It also validates that the control plane is functioning correctly.
web (Dashboard)¶
Provides the Linkerd dashboard UI for visualizing service mesh health, traffic metrics, and topology. Optional -- not required for production data plane operation.
2. Data Plane -- linkerd2-proxy¶
sequenceDiagram
participant K8s as Kubernetes API
participant Injector as proxy-injector
participant Pod as Application Pod
participant Proxy as linkerd2-proxy
participant App as Application Container
participant Dest as destination service
participant Identity as identity controller
K8s->>Injector: Pod creation event (inject=enabled)
Injector->>Pod: Modified Pod spec with linkerd2-proxy + linkerd-init
Pod->>Proxy: linkerd2-proxy starts
Proxy->>Identity: Request TLS certificate (CSR)
Identity-->>Proxy: Issue certificate (SPIFFE-style identity)
Proxy->>Dest: Connect for service discovery
Dest-->>Proxy: Endpoints, routing, retry configuration
App->>Proxy: Outbound traffic (iptables redirect)
Proxy->>Proxy: Apply routing, load balancing, mTLS
Proxy->>Proxy: Forward to destination proxy via mTLS
linkerd2-proxy Design¶
The linkerd2-proxy is purpose-built for Linkerd and written in Rust:
- Protocol detection -- automatically detects HTTP/1, HTTP/2, gRPC, and TCP traffic
- L7 routing -- HTTP routing, retries, timeouts, and traffic splitting when Service Profiles are defined
- L4 proxying -- transparent TCP proxying for non-HTTP protocols
- Load balancing -- EWMA (Exponentially Weighted Moving Average) load balancing using latency estimates from the destination service
- mTLS -- automatically encrypts all traffic between meshed pods using certificates from the identity controller
- Telemetry -- reports request-level metrics (latency, success rate, throughput) to the control plane
- Resource footprint -- approximately 10 MB RAM per proxy instance
Init Container (linkerd-init)¶
The linkerd-init container runs before the application starts and configures iptables rules to redirect all inbound and outbound TCP traffic through the linkerd2-proxy. Alternatively, Linkerd provides a CNI plugin that does this redirection at the network level. This removes the need for privileged init containers.
3. Service Profiles¶
Service Profiles are Linkerd CRDs that define per-service routing policies:
apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: my-service.my-namespace.svc.cluster.local
namespace: my-namespace
spec:
routes:
- name: GET /api/users
condition:
method: GET
pathRegex: /api/users
responseClasses:
- condition:
status:
min: 500
max: 599
isFailure: true
retryBudget:
retryRatio: 0.2
minRetriesPerSecond: 10
ttl: 10s
Service Profiles enable:
- Retries with configurable budgets (retry ratio, min retries per second, TTL)
- Timeouts per route
- Traffic splitting for progressive delivery (canary, blue/green)
- Response classification for success/failure rate calculation
4. Multi-Cluster Architecture¶
Linkerd supports multi-cluster communication via gateway proxies:
- A linkerd-gateway deployment runs in each cluster
- A mirror service that watches remote cluster endpoints handles remote service discovery
- Traffic between clusters is automatically encrypted with mTLS
- Applications need no special configuration in their code
5. Supported Features¶
| Feature | Status |
|---|---|
| HTTP, HTTP/2, gRPC proxying | Stable |
| TCP proxying with protocol detection | Stable |
| Automatic mTLS | Stable (on by default) |
| Retries and timeouts | Stable (via Service Profiles) |
| Traffic splitting (canary) | Stable |
| Load balancing (EWMA) | Stable |
| Distributed tracing | Stable |
| Fault injection | Stable |
| Gateway API support | Stable |
| Multi-cluster communication | Stable |
| CNI plugin | Stable |
| Rate limiting | Available |
| Topology-aware routing | Available |
| Non-Kubernetes workloads | Available (mesh expansion) |
6. Resource Comparison¶
| Metric | Linkerd | Istio (sidecar) |
|---|---|---|
| Proxy language | Rust | C++ (Envoy) |
| Proxy memory per pod | ~10 MB | ~50 MB |
| Control plane memory | ~300 MB total | ~500 MB (istiod) |
| Proxy startup time | ~1 second | ~3-5 seconds |
| Configuration CRDs | ServiceProfile | VirtualService, DestinationRule |
Key Insight
The architecture of Linkerd is deliberately minimal: a single Rust micro-proxy per pod, a small control plane with three core services (destination, identity, proxy-injector), and automatic mTLS on by default. This design trades the rich L7 feature set of Istio for simplicity, lower resource consumption, and a faster time-to-value for teams that need secure service-to-service communication without complex traffic management.
How It Works¶
Rust micro-proxy internals, transparent iptables interception, mTLS trust chain, EWMA load balancing, and sidecar injection.
Proxy Data Path¶
sequenceDiagram
participant App as App Container
participant Proxy as linkerd2-proxy (Rust)
participant Dest as Destination Service
participant Peer as Remote linkerd2-proxy
participant Remote as Remote App
App->>Proxy: TCP connect (transparent iptables intercept)
Proxy->>Dest: Lookup service endpoints + policy
Proxy->>Proxy: Establish mTLS (ML-KEM-768 + X25519)
Proxy->>Peer: mTLS tunnel + HTTP/2 multiplex
Peer->>Remote: Forward request
Remote-->>Peer: Response
Peer-->>Proxy: Response
Proxy-->>App: Response
Note over Proxy: Emits metrics:<br/>latency, success rate, RPS
Transparent Interception¶
Linkerd uses iptables rules injected into the network namespace of each pod via an init container (linkerd-init). The rules redirect all TCP traffic through the proxy:
- Outbound redirect: All traffic from the app container to port 4140 (outbound proxy port)
- Inbound redirect: All incoming traffic on port 4143 (inbound proxy port)
- Exclusions: Traffic to
169.254.169.254(metadata service) and the control port of the proxy itself is excluded
The iptables rules use the REDIRECT target, which keeps the original destination IP in the socket option SO_ORIGINAL_DST. The proxy reads this option to discover where the application intended to connect.
mTLS Identity and Trust Chain¶
Linkerd uses a rotating certificate system with a trust anchor (root CA):
| Component | Certificate | Lifetime | Rotation |
|---|---|---|---|
| Trust anchor | Root CA (self-signed) | 365 days | Manual (on upgrade) |
| Identity issuer | Intermediate CA | 24 hours | Automatic via linkerd-identity |
| Proxy certificate | Leaf (SPIFFE ID) | 24 hours | Automatic on process start |
SPIFFE ID format: spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>
When proxy A connects to proxy B: 1. A presents its leaf certificate (signed by the identity issuer) 2. B validates the certificate chain back to the trust anchor 3. Both proxies verify each other's SPIFFE ID against the configured RBAC policy
EWMA Load Balancing¶
Linkerd uses Exponentially Weighted Moving Average -- it tracks backend latency and prevents routing to slow endpoints:
flowchart LR
Req["Request"] --> LB["EWMA LB"]
LB -->|"pick lowest\nlatency score"| B1["Backend A\n(EWMA: 5ms) ✅"]
LB -.->|"avoid"| B2["Backend B\n(EWMA: 500ms) ❌"]
LB -.->|"avoid"| B3["Backend C\n(EWMA: 200ms)"]
style B1 fill:#2e7d32,color:#fff
style B2 fill:#c62828,color:#fff
The EWMA algorithm: new_ewma = (1 - α) × old_ewma + α × latest_latency where α is a decay factor. After each request completes, the proxy updates the EWMA for that endpoint. Endpoints with the lowest EWMA scores receive more traffic. This automatically routes around degraded backends without health checks.
Control Plane Components¶
| Component | Purpose |
|---|---|
linkerd-destination |
Service discovery: resolves Kubernetes services to endpoint lists + policy |
linkerd-identity |
Certificate authority: issues leaf certificates to proxies on startup |
linkerd-proxy-injector |
Mutating webhook: injects the proxy sidecar into pods based on annotations |
linkerd-sp-validator |
Validates ServerPolicy and Server CRDs before admission |
Sources¶
Benchmarks¶
Scope
Performance characteristics, scaling limits, and resource consumption for Linkerd.
Proxy Performance¶
| Metric | Linkerd2-proxy | Envoy (Istio) | Notes |
|---|---|---|---|
| Latency added (P50) | < 1ms | 1-2ms | Rust vs C++ |
| Latency added (P99) | 1-3ms | 3-10ms | Under load |
| Memory per proxy | 15-25MB | 50-100MB | Significantly lighter |
| CPU per proxy | 10-50m | 50-100m | Lighter workload |
| Throughput | 95-98% native | 90-95% native | Minimal overhead |
Control Plane Resources¶
| Mesh Size (pods) | Destination CPU | Destination Memory | Identity Memory |
|---|---|---|---|
| 100 | 100m | 128Mi | 64Mi |
| 500 | 200m | 256Mi | 128Mi |
| 2,000 | 500m | 512Mi | 256Mi |
Scaling Limits¶
| Dimension | Limit | Notes |
|---|---|---|
| Pods in mesh | 10,000+ | Per cluster |
| Services | 5,000+ | |
| Endpoints per service | 5,000 | EndpointSlice support |
| Certificate rotation | Auto, 24h default | Zero-downtime |
Sourcing Status¶
Unsourced Performance Data
The performance numbers in this document are estimated from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.
Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.
Sources¶
Security¶
Overview¶
Linkerd takes a security-first approach: automatic mTLS is enabled by default for all traffic between meshed pods. This requires zero configuration. The identity system uses a simple PKI chain anchored by a trust anchor certificate. Authorization policies use the Kubernetes-native Server and ServerAuthorization CRDs.
See also: index, service-mesh/linkerd/explanation, service-mesh/linkerd/how-to-guides
1. Automatic mTLS¶
Linkerd automatically enables mutually-authenticated TLS (mTLS) for all TCP traffic between meshed pods. This means every connection between meshed workloads is both encrypted and authenticated without application code changes or manual certificate management.
graph LR
subgraph "Source Pod"
AppA["Application"] --> PA["linkerd2-proxy"]
end
subgraph "Destination Pod"
PB["linkerd2-proxy"] --> AppB["Application"]
end
PA <-->|mTLS<br/>encrypted & authenticated| PB
PA -.->|certificate request| ID["identity controller"]
PB -.->|certificate request| ID
Key characteristics:
- On by default -- no configuration needed to enable mTLS
- Zero application changes -- encryption and authentication happen at the proxy layer
- Protocol agnostic -- works for HTTP, HTTP/2, gRPC, and raw TCP
- Control plane also secured -- the Linkerd control plane components themselves communicate via mTLS
2. Trust Anchor PKI¶
Linkerd uses a simple two-tier PKI (Public Key Infrastructure):
Trust Anchor (root certificate, long-lived)
└── Issuer Certificate (intermediate, managed by identity controller)
└── Workload Certificate (per-Service Account, short-lived)
Trust Anchor¶
The trust anchor is the root of trust for the entire mesh:
- Generated during
linkerd installor provided manually - Typically uses ECDSA (P-256) for small key size and fast verification
- Common name format:
root.linkerd.cluster.local - Distributed to all proxies as a read-only ConfigMap (
linkerd-identity-trust-roots) - Store it securely. Rotate it infrequently
Issuer Certificate¶
The issuer certificate is signed by the trust anchor and used by the identity controller to sign workload certificates:
- Stored as a Kubernetes Secret in the linkerd namespace
- Can be managed by cert-manager for automatic rotation
- Rotating the issuer does not require restarting proxies
Provisioning with cert-manager¶
For production deployments, cert-manager can manage the trust anchor and issuer lifecycle:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: linkerd-trust-anchor
namespace: cert-manager
spec:
issuerRef:
kind: Issuer
name: linkerd-trust-root-issuer
secretName: linkerd-trust-anchor
isCA: true
commonName: root.linkerd.cluster.local
duration: 8760h # 1 year
renewBefore: 7320h # renew 60 days before expiry
privateKey:
rotationPolicy: Always
algorithm: ECDSA
The trust-manager Bundle resource distributes trust anchors to all namespaces:
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: linkerd-identity-trust-roots
namespace: linkerd
spec:
sources:
- secret:
name: "linkerd-trust-anchor"
key: "tls.crt"
target:
configMap:
key: "ca-bundle.crt"
namespaceSelector:
matchLabels:
linkerd.io/is-control-plane: "true"
3. Identity Controller¶
The identity controller is the built-in certificate authority of Linkerd:
- Issues TLS certificates to linkerd2-proxy instances on startup
- Identity is derived from the Kubernetes Service Account of the pod
- Identity format:
<service-account>.<namespace>.serviceaccount.identity.linkerd.cluster.local - Validates CSR (Certificate Signing Request) against the Kubernetes API
- Short-lived certificates (default 24 hours) with automatic rotation
- Runs as part of the linkerd-control-plane deployment
4. Certificate Rotation¶
Linkerd handles certificate rotation at multiple levels:
| Certificate | Lifetime | Rotation Mechanism |
|---|---|---|
| Trust anchor | 1+ years | Manual or cert-manager |
| Issuer certificate | Configurable | cert-manager or manual |
| Workload certificate | 24 hours (default) | Automatic via identity controller |
Workload certificate rotation is transparent to the application. The proxy requests a new certificate from the identity controller before the current one expires, with no connection interruption.
5. Server and ServerAuthorization¶
Linkerd uses two CRDs for fine-grained authorization:
Server¶
The Server resource defines which traffic a policy applies to, selecting pods by label and specifying the port and protocol:
apiVersion: policy.linkerd.io/v1beta1
kind: Server
metadata:
name: backend-server
namespace: production
spec:
podSelector:
matchLabels:
app: backend
port: 8080
proxyProtocol: HTTP/2
ServerAuthorization¶
The ServerAuthorization resource specifies which clients are allowed to connect to a Server:
apiVersion: policy.linkerd.io/v1beta1
kind: ServerAuthorization
metadata:
name: backend-authz
namespace: production
spec:
server:
name: backend-server
client:
meshTLS:
identities:
- "frontend.production.serviceaccount.identity.linkerd.cluster.local"
unauthenticated: false
Authorization features:
- Identity-based access control -- restrict access to specific Service Account identities
- Namespace-based access control -- allow traffic from specific namespaces
- Network-based access control -- allow traffic from specific CIDR ranges
- Protocol-aware -- HTTP method and path matching for L7 policies
6. Tap RBAC¶
The Linkerd tap feature allows real-time inspection of traffic flowing through the mesh. Access to tap data is controlled by Kubernetes RBAC:
- The
linkerd-taprole controls who can tap traffic - Tap can be scoped to specific namespaces, deployments, or pods
- Sensitive data (headers, body) is not exposed by default
- Requires explicit RBAC binding to use
Tap Access
Grant tap permissions carefully. While tap does not expose payload data by default, it reveals request metadata including URIs, headers, and response codes that can contain sensitive information.
7. Security Comparison with Istio¶
| Aspect | Linkerd | Istio |
|---|---|---|
| mTLS default | On by default | PERMISSIVE by default (requires PeerAuthentication STRICT) |
| Identity format | Service Account based (custom format) | SPIFFE format |
| Authorization CRD | Server + ServerAuthorization | AuthorizationPolicy |
| JWT validation | Not built-in (use external) | RequestAuthentication (native) |
| Certificate management | Built-in or cert-manager | Built-in Citadel or external CA |
| Ambient / sidecar-less | Not available | ztunnel + waypoint |
Key Insight
The security philosophy of Linkerd is "secure by default." mTLS is automatically enabled for all meshed traffic, the PKI is simple (trust anchor + issuer + workload), and authorization uses Kubernetes-native CRDs. This trades the richer security features of Istio (native JWT, SPIFFE standard, ambient mode) for a dramatically simpler operational model where the default configuration is already production-ready.