Connections
Pomerium enables proxying of protocols such as HTTP, SSH, and even MCP over TCP and UDP, uniformly applying access policies across the routed traffic.
This page provides an overview of how Pomerium manages connections for those protocols. It walks through the HTTP connection lifecycle (TLS, routing, authorization, upstream selection, and response), outlines performance considerations, details the applicable timeouts, explains handling of long-lived connections and safe connection draining during configuration changes, and describes the Envoy listeners Pomerium configures and their key settings. It also notes related metrics and stat prefixes for observability.
HTTP connection lifecycle
TCP and UDP traffic can be tunneled over HTTP, so let's focus on the HTTP connection lifecycle.
1. Downstream connection and TLS termination
- A client, usually a web browser, Pomerium CLI, or Pomerium Desktop App, initiates a connection to Pomerium. This connection can be HTTP/1.1, HTTP/2, or HTTP/3.
HTTP/2 and HTTP/1.1 are allowed by default, with most modern browsers defaulting to HTTP/2.
HTTP/3 can be enabled by setting the codec_type
parameter. This is recommended when proxying UDP traffic.
In rare circumstances, you may need to force HTTP/1.1 using the codec_type
parameter.
- Pomerium presents a server certificate for use based on the provided
certificates
. - Optionally, client-side mTLS can be enabled for downstream connections to enforce the use and validation of client certificates.
- Pomerium enforces some minimum TLS requirements for best practice, which are non-configurable.
2. Request initiation
After TLS handshakes are complete, the downstream client sends an HTTP request. The proxy parses this request, matches it against the configured routes, and determines the upstream service to which the request should be forwarded.
3. Request authorization
The request is authorized according to the policies applied to the Pomerium route before it is forwarded to the upstream service. For more details, see request lifecycle.
4. Upstream connection and request forwarding
- Load Balancing: Pomerium utilizes its load balancing algorithms to select the optimal upstream service instance to process the request. This selection is made from a list of healthy service instances, which is maintained by service discovery and health checking subsystems. The algorithm used for load balancing can be configured and includes options such as Round Robin, Least Request, Random, and more. See Load Balancing Policy options and Load Balancing Policy Config for more information.
- Health Checks: Health checks are key to maintaining the list of viable service instances for load balancing. Pomerium can be set up to routinely check the health of each upstream service instance using methods like HTTP calls, TCP connections, or custom health checks. If an instance fails the health check, it is deemed unhealthy and removed from the list of available instances for load balancing. This ensures that traffic is not directed to instances incapable of handling it.
- Connection & Request Forwarding: Upon selecting a healthy upstream service instance, Pomerium establishes a connection with this instance (if one does not already exist) and forwards the client's request to it. Connection pooling is employed to improve efficiency by reusing existing connections where possible. This behavior can vary depending on whether the connection is HTTP/1.1 (where each connection can be reused but only manages one request at a time) or HTTP/2 (which supports multiple concurrent requests over a single connection).
5. Response forwarding
The upstream service processes the request and sends a response back to Pomerium, which subsequently forwards the response to the client.
6. Connection termination
The connections between the client, Pomerium, and the upstream service are maintained for future requests and are terminated in accordance with the Timeouts.
Request Performance
Web apps, especially modern single-page applications, often handle multiple requests simultaneously. Such apps can benefit significantly from using HTTP/2 transport from the downstream, through Pomerium, and up to the upstream application server.
Typically, HTTP/2 necessitates TLS. Thus, enabling TLS between Pomerium and the upstream applications not only boosts security, but also significantly enhances performance, leading to a smoother and faster user experience.
Timeouts
Different timeouts apply at various stages of the connection lifecycle:
-
Upstream Connection timeout: This timeout is applicable when Pomerium is attempting to establish a connection with an upstream service. If the connection cannot be established within the specified timeout, Pomerium will return an error to the client. This timeout, which defaults to 10s and cannot be configured, includes the TLS handshake.
-
Stream idle timeout: This timeout applies to periods of inactivity between the downstream client and Pomerium, or between Pomerium and the upstream service. If no requests are received within the specified timeout, the connection is terminated. This timeout can be configured via the
timeout_idle
global parameter and can be disabled (but not exceeded) for a specific route using theidle_timeout
value. -
Request timeout: This timeout applies to the total time a request can take, which includes connection establishment, request forwarding, processing at the upstream service, and response forwarding. If a complete response is not received within the set timeout, Pomerium will return an error to the client. This timeout can be configured via the
timeout_write
parameter. It may need adjustment, for instance, when handling large uploads. -
Upstream timeout: This timeout applies to the time a request takes to travel from Pomerium to the upstream service and back. It can be configured per-route via the
timeout
parameter and is considered part of the request timeout. By default, it aligns with the globaldefault_upstream_timeout
setting.
Long-lived connections
Most browser HTTP requests are relatively short-lived. However, certain upstream applications may require long-lived connections that span hours or even days. Examples include Websockets, gRPC streaming, and proxied TCP connections.
From the perspective of HTTP request management, once such a request is initiated, timeouts will apply, potentially terminating the long-lived request. For TCP routes and HTTP routes marked for allow_websockets
, timeouts adjust automatically. For connections involving long-lived requests, such as gRPC streaming API or server-sent events, you need to set the route idle_timeout
to 0
to disable it.
Draining connections
Most configuration changes (for example, adding or editing a route) are handled seamlessly and do not impact long-lived connections.
However, major changes to global parameters, including TLS certificates, typically require the connection handler to be reconfigured. Internally, this is handled by creating a new handler to accept new requests, while the old handler stops accepting new requests and sends an HTTP/2 GOAWAY or adds Connection: close
for HTTP/1 upon request completion, prompting the downstream client to close the current connection.
After a grace period for this transition (~10m), any active connections are terminated.
You may observe the process of connections with envoy_listener_manager_total_filter_chains_draining
metric gauge representing many draining processes are there. Normally you should see a value of 1
when there's an active draining process, or 0
if there's no draining currently underway.
If your deployment requires long-lived connections, ensure you use long-lived TLS certificates and avoid other significant global configuration changes.
Listeners
This section describes the network listeners that Pomerium configures in Envoy: what they do, when they are created, and the configuration settings that affect them.
Summary Table
Listener Name | Protocol | Purpose | Typical Port | Key Settings |
---|---|---|---|---|
http-ingress | TCP/HTTP | Accepts insecure HTTP traffic, ACME/Autocert HTTP-01 challenges, and (if enabled) performs plain-HTTP to HTTPS redirects | 80 | address with insecure_server: true or http_redirect_address |
https-ingress | TCP/HTTPS | Primary HTTPS entrypoint (TLS termination) | 443 | address |
quic-ingress | UDP/QUIC (HTTP/3) | HTTP/3 listener over QUIC | 443 | address with codec_type: http3 |
grpc-ingress | TCP/gRPC | Inbound internal gRPC between Pomerium services | random | grpc_addr |
grpc-egress | TCP/gRPC | Outbound internal gRPC to other Pomerium services | random | (created automatically) |
envoy-admin | TCP/HTTP | Envoy administration interface | varies | envoy_admin_address |
metrics-ingress | TCP/HTTP | Prometheus metrics (Pomerium + Envoy) | 9902 (default) | metrics_address |
ssh | TCP/SSH | Native SSH proxy | 22 (default) | ssh_address |
Details
HTTP Ingress (http-ingress
)
Handles insecure HTTP traffic when insecure_server
is enabled, serves ACME / autocert challenges, and otherwise redirects to HTTPS. May also be created when http_redirect_address
is set explicitly.
Protocol: TCP with HTTP connection manager.
HTTPS Ingress (https-ingress
)
Primary secure listener. Terminates TLS and forwards to appropriate upstream clusters.
QUIC Ingress (quic-ingress
)
Enables HTTP/3 over QUIC for improved performance (reduced latency, better performance on lossy networks). Requires UDP port open and codec_type: http3
.
gRPC Ingress (grpc-ingress
)
Internal gRPC server endpoint used by other Pomerium services. TLS terminated unless grpc_insecure
is set.
gRPC Egress (grpc-egress
)
Logical grouping for outbound internal gRPC connections initiated by this instance. Created automatically when internal services are enabled.
Envoy Admin (envoy-admin
)
Provides access to Envoy's admin interface (stats, config dump, etc). Not exposed publicly by default; restrict at the network layer.
Metrics Ingress (metrics-ingress
)
Serves combined Pomerium and Envoy Prometheus metrics at metrics_address
. Name includes a hash when multiple instances are present. Use for scraping by Prometheus.
SSH (ssh
)
Native SSH proxy listener created when SSH access capability is enabled and ssh_address
is set. Supports policy-based access control over SSH.
Configuration Notes
- All TCP listeners enable SO_REUSEPORT on Linux for better multi-core scaling.
- Per-connection buffer limits default to 32KB.
- Listeners are only created when the relevant feature / service is enabled (e.g., no SSH listener unless SSH access configured).
- QUIC requires UDP reachability and proper ALPN support by clients.
- The metrics listener may use a hash-based name internally for uniqueness.
Stat Prefixes
Each listener contributes a stat prefix used in Envoy metrics, helping differentiate traffic classes and drive alerting.
See the Envoy docs: https://www.envoyproxy.io/docs/envoy/latest/configuration/listeners/stats#listener