In Envoy's terminology and configuration, what do the words 'downstream' and 'upstream' refer to, and which Envoy objects sit on each side?
answer
- defined by who opened the connection
- listener side versus cluster side
- one word per hop, not per service
- TLS config exists on both sides
- sidecar holds both roles at once
basics
~20 sIn Envoy, downstream is the client that connects to Envoy and upstream is the service Envoy connects out to. Listeners and their filter chains are the downstream side; clusters and their endpoints are the upstream side.
solid answer
~50 sEnvoy defines the words strictly by direction of connection, not by position in a diagram. The **downstream** is whoever opened the connection *to* Envoy — a browser, another service, or the local application in a sidecar. The **upstream** is whatever Envoy opens a connection *to* on the request's behalf. That split runs through the whole config: `listeners`, `listener_filters`, `filter_chains` and the `http_connection_manager`'s codec settings all describe downstream handling, while `clusters`, their `load_assignment` endpoints, `connect_timeout`, `lb_policy` and the cluster's own `transport_socket` describe upstream handling. Envoy's stat and timeout names follow the same convention, so `downstream_*` and `upstream_*` counters tell you which half of the proxy a problem lives in. The important consequence is that one Envoy instance can be downstream-facing and upstream-facing at once: in a sidecar, the inbound listener's downstream is a remote caller while the outbound listener's downstream is the local app.
go deeper
Say plainly that downstream is whoever connected to Envoy and upstream is whatever Envoy connects out to, then name listeners as the downstream object and clusters as the upstream one.
Explain that the terms are per-connection rather than per-service, and use them to place configuration: server TLS on the filter chain, client TLS on the cluster, codec options on http_connection_manager.
Use the vocabulary to triage: separate downstream-side symptoms such as codec errors and client disconnects from upstream-side ones such as connect failures, and reason about a sidecar holding both roles simultaneously.
Insist on the vocabulary as a shared interface across teams — every proxy, gateway and mesh abstraction inherits this axis, and imprecision here is what makes ownership of timeouts, certificates and error budgets ambiguous between platform and application teams.
## The definition, and why it is not relative Newcomers try to read "upstream" and "downstream" as positions on a diagram, which produces arguments about which way the page is drawn. Envoy defines them by *who initiated the connection*: - **Downstream**: the entity that connects to Envoy and sends requests to it. - **Upstream**: the entity Envoy connects to and forwards requests to. The words are properties of a hop, not of a service. A given service is upstream of the proxy in front of it and downstream of the proxy it calls through. ## How the split shows up in configuration Almost every Envoy object belongs to one side, and knowing which one removes most config confusion: | Downstream side | Upstream side | | --- | --- | | `listeners`, `address.socket_address` | `clusters` | | `listener_filters` (TLS inspector, HTTP inspector) | `load_assignment` endpoints | | `filter_chains`, `filter_chain_match` | `lb_policy` | | the chain's `transport_socket` (server TLS) | the cluster's `transport_socket` (client TLS) | | `http_connection_manager` codec and header options | `connect_timeout`, upstream protocol options | Notice that TLS appears twice, in two different objects. The filter chain's `transport_socket` is Envoy acting as a TLS *server* for the downstream; the cluster's `transport_socket` is Envoy acting as a TLS *client* toward the upstream. Wiring a certificate into the wrong one is one of the most common beginner mistakes, and the vocabulary is what tells them apart. ## Requests and responses do not swap the labels A response travels from the upstream back through Envoy to the downstream, but the labels do not flip. The connection that the client opened is the downstream connection for its entire life, including while carrying responses. That is why Envoy's HTTP filter API talks about *decoder* and *encoder* paths rather than reusing the direction words: decoding is the request travelling downstream-to-upstream, encoding is the response travelling upstream-to-downstream. ## Naming conventions that depend on it Envoy's counters and settings are named on this axis, so reading them tells you which half of the proxy is unhealthy: connection counters, connection-length histograms and protocol-error counters all exist separately per side. If your downstream connection counts are fine while upstream connect failures climb, the client reached Envoy successfully and the backend is the problem — the vocabulary itself narrows the search. ## The sidecar case, where both apply at once Deployed as a sidecar next to an application, one Envoy process holds both roles simultaneously: - Its **inbound** listener accepts connections from remote callers (downstream = the remote caller) and forwards to a cluster pointing at localhost (upstream = the local app). - Its **outbound** listener accepts connections from the local app (downstream = the local app) and forwards to clusters representing remote services (upstream = those services). So the same local application is the *upstream* of one listener and the *downstream* of another, in the same process. This is exactly why the terms are defined per connection instead of per service. ```yaml # downstream side: Envoy is the TLS server filter_chains: - transport_socket: { name: envoy.transport_sockets.tls } # upstream side: Envoy is the TLS client clusters: - name: backend transport_socket: { name: envoy.transport_sockets.tls } ``` ## What to say in an interview Give the connection-direction definition first, then anchor it to objects: listeners are downstream, clusters are upstream. Then show you understand it is per-hop by mentioning the sidecar case. Candidates who define it as "upstream means the client, like a river source" get it backwards, and that mistake propagates into misreading every metric and timeout name in the product.
- Envoy's HTTP filters have decoder and encoder callbacks rather than downstream and upstream ones. Why the different vocabulary?Because direction words describe connections, which never swap roles, while a filter needs to talk about the two halves of one request's lifecycle. Decoding is the request travelling from the downstream connection toward the upstream; encoding is the response travelling back. A single filter can implement either or both, so the API names the phase rather than the peer.
- Where would you configure the certificate Envoy presents to browsers, versus the trust store it uses to verify a backend?The certificate presented to browsers goes in the filter chain's `transport_socket` on the listener, because there Envoy is the TLS server for the downstream. Verification of the backend goes in the cluster's `transport_socket`, where Envoy acts as a TLS client toward the upstream. Same extension, two objects, decided entirely by which side of the proxy you mean.
saying these in an interview costs you the question
- Says upstream means the calling client
- Thinks the labels flip on the response path
- Treats a service as permanently upstream or downstream
- Looks for backend TLS settings on the listener
- Assumes a sidecar has only one direction