Have you ever run a load test and suddenly seen a flood of Connection is not available, request timed out errors? In most cases, the root cause is shipping HikariCP to production with its default settings. The defaults are conservative values that prioritize “just working,” so production traffic requires tuning. In this article, we’ll walk through what the main parameters mean and how to derive appropriate values for them.

For query-side improvements, see Spring Data JPA Performance Optimization, and for designing connection counts when running multiple Pods, see How to Deploy a Spring Boot App to Kubernetes.

If you just want a working configuration example, jump ahead to Configuration example for application.yml.

What Is HikariCP?

HikariCP is a JDBC connection pool implementation for Java. Its name comes from the Japanese word “hikari” (light), and it is known for being fast and lightweight. It has been the default in Spring Boot since version 2.0, and adding spring-boot-starter-jdbc or spring-boot-starter-data-jpa as a dependency pulls in com.zaxxer:HikariCP transitively.

A connection pool is a mechanism that creates and holds a fixed number of DB connections in advance, then lends them out to the application for reuse. It not only eliminates the cost of re-establishing a TCP connection and re-authenticating on every request, but also controls the upper limit on the number of connections the application opens to the DB. Keep in mind that in HikariCP, Connection.close() does not mean a physical disconnect but rather “returning the connection to the pool.” This makes the discussion of leaks later on much easier to follow.

When lent-out connections don’t come back due to slow queries or connection leaks, the pool empties, and new requests wait for connectionTimeout before failing with a timeout exception. This is the true identity of the Connection is not available error mentioned at the beginning, commonly known as “DB connection exhaustion.”

Spring Boot Auto-Configures It

In Spring Boot, simply specifying spring.datasource.url / username / password auto-configures a HikariDataSource Bean, and you can tune the pool’s behavior via spring.datasource.hikari.* properties. The JDBC driver (mysql-connector-j or postgresql) must be added as a separate dependency.

Using HikariCP in Plain Java Without Spring

HikariCP itself is a library with no dependency on Spring, so you can also use it from standalone Java applications or batch jobs.

<!-- pom.xml (Spring Boot 外で使う場合はバージョンを明示。Maven Central で最新版を確認) -->
<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>HikariCP</artifactId>
  <version>6.3.0</version>
</dependency>
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/app");
config.setUsername("app");
config.setPassword("secret");
config.setMaximumPoolSize(20);
config.setConnectionTimeout(3000);
config.setPoolName("MyAppPool");

HikariDataSource dataSource = new HikariDataSource(config);

try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement("SELECT 1")) {
    ps.executeQuery();
} // close() でプールに返却される(物理切断ではない)

// アプリ終了時にプールごと閉じる
dataSource.close();

The setter names on HikariConfig map one-to-one to Spring Boot property names (setMaximumPoolSizemaximum-pool-size), and the meaning of each parameter is the same regardless of which approach you use.

What Is “HikariPool-1” in the Startup Log?

When you start a Spring Boot application, you’ll see log output like the following.

com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
com.zaxxer.hikari.pool.HikariPool  : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@1a2b3c
com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.

HikariPool-1 is the pool name auto-assigned when pool-name is not specified. If you have multiple DataSources, the number increments to HikariPool-2 and so on. There are three log messages worth remembering.

  • Start completed. indicates that pool initialization has finished. Because the pool is initialized at the moment a connection is first requested, depending on your configuration this may appear when the first request arrives rather than immediately after startup.
  • Exception during pool initialization. means not a single connection could be established. The cause is almost always an incorrect JDBC URL or credentials, or the DB not being up yet. It is not a pool size problem.
  • Shutdown initiated... is logged when the application shuts down. If in-flight requests error out right before this, check your graceful shutdown settings in How to Implement Graceful Shutdown and Zero-Downtime Deployment in Spring Boot.

Understanding the Key Parameters

There are many configurable parameters, but understanding the following six will cover the majority of cases.

Default: 10. Recommended: Start from (core count × 2) + 1, then adjust to roughly 10 to 30 based on Tomcat’s max-threads and the DB’s max_connections. The property name is spring.datasource.hikari.maximum-pool-size.

Default: Same as maximumPoolSize. Recommended: Keep it equal to maximumPoolSize and operate as a fixed-size pool. The HikariCP project explicitly recommends against letting the pool size fluctuate dynamically. The property name is spring.datasource.hikari.minimum-idle.

Default: 30000ms (30 seconds). Recommended: 3000 to 5000ms. The property name is spring.datasource.hikari.connection-timeout.

Default: 600000ms (10 minutes). Recommended: The default is usually fine. This setting is ignored when minimumIdle == maximumPoolSize. The property name is spring.datasource.hikari.idle-timeout.

Default: 1800000ms (30 minutes). Recommended: Several tens of seconds shorter than the DB’s wait_timeout. Since MySQL’s default wait_timeout is 8 hours, 30 minutes is already comfortably short, so you can generally leave it as is. The property name is spring.datasource.hikari.max-lifetime.

