Tuning the Apache Fineract database connection pool

Fineract opens database connections through HikariCP, and the pool size on each instance decides how many requests reach PostgreSQL at once. This page explains how to size pools per instance type, set timeouts around Close of Business, and stay within connection limits.

On this page

Why the Apache Fineract database connection pool decides throughput and outages

Each Fineract instance holds a pool of database connections, and the pool size and timeouts on that instance decide how many requests reach PostgreSQL at the same moment. Fineract uses HikariCP for this pool, and its behavior is set through FINERACT_HIKARI_* environment variables. Because the pool is configured per instance, the total number of connections PostgreSQL sees is the sum across every read, write and batch instance, multiplied by the number of replicas you run. A pool that looks reasonable on one pod can exhaust the server once the deployment scales out.

The project's documentation describes read, write and batch instance types, set by environment variables, and recommends one write instance, one batch instance and multiple read instances on database read replicas for high load. Each type has a different workload: read instances serve queries, the write instance handles transactions and Liquibase migrations, and the batch instance runs the Close of Business job. A single pool setting applied to all three will rarely fit all of them. Plan the pool for each instance type separately, and do it before go-live, again whenever you add read instances, and before you change the COB schedule if the batch job is already running for hours on a large book.

Slow API responses under load usually show up as requests waiting for a free connection, and connection errors appear when waits exceed the configured timeout. Treat the pool as a shared limit that must be reconciled with the PostgreSQL connection ceiling and any pooler in front of it. Confirm the exact FINERACT_HIKARI_* setting names, their defaults and their behavior in the platform documentation for the release you run, since these details change between versions. Then check them against your database configuration and your instance counts.

How Fineract's read, write and batch instances split database load

Fineract assigns each instance one job through environment variables, and those jobs create very different database demand. Pool sizing only makes sense once you know which role each instance plays, which database it talks to, and which work it must finish without starving the others.

Read instances on replicas

Read instances serve API calls that only query data, such as client lookups, loan schedules and reports. The Fineract documentation recommends running multiple read instances against database read replicas for high load, which moves this traffic off the primary. Check that each read pool points at a replica, and that replica lag is acceptable for the screens and reports that use it. Read instances can usually be scaled out, so their pool size should be set per instance and multiplied by the replica count you plan to run.

One write instance

The write instance handles transactions that change balances, such as disbursements, repayments and postings. The documentation recommends exactly one write instance, so its pool is the one that must absorb peak posting activity against the primary database. Treat its pool as the main constraint on concurrent writes, and confirm the total connections it opens against the primary's connection limit before adding any other instance that can write.

One batch instance

The batch instance runs scheduled work, including the Close of Business job that can run for several hours on large deployments. Its connections are held for long stretches, so a batch pool that is sized like a web pool can hold connections for hours while the write path waits. Confirm with your team which instance runs the scheduler and how long a full COB run takes in your own tests, since those timings drive the timeout settings you will choose.

Liquibase migration run

Liquibase database migrations run only on write instances, and on Kubernetes they can run separately using the liquibase-only Spring profile. This is a short-lived process that changes schema, so it needs its own connection budget during upgrades. Do not assume the migration job can borrow connections from the running write pool. Plan the upgrade window so that migration connections, the write instance and any batch job do not compete for the same primary database capacity.

Sizing across the four roles

The four roles together define the total connection demand on each database, not just on each instance. Write down the instances you expect at peak, the database each one uses, and the connections each pool can open, then compare that total with the limits of the primary and of each replica. Ask your Fineract implementer to show this calculation for your planned topology before go-live, and repeat it after any change to instance count.

Pool settings by instance type and when each fits

This table compares the four Fineract instance roles (read, write, batch and the Liquibase-only job) by workload, database target, pool-sizing concern and the question to confirm before setting values. Read it as a checklist for each role, then confirm the exact FINERACT_HIKARI_* names and defaults against the platform documentation for your release, because the supplied sources do not give pool sizes or timeout values.

