Open-Source Wikis

/

Envoy

/

Envoy

/

Architecture

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 --> Upstream

The 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:

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/ and source/server/ contain *_impl.cc/*_impl.h files 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 all contrib/ 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.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – Envoy wiki | Factory