---
title: "Pumas, Routers & Keepalives—Oh my!"
description: "Heroku’s Router 2.0 beta exposed elevated tail response times and request failures in a subset of Puma applications, especially under 200 milliseconds of server-side lag and 30 requests per second. Investigation compared legacy and Router 2.0 connections, examined TIME_WAIT behavior, and tested Go’s http.Transport idle-connection limit; raising MaxIdleConnsPerHost from the default of 2 to 100 stabilized connection states but did not improve latency. The underlying issue was Puma’s handling of persistent connections, where request balancing could leave work queued behind busy connections; disabling keepalives eliminated the problem, while thread, worker, and max_fast_inline tuning were offered as alternatives. Load tests found the issue unique to Puma, and the update reports that Puma 7.0 fixed the bug, recommending version 7.0.4 or higher."
---

# Pumas, Routers & Keepalives—Oh my!

[Heroku](https://yomu.fyi/company/heroku) · Elizabeth Cox · Nov 21, 2024

**Type:** Incident / postmortem

## Summary

Heroku’s Router 2.0 beta exposed elevated tail response times and request failures in a subset of Puma applications, especially under 200 milliseconds of server-side lag and 30 requests per second. Investigation compared legacy and Router 2.0 connections, examined TIME\_WAIT behavior, and tested Go’s http.Transport idle-connection limit; raising MaxIdleConnsPerHost from the default of 2 to 100 stabilized connection states but did not improve latency. The underlying issue was Puma’s handling of persistent connections, where request balancing could leave work queued behind busy connections; disabling keepalives eliminated the problem, while thread, worker, and max\_fast\_inline tuning were offered as alternatives. Load tests found the issue unique to Puma, and the update reports that Puma 7.0 fixed the bug, recommending version 7.0.4 or higher.

## Context

During the Router 2.0 beta, a subset of Puma applications showed sharply increased tail response times under load, with throughput reductions and H12 request timeouts. The issue appeared when applications had significant server-side latency and did not reproduce in the same way across other tested languages and frameworks.

## Approach / What changed

The investigation reproduced the behavior with a Puma and Rails application using 200 milliseconds of artificial server lag, compared TCP connection states between the legacy and Router 2.0 routers, and tested Go HTTP transport settings. It then evaluated disabling router-to-dyno keepalives and tuning Puma threads, workers, and max\_fast\_inline. The update identifies Puma 7.0 as containing a fix.

## Takeaways

- With 200 milliseconds of server-side lag at 30 requests per second, Puma with Router 2.0 keepalives reached 23 requests per second, with 3,455-millisecond P95 and 5,375-millisecond P99 response times; disabling keepalives restored 30 requests per second and reduced P99 to 335 milliseconds.
- Go’s http.Transport defaults MaxIdleConnsPerHost to 2. Raising it to 100 changed Router 2.0 connection states from mostly TIME\_WAIT to stable established connections, but had no positive effect on tail response times.
- The issue was unique to Puma in the load tests; the recommended long-term fix is Puma 7.0.4 or higher, while disabling keepalives through the Router 2.0 labs flag is a stopgap.

**Tags:** [Performance](https://yomu.fyi/topic/performance), [Ruby on Rails](https://yomu.fyi/topic/ruby-on-rails)

- Source: [Heroku](https://www.heroku.com/blog/pumas-routers-keepalives-ohmy)
- Source URL: https://www.heroku.com/blog/pumas-routers-keepalives-ohmy
- Ingested by Yomu: 2026-09-10T00:05:07.189Z

[Read original post](https://www.heroku.com/blog/pumas-routers-keepalives-ohmy)