Default: 0 (disabled). Recommended: In environments where a firewall drops TCP sessions, set it to 30000 to 60000ms (30 to 60 seconds). It must be shorter than maxLifetime and at least 30 seconds. The property name is spring.datasource.hikari.keepalive-time.

How to Correctly Calculate maximumPoolSize

It’s tempting to think “just make it big to be safe,” but this can actually backfire. The official HikariCP wiki page “About Pool Sizing” presents the following formula.

connections = (core_count * 2) + effective_spindle_count

effective_spindle_count is the number of spinning disks, and for SSDs or RDS it is generally treated as 1. With 4 cores, that’s 4 * 2 + 1 = 9, so around 10 is the starting point.

It does not need to match Tomcat’s max-threads. Even with a maximum of 200 threads and a pool of only 20, HikariCP’s official design philosophy is that throughput is better when requests wait briefly in the application-side pool rather than contending on the DB side. Adjust based on measured pool utilization (hikaricp.connections.active).

Another thing you must not forget is the DB-side max_connections. If instance count × maximumPoolSize (or the sum across all pools if you have several) exceeds the DB’s limit, connections will be refused, so design with the total connection count in view.

Note that enabling Java 21 virtual threads does not change this formula, because the DB’s processing capacity stays the same. If anything, a large number of virtual threads will end up waiting on the pool, making connectionTimeout and monitoring of pending (covered later) even more important. For details, see Java 21 Virtual Threads and Spring Boot.

Failures Caused by Misconfiguring connectionTimeout and idleTimeout

If connectionTimeout is too short, even healthy requests will time out immediately under high load. Conversely, if it’s too long, threads stay blocked for extended periods and the entire server grinds to a halt. The default of 30 seconds is too long for most cases, so we recommend tightening it to around 3 to 5 seconds.

If maxLifetime is longer than the DB’s connection timeout setting (wait_timeout for MySQL), HikariCP will keep holding connections the DB has already closed, and a SQLException will occur the next time one is used. As a rule of thumb, set it several tens of seconds shorter than the DB’s wait_timeout. In environments where a firewall enforces a short TCP timeout, use keepaliveTime in combination.

Configuration Example for application.yml

The minimal configuration is just this. At the very least, start by revisiting maximumPoolSize and connectionTimeout.

# 最小構成
spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      connection-timeout: 3000

Here is the recommended configuration for production environments.

# 推奨構成
spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USER}
    password: ${DB_PASSWORD}
    hikari:
      # コア数・スレッド数・DB max_connectionsを元に算出
      maximum-pool-size: ${HIKARI_MAX_POOL_SIZE:20}
      minimum-idle: ${HIKARI_MAX_POOL_SIZE:20}
      # 高負荷時に正常リクエストを巻き込まない上限
      connection-timeout: 3000
      # DBのwait_timeout(例:28800s)より短く設定
      max-lifetime: 1800000
      # アイドル接続は10分で破棄
      idle-timeout: 600000
      # ファイアウォール環境では切断検知のため有効化
      keepalive-time: 60000
      pool-name: MyAppPool

The configuration keys are spring.datasource.hikari.* in both Spring Boot 2.x and 3.x. Passing the password via environment variables as in this example is the first choice, but if you have no option but to write it in the configuration file, the ENC() format encryption described in How to Encrypt Sensitive Values in Configuration Files with Jasypt in Spring Boot is an easy alternative.

Before and After Comparison

Here is an illustrative comparison under a load scenario of 100 concurrent connections, 20% of which are slow queries.

ConfigurationmaximumPoolSizeconnectionTimeoutTimeout error rate
Default1030000msapprox. 40%
Tuned203000msapprox. 3%
Oversized1003000msapprox. 8% (reduced throughput)

Note that oversizing increases thread contention on the DB side and actually lowers throughput.

Monitoring Pool Usage with Actuator

After tuning, add spring-boot-starter-actuator as a dependency and verify how the pool actually behaves using the HikariCP metrics automatically exposed via Micrometer.

<!-- pom.xml -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# メトリクスエンドポイントを有効化
management:
  endpoints:
    web:
      exposure:
        include: metrics

Check a metric by appending its name to the URL, such as /actuator/metrics/hikaricp.connections.active. The ones to focus on are as follows.

MetricMeaningHow to read it
hikaricp.connections.activeNumber of connections in useIf it’s constantly pinned at maximumPoolSize, the pool is undersized or there are slow queries
hikaricp.connections.idleNumber of idle connectionsIf it stays consistently high, the pool is oversized
hikaricp.connections.pendingNumber of threads waiting for a connectionIf it’s consistently above 0, action is needed
hikaricp.connections.timeoutCumulative count of connection acquisition timeoutsIf it keeps growing, connectionTimeout is being exceeded
hikaricp.connections.acquireTime taken to acquire a connectionIf p99 is in the hundreds of ms, waiting has become the norm
hikaricp.connections.usageTime connections are borrowedThe longer it is, the more likely slow queries or leaks

