Building a custom Apache Fineract image with plugin JARs

A custom Fineract image lets you ship your own plugins and drivers on top of the official release, but every Fineract release then needs a rebuild, retest and redeploy.

On this page

When a custom Apache Fineract image with plugin JARs is worth building

Build a custom image only when a plugin or driver must run inside the Fineract application itself. If your deployment needs a JDBC driver other than PostgreSQL, a plugin JAR, or a licensed component that the stock container does not contain, you have to package those files with Fineract. Configuration alone will not add them. A custom image is a Dockerfile based on the apache/fineract image that copies your extra JARs into /app/libs, so the container starts with the same Fineract build plus the files you added. Before choosing this route, confirm that the capability cannot be met through the REST API, an event broker, or an external service, because each of those keeps Fineract's image unchanged.

The official Docker image ships only the PostgreSQL JDBC driver, and the Fineract README names PostgreSQL as the recommended database, with MariaDB and MySQL deprecated under FSIP-9. Any other driver or plugin is therefore your responsibility from the first build. Once you own the image, you also own its base image choice, the provenance and version of every added JAR, its license review, vulnerability scanning, and the container registry where it is stored. Ask your vendor or internal team to confirm which Fineract tag each plugin was tested against, because a JAR compiled for one release can fail on another.

The README describes official Fineract releases as quarterly, created at the end of each quarter under the ASF release policy, with end-of-life releases archived. That rhythm means a custom image is not a one-time artifact. Each new release requires rebuilding your image on the new base, retesting the plugins, and redeploying through your upgrade process. Plan a recurring owner and a test environment for that cycle before the first build, and agree in writing who handles rebuilds when a plugin lags behind a Fineract release. If the plugin cannot be maintained on that cadence, reconsider the design before committing to it.

Packaging options and when each fits

This table compares four ways to package Fineract with additional libraries, from the unmodified official image to running a JAR or WAR with external libraries. Read each row against your own plugin count, upgrade cadence and supply chain requirements. The main takeaway is that every extra JAR you add moves a rebuild and a retest onto your team with each Fineract release.

OptionMaintenance and upgrade effortSupply chain control and rollbackWhen it fits
Unmodified official image, no extra JARsLowest. You change the image tag and retest the platform itself.Images come from the upstream project. Rollback means redeploying the previous tag.Standard deployments that need no plugins or drivers beyond the bundled PostgreSQL JDBC driver.
Custom image built on apache/fineract with JARs in /app/libsModerate. Each Fineract release needs a rebuild and a check that every added JAR still loads.You own the build pipeline, base image pinning and JAR provenance. Rollback is a retag of your last good image.Plugins or extra libraries are required and you want a reproducible, versioned artifact per release.
Custom image that also bakes in additional JDBC driversModerate to high. Driver versions must match the database platform and be retested on each database upgrade.Adds third-party driver binaries to your scope. Confirm licenses and checksums before you accept them.A database platform beyond the bundled PostgreSQL driver is approved for your deployment. Confirm project support first.
Running the JAR or WAR with external libraries on the hostHighest. Libraries are managed outside the image, so drift between hosts is likely.Weakest traceability unless you record hashes for every file. Rollback depends on host state.Legacy or constrained environments where container builds are not available. Confirm the chosen deployment type with the project documentation.

Build sequence for a custom Fineract image

