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 = 10web 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 coresandRAILS_MAX_THREADS = 3–5. - If you need more concurrency than
max_connectionsallows, raisemax_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 exceededmax_connections.