Buoyant Enterprise for Linkerd

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.

This is a new major version of Linkerd. There are breaking changes in this release. Please read these release notes carefully before upgrading.

Who should upgrade?

This is a feature release. Customers who want to take advantage of the new features introduced should upgrade.

Note: Linkerd 2.20 does not yet provide FIPS support. Customers who require FIPS should continue using the latest release of BEL 2.19.

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.image are gone: proxyInit.image.name, proxyInit.image.version, and proxyInit.image.pullPolicy. The init container now derives its image from proxy.image.*
  • The linkerd inject / linkerd install flags --init-image and --init-image-version have been removed.
  • The corresponding per-workload override annotations (config.linkerd.io/init-image and config.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/proxy image; the standalone ghcr.io/buoyantio/proxy-init image is no longer pulled and no longer needs to be mirrored.
  • Remove any proxyInit.image.name, proxyInit.image.version, or proxyInit.image.pullPolicy settings from your Helm values; rely on the proxy.image.* values instead.
  • Drop the --init-image and --init-image-version flags from any linkerd inject / linkerd install invocations or scripts.
  • Remove the config.linkerd.io/init-image and config.linkerd.io/init-image-version annotations from any workloads or templates that set them.
  • Confirm your image-pull credentials/policies cover the proxy image for the init container (it now uses proxy.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-sidecar annotation is deprecated in favor of config.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-env annotation 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_subscribers metric 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 upgrade failing after an install that used an external CA, by applying --set overrides 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.nativeSidecar now defaults to true [#15267]
  • Fix typos in the EgressNetwork and ExternalWorkload CRD templates [#14681]
  • Make honorTimestamps configurable 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.healthCheckNodePort Helm value [#15174]
  • Add missing Server/AuthorizationPolicy resources [#14992]

Viz

  • Update admin port names in the viz extension [#15090]