Constant waiting (pending) is a sign you should increase maximumPoolSize, but before doing so, check whether there are connections with long usage times, in other words whether slow queries or leaks are the real cause. If you have multiple DataSources, you can filter by the pool tag. For detailed visualization with Prometheus and Grafana, refer to Spring Boot Observability Setup (Micrometer + Prometheus + Grafana).

Common Errors and Troubleshooting

The typical errors that occur around HikariCP fall into a limited number of cause patterns.

Connection is not available, request timed out after Nms

This occurs when a connection could not be acquired from the pool within connectionTimeout. There are three main causes.

  1. The pool size is insufficient: If hikaricp.connections.active is constantly pinned at maximumPoolSize, the pool is undersized.
  2. Slow queries are holding connections for a long time: Check the hikaricp.connections.usage (borrow time) metric and cross-reference it with slow queries in your SQL logs.
  3. Connection leaks: The application may not be returning connections. These can be detected with leakDetectionThreshold, described below.

Detecting Connection Leaks with leakDetectionThreshold

When leakDetectionThreshold is set, HikariCP logs a warning if a lent-out connection is not returned within the specified time. Because it has a performance impact, we recommend using it in staging environments rather than production.

spring:
  datasource:
    hikari:
      # 60秒以上返却されない接続を警告
      leak-detection-threshold: 60000

The warning log includes the stack trace of the leaking code, making it easier to pinpoint the culprit.

HikariPool-1 - Failed to validate connection

This occurs frequently when maxLifetime is longer than the DB’s wait_timeout. It’s the pattern where HikariCP attempts to reuse a connection the DB has already closed and fails. Reconfigure maxLifetime to be shorter than the DB’s wait_timeout. In environments where a firewall or NAT drops TCP sessions that have been idle for a certain period, setting keepaliveTime to 30 to 60 seconds is also effective for detecting disconnections in advance.

Other Settings Worth Knowing

Finally, here is a roundup of settings that come up frequently in questions.

validationTimeout and connectionTestQuery

Generally, no configuration is needed. HikariCP checks liveness with JDBC4’s Connection.isValid() before lending out a connection, and validationTimeout (default 5000ms) is the upper limit on how long that check may take. connectionTestQuery is a backward-compatibility option for older drivers that don’t support JDBC4, and the project officially recommends not setting it for compliant drivers. If you do adjust validationTimeout, always keep it shorter than connectionTimeout.

Relaxing Startup Order Constraints with initializationFailTimeout

The constraint that the application won’t start unless the DB is up can be controlled with initializationFailTimeout (default 1ms). With 0, startup continues even if the connection fails, and with a negative value, the connection check at startup is skipped entirely. You can use -1 if you want to wait for a DB container to come up, but in production it’s safer to keep the default so that misconfigurations are caught early.

data-source-properties for MySQL

HikariCP itself is designed without a PreparedStatement cache, so caching is enabled on the JDBC driver side. The recommended values for MySQL from the official HikariCP wiki are as follows.

spring:
  datasource:
    hikari:
      data-source-properties:
        cachePrepStmts: true
        prepStmtCacheSize: 250
        prepStmtCacheSqlLimit: 2048
        useServerPrepStmts: true

These values are passed straight through to the driver, so for PostgreSQL you would specify driver-specific keys such as reWriteBatchedInserts: true.

Separating Configuration for Multiple DataSources

Use a different @ConfigurationProperties prefix for each DataSource, and assign a distinct pool-name to each.

@Bean
@ConfigurationProperties("app.datasource.primary")
public DataSourceProperties primaryProperties() {
    return new DataSourceProperties();
}

@Bean
@ConfigurationProperties("app.datasource.primary.hikari")
public HikariDataSource primaryDataSource(DataSourceProperties primaryProperties) {
    return primaryProperties.initializeDataSourceBuilder()
            .type(HikariDataSource.class)
            .build();
}
app:
  datasource:
    primary:
      url: jdbc:mysql://primary:3306/app
      hikari:
        pool-name: PrimaryPool
        maximum-pool-size: 20
    replica:
      url: jdbc:mysql://replica:3306/app
      hikari:
        pool-name: ReplicaPool
        maximum-pool-size: 10

In this case, note that you should estimate against the DB’s max_connections using the sum of maximumPoolSize across all pools × instance count.

Configuration Review Checklist

When reviewing your own project’s configuration, go through the following items.

  • Are you shipping maximumPoolSize to production at its default (10)?
  • Is maxLifetime longer than the DB’s wait_timeout?
  • Is connectionTimeout still at 30000ms (the default)?
  • Are Tomcat’s max-threads and maximumPoolSize in balance?
  • In a multi-instance setup, does instance count × poolSize exceed the DB’s max_connections?

Summary

HikariCP’s default settings are sufficient for development, but they must be revisited for production traffic. The safe approach is to first adjust maximumPoolSize and connectionTimeout to match reality, then fine-tune step by step while watching the metrics in Actuator.

For JPA query optimization, be sure to also read Spring Data JPA Performance Optimization.