Envoy Proxy
An open-source high-performance proxy developed by Lyft, used as the sidecar proxy in Istio and several other service meshes. Envoy handles mTLS, load balancing, circuit breaking, retries, metrics, and distributed tracing at the proxy level.
What is Envoy Proxy
Envoy is a Layer 7 proxy designed specifically for cloud-native microservices. It was built by Lyft to solve the same networking problems that led to service meshes - retries, circuit breaking, observability - but as a separate process that any language can use without a library.
Istio uses Envoy as its data plane sidecar proxy. When Istio injects a sidecar, what it actually injects is an Envoy container.
What Envoy Does
Envoy operates at two network layers simultaneously:
Layer 3/4 (TCP/UDP):Handles raw TCP connections, TLS termination, connection poolingThis is where mTLS is enforced Layer 7 (HTTP/gRPC):Understands HTTP headers, paths, methodsThis is where header-based routing and retries workRequest arrives at pod | vEnvoy sidecar (inbound) - validates mTLS client certificate - decrypts the TLS connection - records request arrival time and metadata | vApplication receives plain HTTP | vApplication sends response | vEnvoy sidecar (outbound for response) - encrypts with mTLS - records response time and status code - reports metrics to control planeEnvoy Configuration
Envoy does not read static config files in a service mesh. Its configuration is pushed dynamically by the control plane using the xDS protocol (specifically: CDS, EDS, LDS, RDS, SDS). This is how Istio can update routing rules across thousands of pods without restarting any of them.
Istio control plane (istiod) | | xDS protocol (gRPC streaming) vEnvoy sidecarreceives updated routing rules, certificates, and cluster infoapplies them immediately without restartMetrics Envoy Exposes
Every Envoy sidecar exposes metrics at port 15090 that Prometheus scrapes:
## Check raw Envoy metrics from a pod's sidecarkubectl exec -n production \ $(kubectl get pod -l app=orders -n production \ -o jsonpath='{.items[0].metadata.name}') \ -c istio-proxy -- \ curl -s localhost:15090/metrics | grep istio_requestsKey metrics: istio_requests_total, istio_request_duration_milliseconds, istio_tcp_connections_opened_total
Envoy vs Linkerd's Proxy
| Envoy (Istio) | Rust Proxy (Linkerd) | |
|---|---|---|
| Language | C++ | Rust |
| Memory per sidecar | ~100 MB | ~10-20 MB |
| Feature richness | Very high | Focused on core use cases |
| WebAssembly extensions | Yes | No |
| Learning curve | Steep (xDS, filters) | Gentle |
RememberYou rarely interact with Envoy directly when using Istio. Istio translates your VirtualService and DestinationRule YAML into Envoy configuration and pushes it to the proxies automatically. You write Istio resources; Istio configures Envoy.
TipUse
istioctl proxy-statusto check if all Envoy sidecars are in sync with the control plane. If a sidecar shows STALE, it may be applying outdated routing rules - this is how stale canary configurations survive pod restarts unexpectedly.
Frequently Asked Questions
Why did Istio and other service meshes choose Envoy instead of writing a custom proxy?
Envoy was built at Lyft specifically to solve the problem of making networking observable and resilient between microservices, with first-class support for dynamic service discovery, hot-reload configuration via xDS APIs, and rich L7 telemetry out of the box. Rather than reinventing this, Istio (and others like AWS App Mesh) adopted Envoy as their data-plane proxy and focus their own control plane on translating higher-level policy into Envoy's configuration.
What's a common gotcha when running Envoy as a sidecar in Kubernetes?
Sidecar injection adds real per-request latency and memory overhead per pod, which is often underestimated when sizing resource requests — a mesh with hundreds of pods can meaningfully increase cluster memory footprint. Another frequent issue is sidecar startup ordering: if the application container starts before the Envoy sidecar is ready, early outbound calls fail; most meshes now support native sidecar containers or init-container ordering to fix this.