Instance typeConnection targetPool-sizing concernQuestion to confirm
Read instanceRead replica of the tenant databasesMany concurrent read requests can open many connections, so the replica's connection limit caps the total across all read instances.Which replica endpoint each tenant uses, and how the replica's maximum connections are shared across read pods.
Write instancePrimary databaseServes all transactional API writes, so its pool competes with the batch and job connections for primary connection slots.Whether the write pool must leave headroom for the batch instance and any Liquibase or admin sessions on the primary.
Batch instancePrimary databaseClose of Business runs for several hours on large deployments, so long-held connections and timeouts during COB are the main risk.How the batch pool behaves during COB, and whether connection timeout and maximum lifetime values need separate settings from the write pool.
Liquibase-only jobPrimary database, used brieflyRuns schema migrations only on write-capable setups, so it needs a short-lived connection rather than a steady pool.Whether the liquibase-only profile uses the same pool settings, and that no write instance runs migrations during the same rollout.
Timeout and lifetime settings (all roles)Applies to every pool that connects to PostgreSQLConnection timeout, idle timeout and maximum lifetime control how stale or broken connections are replaced, especially after failovers or replica restarts.The names, units and default behavior of each FINERACT_HIKARI_* timeout and lifetime variable in your release, and how they interact with PostgreSQL idle session settings.
PostgreSQL and replica connection limitsServer-side limit across all instancesTotal pool maximums across every instance and tenant must fit under the server's connection limit, with room for administrative sessions.The server's maximum connection setting, any pooler in front of it, and the arithmetic the vendor or DBA expects for your instance count.

Treating pool size as a connection budget across every instance

The number that matters for HikariCP is not the pool size on one Fineract instance but the sum of every pool that can open connections to PostgreSQL at the same time. Each read, write and batch instance holds its own pool, and each tenant configured from the tenants database can draw on connections as well. Before you choose a value for the FINERACT_HIKARI_* settings, write down the instance count per type, the number of Kubernetes replicas each deployment may scale to, and the tenants that share the same server. Multiply the maximum pool size by that total and compare the result with the server's maximum connection setting, minus the connections reserved for administrators, monitoring and migrations. That comparison is the budget you are actually spending.

The budget is easy to exceed because scaling works against you. A pool that looks safe on one instance can exhaust the server once a horizontal autoscaler adds replicas during month-end load, or once a second deployment is pointed at the same database for a region or test environment. A batch instance running Close of Business work may hold connections for hours, so its pool can stay full while read instances compete for what remains. Rolling upgrades also temporarily run old and new pods together, which briefly doubles the demand. Ask the team who controls each scaling rule and whether anyone reviews the combined total when a rule changes.

Confirm the server's connection ceiling and any pooler in front of it before you set any pool size. The PostgreSQL platform documentation describes the instance types but does not give your server's limits, which depend on your hardware, managed service tier and other applications. Server-side settings, read replica limits and connection pooling are covered on the PostgreSQL setup for Apache Fineract page, so use that page to confirm the ceiling. Then agree a written budget with the platform owner, with headroom for growth and for the maintenance window, and treat any change to replica counts or tenant count as a change to that budget.

Setting up and checking Fineract's connection pools