This sequence takes the official apache/fineract base image to a tested custom image that carries your plugin and driver JARs. Each step closes with a check, so a fault is caught on a build machine or a non-production tenant database before the image reaches a shared environment. The output is a tagged, digest-recorded image that loads your extensions and reports healthy.

  1. Pin the base image to a release

    The platform engineer sets the FROM line in the Dockerfile to an exact apache/fineract tag that matches the release your team has agreed to run, such as 1.15.0, which the Fineract website lists as the current stable release. Avoid latest or floating tags, because an unannounced base change would alter the core application under your extensions. Check: pull the tag, record its digest in the build notes, and confirm the digest is identical on a second pull.

  2. Collect plugin and driver JARs with checksums

    The engineer gathers each plugin and any JDBC driver the deployment needs, noting the version and the source for every file. The official image ships only the PostgreSQL JDBC driver, so any other driver must be added here. Record a SHA-256 checksum for each JAR and store the list in version control beside the Dockerfile. Check: recompute the checksums on the build machine and confirm each one matches the recorded value before any file enters the build context.

  3. Copy verified JARs into the image

    The engineer adds COPY instructions that place the verified JARs in /app/libs and confirms that the file ownership and permissions let the runtime user read them. Keep the copy steps in the Dockerfile so every rebuild repeats the same actions. Check: start a throwaway container from the built image, list /app/libs, and confirm that every expected file is present with the expected name, size and permissions.

  4. Set loader.path to the plugin directory

    The engineer points the loader.path setting at the directory that holds the JARs, using the configuration mechanism that the documentation for the pinned release describes, either an environment variable or a properties file. Confirm the exact key name, value format and default behavior in the documentation before writing it, because the loader reads only what the documented key declares. Check: start the container with the setting and confirm that the startup log shows the loader scanning the expected path.

  5. Build and tag the image

    The engineer builds the image from a clean build context, applies a tag that combines your registry name, the Fineract release and a build identifier, and pushes it to a registry your organization controls. Never overwrite a published tag, because the tag is the reference used for rollback. Check: run an image inspection and confirm that the tag, digest, labels and layer list match the build log, then record the digest in the release record.

  6. Run against a non-production tenant database

    The engineer starts the image against a non-production tenants database with a disposable tenant schema. Connection details arrive through environment variables or a secrets mechanism, never baked into the image. Expect Liquibase migrations to run on the instance that handles writes, so the test database should be one that can be rebuilt if a migration fails. Check: the container starts without database errors and the migration output completes with no failed changesets.

  7. Verify plugin loading and health

    The engineer confirms that the plugin loads by reviewing the startup log and by exercising the plugin's behavior through the API, then calls the actuator health endpoint. Confirm the endpoint path in the documentation for the pinned release. Check: the loader log names each expected plugin, the plugin behavior works as specified in your test script, and the health endpoint reports a healthy status. Save the logs and health output with the build record as evidence.

Security and supply chain considerations for custom JARs

A custom image bundles code you did not write into a banking platform, so every plugin and driver JAR becomes part of your trust boundary. The cards below cover where each JAR came from, what license it carries, how it is scanned, how the image is signed and stored, and what default credentials must be gone before go-live.

Confirm JAR provenance

Record the source, version and checksum of every plugin and driver JAR before it enters the build context. Prefer artifacts from the upstream project's official release channel or from your own internal build, and reject files copied from forums or unverified mirrors. Ask the plugin author whether the JAR is built from a tagged source revision and whether a build provenance statement is published. Store that evidence next to the Dockerfile so an auditor can trace each binary to its origin.

Review licenses per JAR

Fineract itself is licensed under the Apache License 2.0 (SPDX: Apache-2.0), but a plugin or JDBC driver may carry a different license with different redistribution terms. Check each JAR's license file and its transitive dependencies before you publish an image that includes it. Record the result in a bill of materials so that legal and procurement can approve the combined distribution. Confirm with counsel whether any copyleft terms apply to your deployment model.

Scan dependencies and images

Run software composition analysis on the plugin JARs and on the final container image, not only on your own code. Scan again whenever you rebuild on a new Fineract release, because the base image and transitive libraries change. Decide in advance which severities block a release and who can grant an exception. Keep the scan reports as release evidence, since they show what was known at the time of each build.

Sign and store images privately

Push the built image to a private registry that your organization controls, and tag it with an immutable digest as well as a readable version. Sign the image and verify the signature in the deployment pipeline before the Kubernetes or Docker host pulls it. Limit who can push to the production repository and log every pull and push. Confirm with your registry vendor which signing and attestation features it supports and how keys are rotated.

Remove the default login

The Fineract README documents a default development login, mifos with the password listed in the project's examples, and states it must be changed for any real deployment. Remove or replace that account in every environment before go-live, including staging copies that hold real data. Set new credentials through your secrets store rather than in the Dockerfile or image layers. Verify the change with a login test that fails if the default password is still accepted.

