Guide: Create a DataSource Pool

April 12, 2026 · View on GitHub

Purpose

This guide provides step-by-step instructions for creating and configuring DataSource pools for different deployment scenarios. Follow the steps for the scenario that matches your use case.


Prerequisites

  • A Java project with Maven
  • io.ebean:ebean-datasource dependency already added to pom.xml
  • Database connection details (URL, username, password)
  • Understanding of your deployment environment (standard server, Kubernetes, AWS Lambda, etc.)

Scenario 1: Basic DataSource Pool (Standard Server/VM)

Use this configuration for traditional server deployments.

Step 1 — Create the pool builder

DataSourcePool pool = DataSourcePool.builder()
  .name("mypool")
  .url("jdbc:postgresql://localhost:5432/myapp")
  .username("app_user")
  .password("password")
  .minConnections(5)
  .maxConnections(50)
  .build();

Step 2 — Use the pool in your application

try (Connection connection = pool.getConnection()) {
  try (PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE id = ?")) {
    stmt.setInt(1, 123);
    try (ResultSet rs = stmt.executeQuery()) {
      while (rs.next()) {
        // Process results
      }
    }
  }
}

Scenario 2: Read-Only Pool

Use this configuration for read-only workloads (analytics, reporting, caching layers). This adds readOnly(true) and autoCommit(true) options when building the pool.

Step 1 — Create a read-only pool

DataSourcePool readOnlyPool = DataSourcePool.builder()
  .name("mypool-readonly")
  .url("jdbc:postgresql://read-replica.example.com:5432/myapp")
  .username("readonly_user")
  .password("password")
  .readOnly(true)           // Signal no writes will occur
  .autoCommit(true)         // Skip transaction overhead
  .minConnections(5)
  .maxConnections(30)
  .build();

Step 2 — Use the read-only pool

try (Connection connection = readOnlyPool.getConnection()) {
  try (PreparedStatement stmt = connection.prepareStatement("SELECT * FROM large_table")) {
    try (ResultSet rs = stmt.executeQuery()) {
      while (rs.next()) {
        // Process results - queries will be faster with read-only optimization
      }
    }
  }
}

Scenario 3: Kubernetes Deployment

Use this configuration when deploying to Kubernetes clusters.

Step 1 — Create a pool with warm-up configuration

When pods start, they immediately receive production traffic. Start with initialConnections set higher than minConnections to avoid cold-start connection creation overhead.

DataSourcePool pool = DataSourcePool.builder()
  .name("mypool")
  .url("jdbc:postgresql://postgres.default.svc.cluster.local:5432/myapp")
  .username("app_user")
  .password(System.getenv("DB_PASSWORD"))  // Use environment variables
  .minConnections(5)
  .initialConnections(20)     // Start with 20 connections ready
  .maxConnections(50)
  .build();

Step 2 — Use environment variables for configuration

For Kubernetes deployments, externalize database credentials:

String dbUrl = System.getenv("DATABASE_URL");
String dbUser = System.getenv("DATABASE_USER");
String dbPass = System.getenv("DATABASE_PASSWORD");

DataSourcePool pool = DataSourcePool.builder()
  .name("mypool")
  .url(dbUrl)
  .username(dbUser)
  .password(dbPass)
  .minConnections(5)
  .initialConnections(20)
  .maxConnections(50)
  .build();

Scenario 4: AWS Lambda

Use this configuration for AWS Lambda functions.

Step 1 — Create a minimal pool

The ebean datasource automatically detects when it is running in Lambda via the LAMBDA_TASK_ROOT environment variable and disables background heartbeat validation to optimize for cost and lambda suspend behaviour.

DataSourcePool pool = DataSourcePool.builder()
  .name("mypool")
  .url("jdbc:postgresql://mydb.region.rds.amazonaws.com:5432/myapp")
  .username("app_user")
  .password(System.getenv("DB_PASSWORD"))
  .minConnections(1)
  .initialConnections(2)
  .maxConnections(10)
  .build();

Step 2 — Understanding Lambda connection pooling

Important: Connection pooling in Lambda is per-Lambda instance, not per-invocation:

  • A single Lambda container can be reused across many warm invocations
  • Connection pool persists across warm invocations
  • Cold starts get a fresh pool
  • Connections are trimmed automatically if unused

Configuration Reference

Common Settings

SettingDefaultPurpose
minConnections2Minimum connections to maintain in the pool
initialConnectionsSame as minConnectionsConnections to create when pool starts (useful for Kubernetes/warm-up)
maxConnections200Maximum connections to allow
readOnlyfalseSet to true for read-only workloads
autoCommitfalseSet to true to skip transaction boundaries
validateOnHeartbeattrue (false in Lambda)Enable background connection validation
heartbeatFreqSecs30How often to validate connections (seconds)

Typical Sizing

Development/Testing:

.minConnections(1)
.initialConnections(1)
.maxConnections(5)

Production - Standard Server:

.minConnections(5)
.initialConnections(10)
.maxConnections(50)

Production - High Traffic:

.minConnections(10)
.initialConnections(40)
.maxConnections(100)

Lambda - Standard:

.minConnections(1)
.initialConnections(2)
.maxConnections(10)

Lambda - Provisioned Concurrency:

.minConnections(2)
.initialConnections(10)
.maxConnections(20)

Next Steps