This sequence sizes the HikariCP pool for each Fineract instance type against the database's connection ceiling, then proves the settings hold under load and through a Close of Business window. The result is a documented, version-controlled set of pool values that the team has checked against live connection counts.

  1. Inventory every Fineract instance and its role

    The platform team lists each running Fineract instance with its instance type (write, batch or read), its tenant database targets, its replica count and the deployment object that runs it, such as a container service, a Kubernetes Deployment or a Helm release. Read instances are mapped to the database read replica they use, and any Liquibase migration job is recorded separately because it runs only on write instances. The check is a single table in which every instance has exactly one role and one data source, and the team can point to where each instance's environment variables are defined.

  2. Confirm the PostgreSQL connection ceiling

    A database administrator reads the server's maximum connection setting and the number of connections reserved for superusers and maintenance, then subtracts other clients such as monitoring agents, backup tools, reporting users and any connection pooler. Read replica limits are confirmed the same way on each replica, since a replica has its own ceiling. The output is a written budget of connections available to Fineract on each server, which becomes the upper bound for the sum of all pool maximums. Ask the vendor or your database team to confirm how your PostgreSQL service and any pooler count connections, because managed services and proxies can differ from a self-managed server.

  3. Set FINERACT_HIKARI_* variables per instance type

    Pool values are set for each instance type through the deployment's configuration, using the FINERACT_HIKARI_* environment variables in the container environment, Kubernetes ConfigMap or Secret, or the equivalent in your deployment tooling. Write instances, batch instances and read instances are sized separately, so the batch pool does not compete with interactive traffic for connections. Values are kept out of the container image so that a change does not require a rebuild. The check is that each running instance reports the intended values at startup, which the team confirms in the application logs or by reading the environment inside the running container.

  4. Start instances one at a time

    The team starts or restarts instances one at a time, beginning with the write instance and then the batch and read instances, rather than scaling all replicas at once. After each start, the database administrator watches the active connection count per database and per role, and the platform team checks the pool metrics that the instance exposes through its health and metrics endpoints. The step is complete when connections settle at a level inside the budget from the previous step and no instance logs repeated pool acquisition timeouts. If the count climbs toward the ceiling, the team stops scaling and reviews the maximums before continuing.

  5. Run a load test that includes a COB window

    The team runs a load test against a non-production copy of the tenant databases with representative data volumes, covering normal read and write traffic and a Close of Business run scheduled inside the test window. Connection counts, pool wait times and timeout errors are recorded for the whole run, including the period when the batch job runs. Because the Close of Business job can run for several hours on deployments with millions of accounts, the test must be long enough to see its full profile, and the team should confirm the expected batch duration for their own volume rather than assuming a figure. The step is complete when the test shows no pool exhaustion, the connection total stays inside the budget and the batch job finishes without timeout errors.

  6. Adjust timeouts and maximums from the results

    The team compares the load test results with the initial values and changes only the settings the evidence supports, such as the maximum pool size for an instance type or the connection and idle timeouts. Each change is retested with the same scenario so that its effect is isolated. The output is a final set of values per instance type, with the test result that justified each one. Changes that would push total connections past the ceiling are rejected and the team reduces replica counts or pool sizes instead.

  7. Record final values in configuration management

    The approved FINERACT_HIKARI_* values, the instance-to-role mapping, the connection budget per database server and the test evidence are committed to the team's configuration management repository and linked to the deployment manifests. Production changes to these values go through the same review as other configuration changes, and the connection budget is rechecked whenever an instance or replica is added. The step is complete when a reviewer can trace each running value to a committed file and a load test result, and when a new team member can see from the repository alone what the pool sizes are and why.

How Close of Business changes pool timeouts and connection demand

The Close of Business batch job is the workload most likely to exhaust the Fineract connection pool, and it runs on a different schedule from everything else. The Close of Business Spring Batch job can run for several hours on deployments with millions of accounts. During that window, batch threads hold connections for long stretches while they read loan and savings state, post accruals and write results, so the pool can look healthy on average and still be fully occupied at the moments when interactive reads and writes need it. Pool wait timeouts usually appear here first: API requests queue for a connection, time out, and clients retry, which adds more load to the same pool. Confirm in the Fineract documentation which instance type runs batch work in your topology, since that decides whether COB competes with interactive traffic in the same JVM at all.

Remote partitioning changes the connection arithmetic. The Fineract README describes splitting the COB job across worker nodes coordinated through ActiveMQ or Kafka, so each worker opens its own pool and the total connection count to PostgreSQL grows with the number of workers, not with the number of batch threads on one host. Before you size FINERACT_HIKARI_MAXIMUM_POOL_SIZE or the PostgreSQL max_connections limit, ask the vendor or check the platform documentation how partitioned workers obtain their database connections, whether each partition shares a pool, and how the messaging broker's consumer concurrency maps to active connections. Treat these answers as assumptions to verify in a test environment, not as settled sizing rules.

The practical decision is whether batch and interactive traffic should share a pool configuration. Separate pools let you cap COB connections without starving the API, but they also raise the total connection count and require a clear rule for which instance runs which work. A shared pool is simpler and keeps the total small, but a long COB can then starve interactive requests. Whichever you choose, measure COB duration and pool wait time together across the same time windows. A longer COB alone is not a fault, and a pool wait spike alone may have another cause. Only the two signals together show whether COB is the source of the contention, and that evidence should set the timeout values you test.

