Apache Fineract MySQL or MariaDB to PostgreSQL migration

Moving a Fineract tenant database from MySQL or MariaDB to PostgreSQL is a data migration, not a version upgrade, and the project provides no official tooling for it. This page covers how to plan schema and data transfer, check UTC dates and reconcile balances before cutover.

On this page

How to plan a Fineract MariaDB to PostgreSQL migration

This is a planned data migration between two database engines, not a configuration change. The supported target is a currently supported PostgreSQL major version (14 through 18 at the time of writing, with 18 the newest), running with a Fineract release the project still maintains, such as the current 1.15.0 stable release. Fineract's README recommends PostgreSQL and places MariaDB and MySQL under deprecation through FSIP-9, so the destination is PostgreSQL and the source is the tenant you already run. Confirm the PostgreSQL major version you will target against the PostgreSQL versioning policy before you fix the plan, because support end dates determine how long the result will be patched.

The scope is one tenant database at a time. Fineract is multi-tenant, with tenant connections described in a tenants database and requests identified by the Fineract-Platform-TenantId header, so each tenant has its own schema and its own migration window. Do not hand-write the target schema. Create an empty PostgreSQL database, let Fineract's own Liquibase changes build the schema on that target, and then transfer data into it. The project provides no official migration tool between engines, so the transfer method, its ordering of tables and its handling of foreign keys are your decisions and must be tested on a copy of production data.

Plan the work in three phases: schema build, data transfer and reconciliation. Reconciliation is where most of the risk sits. Before the cutover, agree which row counts, loan and savings balances, ledger totals and client records must match between the two databases, and record the check that proves each one. Pay particular attention to date and time values, since Fineract's UTC date handling is a known source of off-by-one differences after data moves between systems. Ask your implementation partner or internal team to document the rollback position, the freeze window for writes and the owner who signs off each reconciliation result.

What changes when Fineract leaves MySQL or MariaDB

This table compares what changes for a Fineract tenant database moving off MySQL or MariaDB, covering project direction, image support, PostgreSQL support windows and authentication. Read each row as a decision input: the move is driven by the project's database standard, and the PostgreSQL-to-PostgreSQL upgrade tools do not apply to it.

ChangeImpactAction needed
Project recommendation under FSIP-9PostgreSQL is the recommended Fineract database, and MariaDB and MySQL are deprecated under FSIP-9.Confirm the target Fineract release still documents MySQL or MariaDB support before deciding on timing.
Official Docker image driverThe official image ships only the PostgreSQL JDBC driver, so a MySQL or MariaDB tenant cannot run on it unchanged.Plan the PostgreSQL target and build or test any custom image that still needs another driver.
PostgreSQL 18 support windowSupported until November 14, 2030, per the PostgreSQL versioning policy.Choose the target major version against this window and your own patch cadence.
PostgreSQL 17 support windowSupported until November 8, 2029, per the PostgreSQL versioning policy.Confirm whether your platform team can standardize on this version.
PostgreSQL 16 and 15 support windowsPostgreSQL 16 is supported until November 9, 2028 and PostgreSQL 15 until November 11, 2027.Check that the chosen version is still inside its window at go-live.
PostgreSQL 14 support windowSupported until November 12, 2026, so it is the nearest end date in the supported list.Avoid starting a new target on this version without a planned upgrade.
SCRAM replaces MD5 in PostgreSQL 18PostgreSQL 18 deprecates MD5 password authentication in favor of SCRAM.Configure SCRAM for the Fineract database role and confirm application drivers support it.
pg_upgrade and logical replication scopeThese move data between PostgreSQL major versions, not from MySQL or MariaDB, so they do not apply to this move.Plan a separate schema and data transfer from the source database into PostgreSQL.

Why no official tool moves the data for you

The Apache Fineract project does not provide official tooling to move a tenant database from MySQL or MariaDB to PostgreSQL, so the engineering team must design, build and test its own transfer. Treat any community script or blog procedure as untrusted until you have confirmed it against the Fineract release you plan to run and checked current project guidance, including the FSIP-9 record that standardizes on PostgreSQL. The project's position is that PostgreSQL is the recommended database and MariaDB and MySQL are deprecated, which means the move is your responsibility from schema creation through reconciliation.

