Skip to main content

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.
Note

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 the idle_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 global default_upstream_timeout setting.

Example of HTTP request lifecycle and timeout settings

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.

Long-lived connections behavior

When you create a TCP or Websocket connection, Pomerium validates the access policy at the time the connection is made.

Currently, there is no mechanism in place to terminate long-running connections if a policy becomes invalid.

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 NameProtocolPurposeTypical PortKey Settings
http-ingressTCP/HTTPAccepts insecure HTTP traffic, ACME/Autocert HTTP-01 challenges, and (if enabled) performs plain-HTTP to HTTPS redirects80address with insecure_server: true or http_redirect_address
https-ingressTCP/HTTPSPrimary HTTPS entrypoint (TLS termination)443address
quic-ingressUDP/QUIC (HTTP/3)HTTP/3 listener over QUIC443address with codec_type: http3
grpc-ingressTCP/gRPCInbound internal gRPC between Pomerium servicesrandomgrpc_addr
grpc-egressTCP/gRPCOutbound internal gRPC to other Pomerium servicesrandom(created automatically)
envoy-adminTCP/HTTPEnvoy administration interfacevariesenvoy_admin_address
metrics-ingressTCP/HTTPPrometheus metrics (Pomerium + Envoy)9902 (default)metrics_address
sshTCP/SSHNative SSH proxy22 (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

Feedback