Operational and security considerations for database connections

Pool size is only one part of a safe Fineract database setup. The controls below decide who can open connections, how credentials are handled, and how a team notices pool pressure before it becomes an outage during Close of Business.

Keep credentials out of images

Store the tenants database credentials and the primary write database credentials in a secrets manager, then inject them into each Fineract instance at runtime through environment variables. Do not bake them into the image or commit them to deployment manifests. Check that rotating a password does not require rebuilding the image, and that every instance type reads the same secret source.

Restrict database network access

Allow inbound connections to PostgreSQL only from the Fineract read, write and batch instances and from approved administration hosts. Place the database in a private subnet or network segment with no public endpoint. Confirm that connection counts from unexpected sources are visible, because a stray client that holds connections can starve the pool used by the batch instance.

Alert on pool wait time

Track active and idle connections per instance, pending connection requests, and connection acquisition wait time. Set alerts that fire when waits rise during business hours or the COB window, not only when the database is down. A growing wait time usually shows the pool is too small for the load or that long transactions are holding connections, and it is the earliest signal you will get.

Separate read and write credentials

Give read instances a database role that can only read, connected through the read replica endpoint, and reserve the write role for the write and batch instances. Confirm the read role cannot run migrations or write tables. This limits the damage from a compromised read pod and keeps replica traffic from consuming primary connection slots.

Document every pool change

Record each change to a FINERACT_HIKARI_* setting, its value, the instance type it applies to, the reason, and the test evidence behind it under your change control process. Pool settings interact with PostgreSQL connection limits and replica capacity, so an undocumented change can be hard to trace when a later incident appears. Include the rollback value in each record.

Keep sensitive data out of connection logs

Review how connection failures, driver messages and stack traces are logged. Connection URLs, usernames and query parameters can appear in error output, and some drivers include host details in exceptions. Confirm that logs redact credentials, that log levels for the pool are set deliberately, and that retention and access to those logs follow the same rules as other financial system records.

What to confirm on PostgreSQL, read replicas and managed databases

On a PostgreSQL server you run yourself, the connection ceiling is a server setting, and every Fineract instance draws from it along with any other application, batch job, monitoring agent or administrator session. Summing the pool maximums across all Fineract instances and the tenant connections gives the number the server must accommodate. Confirm the following with the database owner:

  • Maximum connection setting: the server-wide limit that applies to all clients combined, and whether it was changed from the value set when the cluster was built.
  • Reserved superuser connections: how many slots are held back for administrators, so that a maintenance session can still connect during an incident.
  • Memory impact: how the server's per-connection memory use was budgeted for the number of connections you plan to allow.
  • Other consumers: scheduled backups, replication slots, monitoring exporters and migration jobs that also hold connections during the Close of Business window.
  • Restart requirements: whether changing the connection limit needs a server restart, which affects when the change can be scheduled.

Ask the owner to confirm these values in writing before the pool sizes are final.

Questions teams ask before tuning the Apache Fineract connection pool

Who should tune the Fineract connection pool, the internal team or a partner?

Your own database and platform engineers should own the final settings, because they run the database, the cluster and the change process after go-live. A partner is useful when the team has not tuned a Fineract deployment before, when the tenant count and instance layout are unusual, or when COB timing needs careful testing. Ask who signs off each change and who watches the pool metrics after release. Whichever option you choose, the pool sizes must be agreed with the DBA, because the total connection count across all instances counts against the PostgreSQL limit. Fineract's documentation describes read, write and batch instance types set by environment variables, so the split of roles is part of the design, not an afterthought.

How long does Fineract connection pool tuning take?

No duration is fixed for this work, and any number quoted without a look at your environment should be treated with caution. The effort depends on how many instances you run, whether read replicas are in use, how long your Close of Business run takes, and how much load testing you can schedule. A single write instance with one tenant is a smaller task than several read instances across many tenants with a batch instance running alongside. Ask the provider to break the timeline into discovery, baseline measurement, test cycles and production change windows, and to state which of those depend on your calendar rather than on their effort.