Your team owns the translation layer that a vendor tool would otherwise provide. That covers type mapping between the two engines, including integer widths, TEXT and VARCHAR lengths, BLOB columns and timestamp types; character set and collation choices, so that accented client names and free-text remarks survive the transfer unchanged; sequences, because MySQL auto-increment counters do not carry over automatically and PostgreSQL sequences must be reset above the highest migrated key; and boolean and decimal handling, where MySQL TINYINT(1) flags and DECIMAL scales must map exactly to PostgreSQL BOOLEAN and NUMERIC values or loan and savings balances will drift. Also plan the tenants database entries that point each tenant at its connection, since a tenant migrated but still referenced at the old host will fail or write to the wrong place.

Confirm each of these points against the exact Fineract release in your test environment, not against general database documentation. Run the transfer at least once in a non-production copy, then compare row counts, control totals for balances, and a sample of loan and savings schedules between source and target. Record each mapping decision and its test evidence, because the reconciliation output becomes the basis for your sign-off and for any later audit question about how the data was moved.

Four decisions to settle before any data moves

Four choices shape everything after them: the PostgreSQL version, how data moves, when the platform stops, and which tenants go first. Each one changes the test plan, the risk and the people who must agree before the cutover date is set.

Target PostgreSQL major version

Pick a major version that is still supported for the whole planned production life, not just the first months after go-live. Check the published end-of-support dates against your planned go-live and your retention horizon, and avoid a version whose support ends soon after launch. This choice sets the Docker image, the managed or self-run service you build on, and the minor release cadence your team must follow. Sign-off: the CIO or head of platform engineering, with the database owner and the security lead confirming authentication settings.

Transfer method and data movement

Choose between a one-time dump and load during a full freeze, or a team-built sync that keeps the target close to current before cutover. The dump approach is simpler to test and reason about, but its downtime grows with data volume. A sync reduces the freeze but adds a component your team must build, monitor and validate, including type mapping between MySQL or MariaDB and PostgreSQL. Sign-off: the Fineract application owner and the data governance lead, after a rehearsal shows the measured freeze length.

Cutover window and batch freeze rules

Define the window in which no Close of Business Day or other batch job runs, and agree who can stop the scheduler and how you confirm it is idle. The README notes that Close of Business Day can run for several hours on large deployments, so a batch that starts during the copy will leave the two databases out of step. Write freeze and thaw steps, a go or no-go checklist and a rollback point. Sign-off: operations lead, the business owner for end-of-day accounting, and the risk function that accepts the freeze.

Tenant scope and migration waves

Decide whether all tenants move in one cutover or in waves, one tenant per wave. Waves limit the blast radius and let you learn from the first tenant, but they require the tenant registry and connection settings to point to the right database for each tenant during the transition. Each tenant also has its own accounting and reconciliation totals, so waves multiply the number of reconciliations. Sign-off: the product owner for each tenant, plus the platform owner who manages the tenants database.

What to prepare before the first test load

Before any data moves, the source and target need recovery points, a complete picture of what reads the MariaDB schema, a test environment that matches production, and locked-down access on PostgreSQL. Each item feeds the next, so a gap in one shows up as a failed reconciliation later.

Backups and point-in-time recovery

Take a verified backup of the MariaDB or MySQL tenant databases and the tenants database before the first load, and confirm you can restore each one to a separate host. On the PostgreSQL target, configure continuous archiving so you can recover to a chosen moment during a failed load. Check that restores are tested, not just that backup jobs report success. Ask your team who holds the restore keys and how long a restore takes at production size.

Inventory of schema readers

List every custom SQL query, scheduled report, reporting view, data warehouse extract, custom Java code and external tool that connects to the MariaDB schema. Many of these use MySQL-specific syntax, functions or collation behavior that will not run unchanged on PostgreSQL. Record the owner, the connection account and the frequency of each reader. Anything missing from this list will break at cutover, so treat the inventory as a sign-off item.

Test environment at production scale

Build a non-production PostgreSQL environment with the same number of tenants, a comparable account count and a similar data volume as production. A small sample will hide slow migrations, long Close of Business runs and query plans that change with real row counts. Use the same Fineract release and configuration as the target so that test results carry over. Agree in advance who signs off the test results and which reconciliation checks must pass.

Target access controls

Create dedicated PostgreSQL roles for the Fineract application, the migration job and read-only reporting, with the minimum privileges each needs. Set password_encryption to scram-sha-256 so new passwords are stored with SCRAM, and do not allow MD5 password authentication. Enable TLS on the database connection and configure the matching SSL settings in Fineract through its environment variables, such as FINERACT_SERVER_SSL_ENABLED. Review who can reach the database port from the network before loading any data. Pooling with PgBouncer is an option to plan for connection handling, not a requirement.