Keep secrets out of layers

Plugin configuration often carries database passwords, API keys and tenant credentials. Anything written with ARG, ENV or COPY during the build can persist in image layers that anyone with pull access can read. Pass secrets at runtime from your orchestrator or secrets manager, and scan the finished image for accidental credential strings. Confirm that your build runner does not echo secrets into logs.

Rebuilding the image on every Fineract release

A custom image is only as current as its base tag, so plan to rebuild it on every new Fineract release. The Fineract README states that official releases are created quarterly, at the end of each quarter, under the ASF release policy, and that end-of-life releases are archived. That means a FROM apache/fineract line pinned to an old tag will eventually sit on a release that no longer receives maintenance. The current stable release listed on the project website is 1.15.0, but the number that matters for your image is the tag you build from next quarter, so confirm the release calendar and archive status with the project before you schedule the work.

Treat each rebuild as a release of your own. Rebuild the image from the new base tag with your plugin and driver JARs in /app/libs, then run the full regression suite against a copy of production-shaped data, including your custom endpoints and the JDBC driver you added. Check that the plugin JARs still match the new Fineract internals and that loader.path still resolves them. Roll out in a fixed order: apply Liquibase migrations through the write instance first, because the documentation says migrations run only on write instances and can be run separately with the liquibase-only Spring profile, then move read and batch instances onto the new image. Confirm the migration ordering with a rollback plan for each step, since a database change cannot be undone by redeploying an older image.

Limits and what to confirm in your environment

A custom image inherits assumptions from the Fineract release, the plugin vendor and your runtime. The cards below cover the items to verify before the first build and again on every upgrade, since each one can break the image silently. Check each item with the project or vendor rather than assume it holds.

Plugin JAR dependencies

Each plugin JAR may bring its own transitive libraries, and those can clash with the libraries Fineract already ships. Ask the plugin vendor for the complete dependency list and the versions it was built against. Confirm there is no duplicate or conflicting class path entry in the final image, and record the list in your build repository so a reviewer can compare it between releases.

Plugin compatibility with the Fineract version

A plugin built for one Fineract 1.x release may fail or misbehave on another, because internal Spring beans, APIs and database changes move between releases. Confirm in writing which Fineract versions the plugin supports. Treat a Fineract release as a new compatibility question, not a routine rebuild, and test the plugin against the exact tag you plan to run.

How loader.path behaves in your image

Confirm that the loader path setting picks up JARs from /app/libs in the exact image you run, including the base image tag and any entrypoint override. Start the container and check the startup log for each plugin class being loaded. A JAR that sits in the directory but is not on the loader path will not fail the build, so verify it at runtime.

JAR or legacy WAR deployment

The plugin approach assumes the Spring Boot JAR packaging. If your environment still runs a WAR on an external Tomcat, the image layout and loader behavior do not apply in the same way. Confirm which packaging your deployment uses before you write the Dockerfile, and plan the migration separately if you run the WAR today.

Tenant database driver

The official image ships only the PostgreSQL JDBC driver. If any tenant database uses a different engine, or a PostgreSQL driver version your security team has not approved, you must add that driver deliberately. Confirm the driver coordinates, the version and its license, then test a tenant connection in a staging environment before promoting the image.

Rebuild and approval ownership

Someone must own the rebuild every time Fineract publishes a release, which the project does quarterly. Confirm who reviews the new base image, re-runs plugin compatibility checks, and approves the image for production. Without a named owner and a written release checklist, custom images drift behind the maintained platform.

Questions teams ask before building a custom Fineract image

How much effort does a custom Fineract image take to build?

The Dockerfile itself is usually small, so effort comes from what surrounds it: confirming which plugin and driver JARs you need, checking each one against the Fineract version you run, building a test run against a copy of your tenant data, and writing the release steps your operations team will follow. The Fineract README warns that a production deployment can be complex, costly and time-consuming, and that is the baseline for the whole platform, not only the image. Ask any partner to split their estimate into discovery, build, test and handover so you can see where the time goes.

What drives the cost of a custom image up?