What does the client need to provide for pool tuning?

Expect to provide access and time, not just a request. The work needs read access to the PostgreSQL server statistics, the current connection limits and the Fineract environment settings for each instance type. It also needs a non-production environment with a realistic copy of tenant data, a load generator, and agreed windows for test runs that may stress the database. Monitoring must show connection counts, wait times, slow queries and JVM metrics for each instance. Without those inputs, pool sizes are guesses. Confirm in writing who grants each access and who approves production windows, since a misjudged test can affect other tenants on the same database.

What makes the tuning effort go up or down?

The main drivers are the number of tenants, the number of instances connecting to each database, the presence of read replicas, COB duration and the interactive load during business hours. Each tenant has its own connection configuration, so more tenants mean more pools to reason about. More instances multiply the connections that PostgreSQL must accept. Read replicas add a second server with its own limit, and a long COB run can hold connections for hours on deployments with millions of accounts, as the Fineract README notes. Peak interactive traffic overlapping with COB is the case that most often needs a separate test. Each added driver increases the number of scenarios to measure.

Can a team tune HikariCP for Fineract without outside help?

Yes, a capable team can, provided it has PostgreSQL experience, a staging environment that resembles production and the discipline to change one variable at a time. The Fineract settings are exposed as FINERACT_HIKARI_* environment variables, so the mechanics are not the hard part. The hard part is measuring the effect of each change under realistic load, understanding how COB and interactive traffic compete for connections, and documenting the result so the next upgrade does not undo it. Teams without that experience often set pools too large, which can exhaust database connections and slow every tenant. Ask an outside provider to show how it would validate its settings, not only what values it would start with.

What should a quote for connection pool tuning include?

A quote should follow from a written scope and list each deliverable and the assumptions behind it. Ask for the instances and tenants in scope, the environments to be used for testing, the load scenarios including COB, and the monitoring the provider expects you to supply. It should state what is one-time, such as the baseline and the tuned configuration, and what recurs, such as re-testing after Fineract upgrades or database changes. It should name the people who approve production changes and the handover material, such as the settings file and runbook. DeployOpen does not publish a price list, and its quotes follow from this kind of written scope.

Does a Fineract upgrade change the pool settings I tuned?

It can, so plan to re-check the pool after each upgrade rather than assuming the settings still hold. Fineract releases are created quarterly under the Apache release policy, and each release may change database migrations, background jobs or the way instances use connections. A tuned value that fit one release can be too large or too small after another. Record the baseline measurements with the configuration, and repeat the COB and peak-load tests in staging before each production upgrade. Confirm with your upgrade plan which release you will target and whether a test cycle is in its scope and quote.

How DeployOpen scopes and delivers Fineract database tuning

DeployOpen treats connection pool tuning as part of the Fineract deployment, not a late-stage fix. We scope the pool settings against your instance layout, database limits and batch windows, then build, test and document them inside infrastructure you control.

How we scope

  • The written scope lists each Fineract instance type, its pool settings and the database endpoints it connects to.
  • Scope records the PostgreSQL connection limits, replica connection limits and any pooler in front of the database.
  • Scope states which Close of Business windows, batch runs and peak periods the pool must survive without timeouts.

How we build

  • We set the FINERACT_HIKARI_* values per instance type in your deployment configuration, not in ad hoc edits.
  • We load test read, write and batch paths against your tenant data volumes and record connection wait and timeout behavior.
  • We check that pool totals across all instances stay within the server connection limit, including replica connections.

How we hand over

  • You receive the final pool settings, the reasoning behind each value and the load test results that support them.
  • The handover documents the sizing formula, the change process and the signals to watch when instance counts change.
  • Your team runs a walkthrough on changing a pool size and rolling it out, so the settings can be adjusted without us.

How we support

  • Fixed-scope engagements close with written acceptance of the tuning deliverables before any retainer begins.
  • Ongoing support is available as a retainer for re-tuning after upgrades, new tenants or a change in instance count.
  • Retainer work can include periodic review of pool metrics and connection usage against the scope we agreed.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.