Migration sequence with checks and rollback points

This sequence moves a Fineract tenant database from MariaDB or MySQL to PostgreSQL in an order that keeps the source intact until the cutover decision, so every step has a verified exit point and a way back.

  1. Back up and verify the MariaDB source

    Take a full logical backup of every tenant database and the tenants database, plus the server configuration and the Fineract environment variables, and store copies outside the source host. Restore the backup into a scratch MariaDB instance and confirm that the schema loads, the Liquibase changelog table is present and row counts match the source. This restore test is the first rollback point: the source stays read-only from this point onward and remains unchanged until the cutover decision. Record the backup checksums and the restore results in the migration log.

  2. Build the PostgreSQL target with Liquibase

    Create empty PostgreSQL databases for the tenants database and each tenant, then start Fineract against them so its Liquibase migrations build the schema the running release expects. Where the deployment supports it, run a liquibase-only job so the schema is created before any application instance starts. Compare the resulting table list and Liquibase changelog against the release documentation. The step is complete when the empty target schema matches the Fineract version that will run in production, and no application data has been loaded yet.

  3. Freeze writes and let batch jobs finish

    Announce a write freeze to operations and stop staff and integration traffic at the gateway or load balancer. Confirm on the batch instance that no Close of Business Day job is running, and wait for any active job to finish, since the README notes that this job can run for several hours on large deployments. Check the job execution history and the business date. The step closes when no batch job is in progress and the write instance accepts no new requests, which gives a consistent point for the extract.

  4. Extract and transfer data with type mapping

    Export data from the frozen MariaDB source and load it into PostgreSQL with an explicit type map. Review how date and timestamp columns, boolean flags, decimal precision for monetary amounts, text collation and auto-increment keys are converted. Load reference and configuration tables before transactional tables so foreign keys resolve. Confirm that date values are interpreted in UTC on both sides, because Fineract's UTC date handling must match between source and target. The step is complete when each table has been loaded without errors and the load log shows no skipped or truncated rows.

  5. Reconcile counts, keys and balances

    Compare row counts for every table, check primary and foreign key integrity on the target, and confirm that identifiers for clients, loans, savings accounts and journal entries match between systems. Reconcile loan principal, outstanding balances, savings balances and general ledger totals by office and currency against the source report. Record any difference, trace it to a cause and fix it in the transfer, then rerun the affected tables. The step is complete only when every reconciliation check passes with zero unexplained differences, and the signed reconciliation report is filed.

  6. Run functional tests on all instance types

    Start the write instance, the batch instance and the read instances against the PostgreSQL target, and run a scripted test pass. Cover login, client creation, loan disbursement and repayment, savings posting, a Close of Business Day run in a non-production copy, and reports read from the read replicas. Confirm that Liquibase runs only on the write instance and that the batch instance behaves as documented. The step is complete when every scripted test passes and the results are reviewed by the business owner, with defects logged and closed.

  7. Switch tenants database and tenant connections

    At the agreed cutover window, point the tenants database and each tenant connection entry to the PostgreSQL target, using the tenant connection settings for the release in use. Restart the instances in the documented order and send a small set of controlled transactions through the gateway. Confirm that the Fineract-Platform-TenantId header resolves to the correct PostgreSQL database for each tenant. Keep the MariaDB source unchanged and reachable in read-only mode until the monitoring period has passed. The step is complete when each tenant returns correct data and the rollback decision is recorded.

  8. Monitor and retire the source

    Watch error logs, connection pool usage, query latency, batch job outcomes and reconciliation spot checks on the new platform for an agreed period set by your operations and risk owners. Compare the first scheduled Close of Business Day run on PostgreSQL with the prior run's expected results. Only after the monitoring period closes without unresolved defects should the MariaDB source be decommissioned, and the final backup retained under your retention policy. The step is complete when the sign-off record names the owner and the retention decision for the source.

Checking UTC dates and reconciling balances after the move

Dates and timestamps are the most common silent failure in a database engine change, because a load can succeed, row counts can match, and a loan still lands on the wrong business day. MySQL and MariaDB, PostgreSQL and the JDBC drivers each have their own rules for storing and returning DATE, DATETIME and TIMESTAMP values, including how fractional seconds, time zone offsets and daylight saving transitions are handled. Before the first test load, inventory every date and timestamp column in the Fineract schema, record its source type, its target type and the Java type the application reads it into, and flag any column that the source stored in session or server time zone rather than UTC. Confirm the session time zone of the source database, the JDBC URL settings used by the application instances and the server time zone of the target, because all three can shift values silently. The date handling in Fineract 1.8.0 and later is the place to start, and you should confirm the exact behavior of your release against the Apache Fineract platform documentation rather than relying on memory of earlier versions.

