Architecture
Big picture
The top-level source/ tree splits into common, server, exe, and extensions. The core networking and HTTP stack lives in common; pluggable behaviour (filters, codecs, transport sockets) lives under extensions/; the configuration model is the protobuf API under api/ (xDS, API v3). A running Envoy is one main thread plus a fixed pool of worker threads, each with its own event loop.
Components
Server and worker threads
The main thread owns configuration and lifecycle; a fixed number of worker threads each run a libevent dispatcher and handle connections non-blocking, without sharing locks between workers. Configuration updates are published as immutable snapshots posted into thread-local slots. The mechanism is ThreadLocal::InstanceImpl at source/common/thread_local/thread_local_impl.h:20 and SlotImpl::runOnAllThreads at source/common/thread_local/thread_local_impl.h:47.
Network and HTTP filter chains
Request processing is a chain of filters. L4 behaviour is implemented as network read/write filters. L7 HTTP is itself a network filter, the HTTP Connection Manager (HCM), which drives an HTTP codec and then runs an inner chain of HTTP decoder/encoder filters. The HCM implementation is ConnectionManagerImpl in source/common/http/conn_manager_impl.h.
Configuration API (xDS)
The api/ directory holds the protobuf definitions for the xDS APIs at API version 3.0.0 (API_VERSION.txt). A control plane streams listeners, routes, clusters, endpoints, and secrets to Envoy over these APIs. Service meshes use the HCM plus xDS as their data plane; in Istio, istiod configures each Envoy through xDS (Istio architecture).
How a request flows
A downstream HTTP request walks from the listener to a chosen route, all in source/common/http/conn_manager_impl.cc.
- Data arrives at the network read filter entry point
ConnectionManagerImpl::onData(source/common/http/conn_manager_impl.cc:515). If no codec exists yet it is created withcreateCodec(data)(:525). - The codec parses bytes with
codec_->dispatch(data)(:546), callingnewStreamfor each new HTTP stream. ConnectionManagerImpl::newStream(:410) builds anActiveStream(:430) and links it intostreams_withLinkedList::moveIntoList(:469).- When headers complete, the codec calls
ActiveStream::decodeHeaders(:1354). It takes ownership ofrequest_headers_(:1366) and validates them. - The route is resolved by
refreshCachedRoute()(:1553), whose body (:1811) callssnapped_route_config_->route(...)(:1827) and caches the result. - The downstream filter chain is built by
filter_manager_.createDownstreamFilterChain()(:1564), then decoding starts withfilter_manager_.decodeHeaders(*request_headers_, end_stream)(:1600). The terminal Router filter picks a cluster and forwards upstream.
Key design decisions
The defining choice is the threading model: rather than guard shared state with locks, Envoy gives each worker single-threaded ownership and swaps configuration in as immutable snapshots through thread-local slots (source/common/thread_local/thread_local_impl.h:20). This keeps the hot path lock-free in the common case.
The second is the universal data plane API. Configuration is data (xDS protobufs in api/), so a proxy becomes a generic component a control plane drives. That is what lets meshes and gateways reuse the same proxy core (Istio architecture).
Extension points
Behaviour is added through extensions under extensions/: network filters, HTTP filters, transport sockets, access loggers, and codecs. Beyond compiled C++ extensions, Envoy supports request-time logic through WebAssembly (proxy-wasm) and Lua, and is reconfigured at runtime through the xDS APIs in api/.