The main drivers are the number of plugins or custom JARs, whether their source is maintained or abandoned, how many of them touch core Fineract classes, and how many environments must receive each build. Plugins that need compatibility work after every quarterly release add recurring effort. Compliance evidence requirements, such as keeping a record of each JAR's origin and checksum, also add work. Ask for a quote that lists each JAR, its owner, its expected update frequency and the environments it reaches.

What does the work need from our side?

You need to provide the list of plugins and drivers with their sources and licenses, the target Fineract version, your container registry and CI system, and access to a non-production environment with a representative tenants database. Someone who owns the release decision must be named, because each rebuild changes what runs in production. Your security team should review the JAR inventory and how images are signed and stored. Without these inputs, the first build tends to stall on access and approvals.

Can our in-house team build and maintain the image without a partner?

Yes, if your team already runs Java services in containers and can read Fineract's Docker documentation and source. The skills that matter are Dockerfile hygiene, Maven or Gradle dependency handling, PostgreSQL configuration and a tested rollback procedure. Teams with no Fineract experience usually need time to learn the Spring Boot packaging and the loader path behavior before they trust a build. Decide early who will be the second person able to run a release, because a single maintainer is a continuity risk.

How long does a rebuild cycle take after a new Fineract release?

The cycle length depends on how many JARs must be rechecked, how much regression testing your banking workflows need and how fast your change approval runs. The Fineract README says official releases are created quarterly, at the end of each quarter, so you should plan for at least one planned rebuild per quarter plus any emergency fixes. Ask your team or partner to publish their own cycle with named steps, rather than accepting a generic duration.

What breaks most often when we rebuild the image?

The most common failures are a plugin JAR built against an older Fineract API, a JDBC driver version that conflicts with the one the image already ships, and a loader.path setting that points to the wrong directory after a base image change. The official Docker image ships only the PostgreSQL JDBC driver, so any other driver must be added deliberately. Schema changes are another risk, because Liquibase migrations run on write instances, so a failed upgrade needs a tested recovery plan before the new image is promoted.

When should we hire a partner instead of building the image ourselves?

Consider a partner when you have several custom JARs, a compliance requirement to document each component, no team member who can own the quarterly cycle, or a go-live date that leaves little room for learning. A partner is also useful when the image must run in an air-gapped or private network where mirroring and dependency control add work. DeployOpen works on fixed-scope engagements agreed in writing before work starts, and ongoing upgrades can be covered by a retainer. Ask for the written scope first, and confirm the license of each plugin with its owner.

How DeployOpen scopes and delivers custom Fineract images

DeployOpen plans, builds, tests, documents and supports custom Fineract images inside your own infrastructure. The work starts with a written scope that names each plugin and driver JAR, the base Fineract release and the environments the image must run in.

How we scope

  • Inventory every plugin and driver JAR, with its version, source, license and the Fineract release it was built against.
  • Agree the base apache/fineract tag, the registry where images are stored and the environments that will pull them.
  • Confirm which JARs go in /app/libs and which need loader.path configuration, and record the reason for each.
  • Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts.

How we build

  • Write a Dockerfile based on the apache/fineract image that adds only the agreed JARs and no unrelated artifacts.
  • Verify that each JAR loads at startup and that the PostgreSQL driver remains available alongside any added drivers.
  • Test the image against a non-production tenant database, including Liquibase migrations and a full application start.
  • Pin every base image and dependency by version or digest so rebuilds are reproducible and traceable.

How we hand over

  • Deliver the Dockerfile, build pipeline definition, JAR manifest and a runbook for rebuilding on each Fineract release.
  • Document the test cases used to confirm the image, with the results from your environment.
  • Walk your platform and security teams through the image contents, configuration keys and rollback steps.
  • Provide a checklist for repeating the rebuild when a new quarterly Fineract release is adopted.

How we support

  • Ongoing support, upgrades and continuous testing are available as retainers, scoped in writing.
  • Retainers can cover rebuilding the image for each new Fineract release and retesting every added JAR.
  • Confirm with your team who approves a new plugin version and who signs off the production image tag.
  • Ask any provider how plugin JARs are checked for license terms and known vulnerabilities before they are added.

Sources and further reading

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.