enterprise-2.20.0
June 23, 2026
Linkerd 2.20 is a new major release that provides automated rotation of mTLS trust anchors; adds support for Windows VMs running outside of Kubernetes; and improves circuit breaking and load balancing (including HAZL) to be aware of rate limits. The 2.20 release also improves memory consumption of the control plane, especially on busy clusters, and promotes native sidecars to be the default deployment model.
See our Linkerd 2.20 announcement blog post for more details.
Who should upgrade?
This is a feature release. Customers who want to take advantage of the new features introduced should upgrade.
Supported Kubernetes and Gateway API versions
BEL 2.20 supports Kubernetes versions 1.31 through 1.35, and Gateway API versions v1.2.1 through v1.5.1.
Control plane operator requirements
To upgrade with BEL’s lifecycle automation operator, you will need Buoyant Extension v0.40.1 or later.
Upgrade guidance
This release includes several breaking changes, as well as several non-breaking changes that may require action on your part. Please read these release notes carefully.
Breaking changes
New minimum Kubernetes and Gateway API supported versions
Linkerd 2.20 requires at least Kubernetes 1.21 and at least Gateway API version 1.2.1. See Versions, Platforms, and Deprecation for more.
Unified proxy image replaces the standalone proxy-init image
The proxy and proxy-init Docker images have been combined into a single
unified image (ghcr.io/buoyantio/proxy) that contains both binaries. The
linkerd-init container now runs from this unified proxy image.
As a result, the dedicated knobs for configuring the proxy-init image have been removed, since the init container’s image now always tracks the proxy image:
- Helm values under
proxyInit.imageare gone:proxyInit.image.name,proxyInit.image.version, andproxyInit.image.pullPolicy. The init container now derives its image fromproxy.image.* - The
linkerd inject/linkerd installflags--init-imageand--init-image-versionhave been removed. - The corresponding per-workload override annotations
(
config.linkerd.io/init-imageandconfig.linkerd.io/init-image-version) are no longer honored.
Who is affected
Anyone who pins, mirrors, or overrides the proxy-init image specifically; in particular users running from a private/air-gapped registry, and users who customize the init image via Helm values, CLI flags, or pod annotations.
What you need to do
- If you mirror images into a private registry, mirror the unified
ghcr.io/buoyantio/proxyimage; the standaloneghcr.io/buoyantio/proxy-initimage is no longer pulled and no longer needs to be mirrored. - Remove any
proxyInit.image.name,proxyInit.image.version, orproxyInit.image.pullPolicysettings from your Helm values; rely on theproxy.image.*values instead. - Drop the
--init-imageand--init-image-versionflags from anylinkerd inject/linkerd installinvocations or scripts. - Remove the
config.linkerd.io/init-imageandconfig.linkerd.io/init-image-versionannotations from any workloads or templates that set them. - Confirm your image-pull credentials/policies cover the
proxyimage for the init container (it now usesproxy.image.pullPolicy).
Native sidecars are now enabled by default
Native sidecars have been promoted from beta to GA and are now the default proxy
injection mode (proxy.nativeSidecar=true). The
config.beta.linkerd.io/proxy-enable-native-sidecar annotation is now
deprecated in favor of config.linkerd.io/proxy-enable-native-sidecar; the beta
and alpha versions of the annotation will be removed in the next major Linkerd
version.
On most clusters this change will require no action from you. However, note that on upgrade, injected workloads will be re-rolled to adopt the new pod shape.
Native sidecars are init containers with a restartPolicy: Always setting. Any
scripts that make assumptions of Linkerd’s proxy container and its type and
position within the pod may need to be updated to account for this new shape.
Note also that proxy shutdown and ordering semantics change accordingly; for
example, waitBeforeExitSeconds is ignored when native sidecars are enabled.
These configurations may be safely removed.
Classic sidecars are still supported. You can force classic sidecars by setting
proxy.nativeSidecar=false in your top-level Linkerd configuration
New features
Trust Anchor Rotation Operator
Linkerd 2.20 features an new operator that handles rotation of its TLS trust anchor, a process that until now has been manual, complex, and error-prone. The operator splits rotation into a sequence of well-defined phases, tracking progress and only advancing once each step has safely converged. It propagates the new trust roots to meshed workloads; coordinates renewal of workload certificates; and removes the retired root once it’s no longer needed. This new operator allows trust anchor rotation to be a controlled, observable, and low-risk operation.
Learn more in the trust anchor rotation docs.
Feature maturity: beta.
Windows support
Linkerd 2.20 includes an alpha release of support for Windows VMs (and physical hardware), allowing you to enable Linkerd on Windows hosts outside of the Kubernetes cluster. See the Windows mesh expansion docs for more.
Feature maturity: alpha.
Rate-Limit-Aware Load Balancing and Circuit Breaking
Load balancing and circuit breaking now optionally take into account HTTP 429 rate-limit responses, steering traffic away from overloaded endpoints and eventually ejecting them temporarily from the load balancer pool. This capability integrates with HAZL load balancing.
Learn more in the HAZL, rate limiting and circuit breaking docs.
Feature maturity: alpha.
Known issues
When enabling load biasing via the
balancer.alpha.linkerd.io/penalize-failures="true" annotation, HAZL will route
traffic from a heavily loaded zone to less heavily loaded zone. However, once
the original zone’s load returns to normal, HAZL does not stop routing traffic
to other zones as expected. HAZL users should avoid using load biasing
(balancer.alpha.linkerd.io/penalize-failures) in BEL 2.20.0.
Fine-grained changelog
Control Plane
- Add support to harness for Windows mesh expansion
- Promote native sidecars to GA and enable them by default; the
config.beta.linkerd.io/proxy-enable-native-sidecarannotation is deprecated in favor ofconfig.linkerd.io/proxy-enable-native-sidecar[#15267] - Fix external workload selection so that a workload’s endpoints are no longer matched across namespace boundaries [#15324]
- Improve robustness of service profile validation [#15325]
- Fix handling of authentication policy removal [#15326]
- Improve accuracy of outbound policy indexer logging [#15314]
- Restrict Servers from affecting workloads in other namespaces [#15243]
- Improve correctness of outbound policy indexing [#15157]
- Use more robust logic to convert injection annotations to metric labels [#15176]
- Skip admission validation for Gateway API routes with unsupported fields [#15118]
- Add
config.linkerd.io/proxy-additional-envannotation for per-scope env overrides [#15156] - Significantly reduce destination controller memory usage by sharing endpoint filtering and diff state across subscribers with the same filtering conditions (up to 84% reduction in load testing) [#15166]
- Include extra attributes in SubjectAccessReview [#14768]
- Guard against deserialization errors in policy watches [#14844]
- Properly discover native sidecar ports in the destination controller [#14814]
- Fix opaque ports not being honored on native sidecar containers when pods are addressed directly by IP [#14791]
- Properly discover hostports in native sidecars [#14786]
- Improve injector support for native sidecar servers [#14767]
- Respect the timeout field on Gateway API HTTPRoutes [#14750]
- Fix the
workload_subscribersmetric to correctly track the total number of subscribers across all IP and port combinations [#14683] - Expose workload metadata in OpenTelemetry traces [#14684]
- Fix request and response bodies exactly at the configured size limit being incorrectly rejected as too large [#15301]
- Add annotations for configuring the new load biaser and unified breaker features [15317]
Proxy
- Breaking change: Merge the proxy-init image into the proxy image, simplifying image management and reducing overall image size – make sure to update any explicit references to the proxy-init image [#14577]
- Introduce additional inbound metrics for request and response duration, HTTP and gRPC status codes, request and response body frame size, and requests count [#4420, #4313, #4180, #4165, #4127]
- Add support for the P256+SHA512 and P384+SHA512 cryptographic signatures [#4383]
- Downgrade the error logged when a non-mTLS connection is attempted to port 4143 from INFO to DEBUG, which can help avoid flooding the logs with error messages, especially in multicluster installations [#4441]
- Fix a bug that could result in an OOMKilled proxy panicking on restart with a “period must be non-zero” message [#4449]
- Allow configuring the TCP listen backlog using the LINKERD2_PROXY_INBOUND_TCP_LISTEN_BACKLOG environment variable [#4355]
- Correctly honor the TTL of a DNS negative response [#4450]
- Prevent latent busy-spin in the consecutive-failures circuit breaker [#4481]
- Implement load biaser to steer outbound traffic away from unhealthy and rate-limited endpoints returning HTTP 429 and gRPC RESOURCE_EXHAUSTED, supporting pushback hints [#4537]
- Implement a unified breaker supporting both the consecutive failures policy and a new success-rate policy handling rate-limited endpoints [#4561]
CLI
- Fix
linkerd upgradefailing after an install that used an external CA, by applying--setoverrides before initializing issuer credentials [#15181] - Improve support for native sidecar servers in
linkerd check --proxy[#14779] - Improve support for native sidecar servers in
linkerd authz[#14780] - Fix broken documentation URLs in CLI commands [#14651]
Helm Charts
- Promote native sidecars to GA and enable them by default;
proxy.nativeSidecarnow defaults totrue[#15267] - Fix typos in the EgressNetwork and ExternalWorkload CRD templates [#14681]
- Make
honorTimestampsconfigurable for PodMonitors [#15080] - Adjust the Prometheus configmap to not scrape proxies from failed pods and completed jobs [#14916]
Multicluster
- Synchronize gateway liveness [#15150]
- Make service cleanup logic respect namespaces [#15201]
- Add
gateway.healthCheckNodePortHelm value [#15174] - Add missing Server/AuthorizationPolicy resources [#14992]
Viz
- Update admin port names in the viz extension [#15090]