Reconciliation proves that the business meaning survived the move, not only that rows were copied. Write the test cases and the sign-off owner down before the load, and keep the document under change control. At minimum, compare row counts per table, control totals for loan principal, savings balances and journal entry debits and credits per currency and office, and the number of accounts with a non-zero balance. Then sample complete account histories for a set of loans, savings accounts and fixed deposits chosen to cover each product, each interest method, a backdated transaction, a reversal and an account closed near a period end. For each sample, compare the transaction dates, value dates, posting dates and running balances in the source and target. Include a check that accounting entries fall on the same business dates as the transactions that produced them, because a one-day shift can move a month-end balance across a period boundary.

Ask the owner of the business calendar and the finance team to approve the reconciliation pack before cutover, and state in the sign-off who may accept a variance and what size of variance requires a rerun. Any difference in a date, however small, should be treated as a defect until its cause is known, since a shift of a few hours in a timestamp can change which business day a transaction belongs to. Keep the source database available in read-only form until the reconciliation is accepted, so that a disputed value can be re-read from the original rather than reconstructed. Ask your implementation partner to show the queries used for every check and to rerun them on the production load, not only on a test copy.

Choosing a cutover method by downtime tolerance

A one-time dump and load is the simplest cutover. The business stops writes to the MySQL or MariaDB tenant database, the team exports the data, transforms it for the PostgreSQL schema, loads it, and starts Fineract against the new database. The downtime equals the freeze window, so the work is mostly planning the window and rehearsing it until the duration is known. Fineract's own schema on the target is created by the platform's migration process, so the team should confirm that the target is built from the same release as the source before loading data.

  • Who does the work: The client's database and platform team runs the export and load, with the implementation partner or application owners validating the Fineract side.
  • What must be proven in testing: Full rehearsal on a copy of production data, timed end to end, with the freeze duration measured rather than estimated.
  • Main risk: The rehearsal runs against a smaller or older copy, so the production window overruns and the rollback path is never exercised.

Before committing to this method, confirm with the business the longest stop in posting and repayments it will accept, and check whether the timed rehearsal fits inside it.

What makes a database migration take longer

This table lists the factors that stretch a Fineract MySQL or MariaDB to PostgreSQL migration and explains how each one changes the work. Use it to decide which drivers apply to your deployment before asking a partner or your own team for a plan. The main takeaway is that data volume, account count and reconciliation depth usually matter more than the schema move itself.

DriverEffect on effort
Number of tenants and total data volumeEach tenant database is moved and verified separately, so more tenants and larger tables mean more dump, load and comparison runs. Ask how many tenant databases exist and their combined size.
Number of accountsThe Close of Business Day job can run for several hours on deployments with millions of accounts, so rehearsal runs and the cutover timing both grow with account count.
Custom SQL, reports and direct database readersAny report, extract or script that queries MySQL or MariaDB directly must be rewritten and retested against PostgreSQL, and each one adds review work.
Custom Fineract modules or core editsCustom code that depends on database behavior or edits core sources must be ported and regression tested, which can take longer than the data move itself.
Integrations using event publishing or read replicasConsumers of external business events or read replicas must be reconnected, reconfigured and checked for ordering and lag after the switch.
Freeze window toleranceA short, fixed write freeze limits how much final delta sync can happen at cutover, so a tight window usually means more rehearsals and a more tested cutover plan.
Depth of reconciliation required by auditors or financeRow counts alone rarely satisfy finance. Balance, journal and loan schedule comparisons across every tenant add time, and the depth should be agreed with auditors before the plan is set.

Questions buyers ask before moving Fineract to PostgreSQL

What drives the cost of moving Fineract from MariaDB to PostgreSQL?

The cost follows the size and state of the tenant databases, the number of tenants, the number of integrations that read or write the database directly, and how much testing the finance team needs. Most of the spend is labor: schema comparison, a repeatable transfer script, test cycles on copies of production data, a cutover rehearsal and a final reconciliation. Infrastructure for a parallel PostgreSQL environment is a smaller, recurring cost while both stacks run. A quote should list each phase separately, the environments it assumes, how many rehearsal cycles are included, who signs off the reconciliation report, and what happens if a rehearsal fails. Ask for the assumptions in writing, because a fixed-scope quote is only as accurate as the data and freeze window it was based on.

