Table of Contents [expand]
Last updated February 26, 2026
Connection pooling uses a pre-created set of reusable connection objects to connect to databases. Instead of opening a new connection for every request, your application retrieves one from the pool and returns it when done. This reduces network traffic, lowers the cost of creating connections, and eases garbage collector load.
Many Scala and Java frameworks include their own connection pooling APIs, but the configuration principles are the same. This guide shows how to create a database connection pool using JDBC and the Apache DBCP pooling library.
This guide assumes you have an existing Scala application that uses JDBC. You should also be familiar with Connecting to Relational Databases on Heroku with Java.
Using Apache DBCP
Open your application’s build.sbt file and add the following libraries to it.
libraryDependencies ++= Seq(
"org.postgresql" % "postgresql" % "42.7.10",
"org.apache.commons" % "commons-dbcp2" % "2.14.0"
)
Run sbt to download and install the new dependencies:
$ sbt clean compile
Next, open the source code for the class that creates connections and add the following import:
import org.apache.commons.dbcp2._
Then add a new singleton object to contain and configure the database connection pool at the end of the file:
object Datasource {
val connectionPool = new BasicDataSource()
connectionPool.setUrl(System.getenv("JDBC_DATABASE_URL"))
connectionPool.setInitialSize(3)
}
The JDBC_DATABASE_URL environment variable is set automatically by Heroku and contains the full JDBC connection string including credentials. setInitialSize(3) sets the initial size of the pool. The BasicDataSource object immediately creates these connections so they’re ready when your application starts receiving traffic.
Retrieve a connection from the pool like so:
val connection = Datasource.connectionPool.getConnection
val statement = connection.createStatement()
statement.executeUpdate("CREATE TABLE IF NOT EXISTS ticks (tick timestamp)")
statement.executeUpdate("INSERT INTO ticks VALUES (now())")
val resultSet = statement.executeQuery("SELECT tick FROM ticks")
while (resultSet.next()) {
println("Read from DB: " + resultSet.getTimestamp("tick"))
}
Once you have a connection object, use it exactly the same as any other JDBC Connection.
In addition to the initial size, you can configure the pool’s maximum size, the maximum lifetime of connections (before they’re discarded and replaced), and the minimum and maximum number of idle connections. Set all of these with methods on the BasicDataSource class.
Configure the Connection Pool
The number of idle connections to keep warm in your pool depends on the size and nature of your application. Many users find one connection per thread handling HTTP requests is sufficient (assuming threads handling HTTP requests are the only threads using connections). Your application may need more if it experiences very high throughput such that it can’t turn connections over to new threads quick enough. Or you may need fewer if not every HTTP request needs to access the database. Ultimately, profiling your application under production loads is the best way to determine the appropriate pool parameters.
In development you can see the number of connections used by your application by checking the database.
$ psql -h localhost
This opens a connection to your development database. You can then see the number of connections to your Postgres database by running:
select count(*) from pg_stat_activity where pid <> pg_backend_pid() and usename = current_user;
This returns the number of connections on that database:
count
-------
5
(1 row)
Under simulated production loads, this gives you a good indication of what size pool you need. There are, however, some constraints.
Maximum Database Connections
Heroku provides managed Postgres databases. Different tiered databases have different connection limits, listed on the Heroku Postgres add-on page. Lower tiers permit fewer connections than higher tiers. Once your database reaches the maximum number of active connections, it no longer accepts new connections, resulting in connection timeouts and exceptions in your application.
When scaling out, it is important to keep in mind how many active connections your application needs. If each dyno allows 5 database connections, you can only scale out to four dynos before you need to provision a more robust database.
With this knowledge, calculate the right number of connections each dyno needs based on your database’s connection limit.
Limit Connections with PgBouncer
You can scale out your applications with additional dynos until you reach your database connection limits. Before that point, we recommend limiting the number of connections required by each dyno using the classic PgBouncer buildpack. There is no Cloud Native Buildpack for PgBouncer.
PGBouncer maintains a pool of connections that your database transactions share. This keeps connections to Postgres, which are otherwise open and idle, to a minimum. However, transaction pooling prevents you from using named prepared statements, session advisory locks, listen/notify, or other features that operate on a session level. See the PgBouncer buildpack FAQ for full list of limitations for more information.
For many frameworks, you must disable prepared statements in order to use PGBouncer. Then set your app to use a custom buildpack that will call other buildpacks.
For JDBC, this requires adding prepareThreshold=0 to the connection string. But it may also be necessary to patch your JDBC driver.
Do not continue before disabling prepared statements, or verifying that your framework is not using them.
$ heroku buildpacks:add heroku/pgbouncer
Next, add the Scala buildpack so your application can build:
$ heroku buildpacks:add heroku/scala
Now modify your Procfile to start PgBouncer by adding bin/start-pgbouncer-stunnel to the beginning of your web entry. For example, change:
web: target/universal/stage/bin/scala-getting-started
To:
web: bin/start-pgbouncer-stunnel target/universal/stage/bin/scala-getting-started
Commit the results to git, test on a staging app, and then deploy to production.
For more information on connection pooling with JDBC and Apache DBCP, see the Apache Commons DBCP documentation.