envoyproxy/envoy
Architecture
Envoy's architecture is best read as three concentric circles: a single process, a small set of threads inside it, and a layered request pipeline running on each worker. Everything else — configuration, extensions, observability — hangs off this core.
Process layout
A running envoy process consists of:
- One main thread that owns configuration, the cluster manager's primary copy, the admin server, stats flushing, and xDS subscriptions.
- N worker threads (one per CPU by default) that own listeners, accept connections, and run filter chains. Workers do not share state with each other.
- A small number of helper threads: a file flush thread for access logs, a stats flush thread, optional gRPC C-core threads.
- Optionally, a child process during a hot restart, which runs alongside the parent and migrates listeners over a Unix domain socket.
graph TB
subgraph Process[Envoy process]
Main[Main thread<br/>config, cluster manager primary,<br/>admin, xDS]
W1[Worker 1<br/>listeners + filters]
W2[Worker 2<br/>listeners + filters]
WN[Worker N<br/>listeners + filters]
FlushFile[File flush thread]
FlushStats[Stats flush thread]
end
XDS[xDS control plane<br/>gRPC] --> Main
Main -.thread-local update.-> W1
Main -.thread-local update.-> W2
Main -.thread-local update.-> WN
Client1[Downstream client] --> W1
Client2[Downstream client] --> W2
W1 --> Upstream
W2 --> UpstreamThe threading model is the single most important architectural fact about Envoy: the data plane runs lock-free per worker, and configuration changes propagate via copy-on-write snapshots through ThreadLocal::SlotImpl. Every other subsystem is designed around it. See Threading model for the mechanism.
Source roots: source/server/server.cc is the heart of Server::InstanceImpl. source/server/worker_impl.cc is the worker. source/common/thread_local/thread_local_impl.cc is the propagation primitive.
The request pipeline
graph LR
Sock([Downstream socket]) --> Listener
Listener --> LF[Listener filters<br/>TLS inspector, proxy protocol, ...]
LF --> NF[Network filters<br/>L4]
NF --> HCM[HTTP connection manager<br/>codec + filter manager]
HCM --> HF[HTTP filters<br/>L7]
HF --> Router
Router --> CM[Cluster manager]
CM --> LB[Load balancer]
LB --> CP[Connection pool]
CP --> TS[Transport socket<br/>raw / TLS / ALTS]
TS --> Upstream([Upstream socket])Every box on this diagram is its own page in the wiki:
- Listener manager creates and reconfigures listeners.
- Listener filters live in
source/extensions/filters/listener/and run once per L4 connection before any data is read. - Network (L4) filters live in
source/extensions/filters/network/. The most important one is the HTTP connection manager. - HTTP filters live in
source/extensions/filters/http/. They form a chain that ends with the router. - The router asks the cluster manager for a cluster, the cluster's load balancer for a host, and the host's connection pool for a connection.
- The connection's transport socket decides what wire bytes look like (raw, TLS, ALTS, proxy-protocol-wrapped, …).
Configuration model
Envoy is configured by a single root protobuf message — Bootstrap — loaded from a YAML/JSON/protobuf-text file at startup. Bootstrap statically declares listeners, clusters, runtime, admin, stats sinks, tracing, and points at xDS endpoints for everything else.
graph TD
File[bootstrap.yaml] --> Bootstrap[envoy::config::bootstrap::v3::Bootstrap]
Bootstrap --> StaticListeners[Static listeners]
Bootstrap --> StaticClusters[Static clusters]
Bootstrap --> ADS[ADS / xDS endpoints]
ADS -->|LDS| Listeners[Listener manager]
ADS -->|CDS| Clusters[Cluster manager]
ADS -->|RDS| Routes[Route configurations]
ADS -->|EDS| Endpoints[Cluster endpoints]
ADS -->|SDS| Secrets[Secret manager]
ADS -->|RTDS| Runtime[Runtime layers]
ADS -->|ECDS| ExtConfig[Extension configs]Static configuration is for bootstrapping the dynamic configuration. In production, almost all listeners, clusters, routes, and secrets arrive over xDS from a control plane (Istio, Consul, go-control-plane, etc.). The protocol itself is defined in api/envoy/service/discovery/v3/discovery.proto, and the client implementation lives in source/extensions/config_subscription/.
Extension model
Almost everything Envoy does at L4/L7 is implemented as an extension registered through Envoy's typed factory system. Extensions live under source/extensions/ and are linked into the binary at build time. Whether an extension is built in is controlled by source/extensions/extensions_build_config.bzl and source/extensions/all_extensions.bzl.
Extension categories (Envoy::Extensions::* namespaces):
| Category | Path | Examples |
|---|---|---|
| HTTP filters (L7) | source/extensions/filters/http/ |
router, jwt_authn, ext_authz, lua, oauth2 |
| Network filters (L4) | source/extensions/filters/network/ |
http_connection_manager, tcp_proxy, redis_proxy |
| Listener filters | source/extensions/filters/listener/ |
tls_inspector, original_dst, proxy_protocol |
| UDP filters | source/extensions/filters/udp/ |
udp_proxy, dns_filter |
| Clusters | source/extensions/clusters/ |
eds, strict_dns, original_dst, redis |
| Load balancing policies | source/extensions/load_balancing_policies/ |
round_robin, maglev, ring_hash, subset |
| Transport sockets | source/extensions/transport_sockets/ |
tls, raw_buffer, alts, proxy_protocol |
| Access loggers | source/extensions/access_loggers/ |
file, grpc, fluentd, open_telemetry |
| Tracers | source/extensions/tracers/ |
datadog, opentelemetry, zipkin, skywalking |
| Stat sinks | source/extensions/stat_sinks/ |
statsd, dog_statsd, metrics_service, hystrix |
| Resource monitors | source/extensions/resource_monitors/ |
fixed_heap, injected_resource, cgroup_memory |
| Bootstrap extensions | source/extensions/bootstrap/ |
wasm, internal_listener |
| Wasm runtimes | source/extensions/wasm_runtime/ |
v8, wamr, wasmtime, null |
| Dynamic modules (FFI) | source/extensions/dynamic_modules/ |
ABI for Rust/Go/C extensions |
A second tier of extensions lives in contrib/ — same structure, looser maintenance guarantees, only present in the envoy-contrib binary. See the extension policy.
Headers vs implementation
A defining convention in Envoy is the strict separation between the public interface (envoy/) and the implementation (source/):
envoy/contains only abstract interface headers — pure virtual classes that callers code against. Examples:envoy/network/connection.h,envoy/http/filter.h,envoy/upstream/cluster_manager.h.source/common/andsource/server/contain*_impl.cc/*_impl.hfiles implementing those interfaces.test/mocks/contains mock implementations of every public interface, used by unit tests.
This three-way split (interface / impl / mock) is what makes Envoy testable: any subsystem can be unit-tested by injecting mocks for its dependencies. It is also why adding a feature usually requires editing three files in three places.
Build system
Envoy is built with Bazel ≥ 7.7, configured by MODULE.bazel and the legacy WORKSPACE file. The two main targets are:
//source/exe:envoy-static— the production Envoy binary.//contrib/exe:envoy-static— Envoy plus allcontrib/extensions.
Bazel rules are in bazel/. All Envoy BUILD files use macros from bazel/envoy_build_system.bzl (e.g. envoy_cc_library, envoy_cc_test, envoy_proto_library) which inject the project's compile flags, sanitizer support, and test wrappers. See the build system page.
Where to read next
- Threading model — the lock-free data plane in detail.
- HTTP connection manager — the L7 dispatcher.
- xDS configuration — how dynamic config is delivered.
- Patterns and conventions — how to write code that fits in.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.