How long does a Fineract database migration to PostgreSQL take?

Duration depends on three things: the data volume across all tenants, the number of tenants that must move together, and how long the business can tolerate a write freeze at cutover. Larger databases take longer to export, load, index and reconcile, and each tenant adds its own verification pass. A low freeze tolerance pushes the team toward more rehearsals and a tighter cutover runbook. Ask for a timeline that separates discovery, build, rehearsal and cutover, and that states which assumptions about volume and freeze window it depends on. Any figure you receive should be tied to those inputs, and you should confirm them against your own database sizes before accepting a schedule.

What must our internal team provide for a Fineract PostgreSQL migration?

Three groups of people are needed before work starts. Schema owners must confirm which tables, custom columns and database objects belong to Fineract core and which were added by your team, and they must approve the mapping. Test users must run representative loan, savings, accounting and close-of-business scenarios on a restored copy and record what they expect to see. Finance must sign off the reconciliation: balances, journal totals, loan schedules and savings positions per tenant, compared against the source database. Your team should also name a decision owner for go or no-go at cutover, provide access to the current and target environments, and document every integration that connects to the database so that connection strings, drivers and SQL dialects are updated together.

Can an experienced internal team move Fineract to PostgreSQL without outside help?

Yes, an experienced team can do this, provided it has people who understand Fineract's Liquibase-managed schema, the PostgreSQL behavior that differs from MySQL and MariaDB, and the finance reconciliation. The work is less about any single command and more about discipline: building a transfer process, proving it on copies, documenting each deviation and rehearsing cutover until the runbook is routine. Consider whether the team has spare capacity for several rehearsal cycles, whether it can test the application against the new database, and whether anyone has moved a production database before. If those gaps exist, a partner can fill them in a fixed-scope engagement, and you can compare that quote with the cost of the internal time it replaces.

Does the Apache Fineract project provide a migration tool for MariaDB to PostgreSQL?

The project does not provide official migration tooling for moving a tenant database from MySQL or MariaDB to PostgreSQL. Its position, set out in FSIP-9, is that PostgreSQL is the recommended database and MariaDB and MySQL are deprecated, so the transfer is left to each operator. Your team, or a partner, must build the export and load process, handle type and collation differences, and verify the result. Treat any script as code that needs its own review and tests, and keep the source database intact until reconciliation is complete. The FSIP-9 proposal is a useful starting point for understanding the project's direction before you design the plan.

Which PostgreSQL version should we target for Fineract, and how long is it supported?

Target a PostgreSQL version that is still in its support window for the whole period you expect to run it. According to the PostgreSQL versioning policy, supported versions are 18, with final release November 14, 2030, 17 to November 8, 2029, 16 to November 9, 2028, 15 to November 11, 2027, and 14 to November 12, 2026. Versions 13 and earlier are unsupported. Version 18 was first released on September 25, 2025, so it gives the longest runway, but confirm that your Fineract release and its drivers and tooling support the version you choose before you commit. Plan to apply minor releases, which keep the storage format unchanged, and schedule future major upgrades early enough to stay supported.

How DeployOpen works on this

DeployOpen plans, builds, tests and documents Fineract database moves inside infrastructure you control, starting from a written scope agreed before work begins. The work covers the tenant databases, the Fineract configuration that depends on the database, and the reconciliation evidence your finance and operations teams need to sign off the cutover.

How we scope

  • Inventory every tenant database, its MySQL or MariaDB version, and the Fineract release it runs on.
  • Confirm the target PostgreSQL major version against the supported versions list before design starts.
  • Agree in writing which tenants, reports, date checks and reconciliation outputs are in the fixed scope.

How we build

  • Design the schema and data transfer path, then rehearse it on copies of production tenant databases.
  • Verify UTC date handling and reconcile balances, accounts and journal totals between source and target.
  • Test Fineract startup, Liquibase migration state and key product, accounting and end-of-day workflows on the new database.

How we hand over

  • Deliver the runbook, cutover steps, rollback plan and reconciliation reports for your own team to repeat.
  • Document configuration changes, database connection settings and monitoring checks for the target environment.
  • Walk your operations and finance staff through the signed-off evidence before the source database is retired.

How we support

  • Offer ongoing support, upgrades and testing as a retainer after the migration is complete.
  • Plan follow-on work for PostgreSQL minor releases and later Fineract upgrades when your scope requires it.
  • Ask any partner for a quote built from the written scope, since DeployOpen does not publish a price list.

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.