LaunchPad

Database connections & Puma

Connection limits on self-hosted PostgreSQL and how to size Puma concurrency.

Unlike managed providers that sell connection tiers (Heroku Postgres caps connections at 20 / 120 / 500 by plan), LaunchPad runs self-hosted PostgreSQL on your own server. There is no artificial per-plan connection cap.

What the real limit is

The limit is PostgreSQL's max_connections — currently the default 100 — bounded by server RAM (roughly 9–10 MB per connection, so ~1 GB for 100). You can raise max_connections in postgresql.conf on a bigger server; for very high connection counts, put a PgBouncer pooler in front.

Sizing Puma against it

Each Puma thread holds one database connection (Active Record's pool should equal RAILS_MAX_THREADS). Your app's total connections are:

WEB_CONCURRENCY × RAILS_MAX_THREADS + background-worker connections

Keep that comfortably under max_connections, leaving headroom for migrations, psql sessions, and admin tools.

  • Example (default Postgres 100, a CX22): WEB_CONCURRENCY=2 × RAILS_MAX_THREADS=5 = 10 web connections, plus your job worker — well within budget. You have room up to roughly 80 before approaching the default cap.
  • Background workers count too: a Solid Queue worker needs its thread count plus a couple of connections for its dispatcher and scheduler. Make sure the database pool (and max_connections) covers web + worker together.

Rules of thumb

  • Start with WEB_CONCURRENCY = number of CPU cores and RAILS_MAX_THREADS = 3–5.
  • If you need more concurrency than max_connections allows, raise max_connections (and add RAM) or add PgBouncer — don't starve the pool.
  • Watch for PG::ConnectionBad / "too many clients" — that's the signal you've exceeded max_connections.