Open-Source Wikis

/

Pingora

/

Features

/

Connection pooling

cloudflare/pingora

Connection pooling

Pingora keeps idle upstream connections in a pool for reuse, which removes the TCP/TLS handshake cost from subsequent requests. Pooling is enabled by default for HTTP/1 and used differently (and automatically) for HTTP/2.

The user guide page is docs/user_guide/pooling.md.

How it works

Pool storage lives in pingora-pool/src/connection.rs. The HTTP-aware pool (pingora-core/src/connectors/http/mod.rs) is the user of the pool; it wraps it with HTTP-version awareness.

graph TD
    proxy[Proxy needs upstream conn]
    connector[Connector::get_http_session]
    pool{Conn in pool<br/>matching peer hash?}
    fresh[TCP/TLS dial]
    use[Run request]
    keepalive{Keep-alive<br/>allowed?}
    release[Pool::release]
    drop[Drop connection]

    proxy --> connector --> pool
    pool -->|hit| use
    pool -->|miss| fresh --> use
    use --> keepalive
    keepalive -->|yes| release
    keepalive -->|no| drop

A connection is keyed by the peer hash — derived from the destination IP, SNI, TLS settings, client cert, etc. Two requests destined for the same logical upstream share connections.

Configuration

The relevant YAML keys (from docs/user_guide/conf.md):

Key Effect
upstream_keepalive_pool_size Total connections to keep in the pool
client_bind_to_ipv4 / client_bind_to_ipv6 Source addresses to bind to when connecting

The pool is per-runtime (per-service), so a server with two services has two independent pools.

HTTP/1

For HTTP/1, an idle connection sits in the pool until either reused or reaped. Pingora applies a small read on idle connections before checkout to detect "dead" connections — if the server closed the connection without telling us, we'd otherwise hand back a useless socket.

The unexpected data counter (added Mar 2026) increments when this read returns bytes that shouldn't be there — usually a buggy upstream. The counter is exposed for metrics.

The 0.7.0 release also added a configurable cap on how many times a single downstream connection can be reused. After the cap is hit, the proxy doesn't keep the upstream alive for further requests on the same downstream.

HTTP/2

HTTP/2 multiplexes streams over one connection, so the "pool" is more of a "stream registry." The H2 pool tracks "in-use" connections (those with active streams) separately from idle ones, so idle pruning doesn't tear down connections that have streams. The 0.7.0 release fixed an interaction where idle h2 connections weren't properly polled (tweak idle_timeout).

The 0.8.0 release added h2 stream window and connection window configuration keys for tuning flow control.

Custom transports

The "custom" HTTP session (pingora-core/src/protocols/http/custom/) supports encapsulated HTTP transports — HTTP-over-something-not-TCP. Pooling support for custom transports follows the H1 model.

Subrequests and pooling

Subrequests use the same pool as the parent request. The pipe-subrequests utility (added in 0.8.0) lets subrequests reuse the in-flight connection or capture a body for chained subrequests.

Source map

Concern File
Pool storage pingora-pool/src/connection.rs
HTTP-aware pool pingora-core/src/connectors/http/mod.rs
H2 stream pool pingora-core/src/connectors/http/v2/ (re-exported via the http module)
Per-pool LRU pingora-pool/src/lru.rs
HTTP/1 client session pingora-core/src/protocols/http/v1/client.rs
HTTP/2 client session pingora-core/src/protocols/http/v2/client.rs

See also

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

Connection pooling – Pingora wiki | Factory