#4242·flyway

flyway-database-nc-mongodb hard-codes "mongosh" on the host PATH

Author: ozgliderpilotCreated May 22, 2026Updated Jul 21, 2026
Labelst: feature

Hi. I'm building flyway-mongodb extension for quarkus. The extension works end-to-end against a host-installed mongosh, but breaks the moment MongoDB is run inside a container — which is the standard Quarkus workflow.

In Quarkus, the canonical way to spin MongoDB up for tests and dev mode is Dev Services: Quarkus launches a mongo:latest container automatically, no developer action required. This is how Quarkus contributors run MongoDB tests locally and how the project's CI runs them (Quarkus PR #54308 review thread). There is no mongosh on the host, only inside the container.

flyway-database-nc-mongodb 12.0.0 hard-codes "mongosh" as the executable invoked from the JVM for .js migrations, so Flyway fails fast with mongosh: command not found. Meanwhile, a fully-working mongosh is sitting inside the container Flyway is already talking to.

java
// MongoDBDatabase.checkMongoshInstalled — line 438
final List<String> commands = Arrays.asList("mongosh", "--version");

// MongoDBDatabase.getMongoshConnectCommands — line 483
List<String> commands = new ArrayList<>(List.of("mongosh", mongoshCredential.url()));

The two unhappy workarounds today are:

  1. Install mongosh on every contributor's machine and on CI. The Quarkus maintainers pushed back on this, reasonably: adding a host-level tool dependency to every contributor's setup just to test one extension is a non-starter.
  2. Stick to .json-only migrations. Works (the API path bypasses mongosh entirely) but loses .js expressivity — control flow, helpers, the full mongosh API. I strongly believe that .js option has bigger potential as every mongoDB developer I know writes and debugs queries in js, once they are done they can just store them in VCS and move on. At the same time .json path forces them to have a step on conversion their .js to .json.

What unblocks every containerized scenario is making the executable configurable, so the embedder can point Flyway at docker exec -i <container_id> mongosh (or kubectl exec, or a wrapper script). The host PATH is left untouched, every existing user is unaffected because the default stays "mongosh".

Root cause analysis

MongoDBDatabase.checkMongoshInstalled and MongoDBDatabase.getMongoshConnectCommands both build their command list with a hard-coded "mongosh" literal. There is no config property, system property, or environment variable that overrides it. The only supported deployment shape is "mongosh installed on PATH on the same host as the JVM running Flyway."

This forces any embedder that runs MongoDB inside a container — Quarkus Dev Services, Testcontainers, Docker Compose, GitHub Actions service containers, Kubernetes — into the workarounds above. None of these scenarios are exotic, and the EXECUTABLE path is the only way to run .js migrations.

Proposed solution

The fix has two parts.

Part 1 — Configurable shell command (list)

Add a Flyway config property, e.g. flyway.mongodb.shellCommand, defaulting to ["mongosh"]. Anywhere MongoDBDatabase currently builds its command list, prepend this configured prefix instead of the bare literal:

java
// MongoDBDatabase

private List<String> shellCommand() {
    final List<String> configured = configuration.getMongodb().getShellCommand();
    return configured != null && !configured.isEmpty() ? configured : List.of("mongosh");
}

private void checkMongoshInstalled() {
    final List<String> commands = new ArrayList<>(shellCommand());
    commands.add("--version");
    final NativeConnectorsProcessRunner runner = new NativeConnectorsProcessRunner(commands, "Mongosh");
    runner.checkToolInstalled(false, "Mongosh ... " + FlywayDbWebsiteLinks.MONGOSH);
}

private List<String> getMongoshConnectCommands() {
    final List<String> commands = new ArrayList<>(shellCommand());
    commands.add(mongoshCredential.url());
    if (mongoshCredential.username() != null) {
        commands.addAll(List.of("--username", mongoshCredential.username()));
    }
    if (mongoshCredential.password() != null) {
        commands.addAll(List.of("--password", mongoshCredential.password()));
    }
    return commands;
}

Example usages:

properties
# default — unchanged behaviour, mongosh on host PATH
flyway.mongodb.shellCommand=mongosh

# Quarkus Dev Services / Testcontainers — mongosh inside the container
flyway.mongodb.shellCommand=docker,exec,-i,my-mongo-container,mongosh

# Kubernetes pod
flyway.mongodb.shellCommand=kubectl,exec,-i,mongo-0,--,mongosh

Strictly backwards-compatible: the default is ["mongosh"], and NativeConnectorsProcessRunner already accepts an arbitrary List<String> as its command.

Part 2 — Configurable shell URL

In a containerized setup the URL the JVM sees and the URL mongosh should connect to are usually different:

  • The Java MongoDB driver runs on the host and connects via the container's mapped port, e.g. mongodb://localhost:54321.
  • mongosh running inside the container needs mongodb://localhost:27017 (the container's view of itself).

Part 1 routes mongosh through docker exec, but Flyway still passes the JVM-side URL straight through to MongoshCredential, so mongosh ends up trying to connect to the host's localhost:54321 from inside the container.

A second property — flyway.mongodb.shellUrl, defaulting to flyway.url — overrides just the URL used by mongosh:

properties
flyway.url=mongodb://localhost:54321/test               # Java driver, host view
flyway.mongodb.shellUrl=mongodb://localhost:27017/test  # mongosh, container view

Only getMongoshConnectCommands reads shellUrl; everything else keeps using connectionString derived from flyway.url.

Help offered

Happy to open a PR. Let me know if you'd prefer the property names or namespace adjusted to match an existing convention in the codebase.