Embedded MongoDB Only in IDE using Spring Boot 3.x

If you have a microservice using MongoDB, you may have a Docker Compose file set up to start your microservice and MongoDB at the same time, in separate Docker containers. But sometimes you'd like to quickly run the application within your IDE without the need to go spin up an instance of MongoDB manually. This is possible using Spring Boot and profiles, although as usual there are lots of gotchas (especially with Spring Boot 3.x) that you'll never find in the books.

Updating the POM

You've no doubt already enabled the spring-boot-starter-data-mongodb dependency. (These examples use Maven with its pom.xml file.)

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

Next you would normally just include the third-party Embedded Mongo library using the latest de.flapdoodle.embed:de.flapdoodle.embed.mongo dependency. Except from Spring Boot 2.7.0 onward, Spring Boot no longer supports auto-configuration of embedded Mongo. So instead you can use the embedded MongoDB Spring 3.x integration from the same third party. You would include it like this, specifying the latest version:

<dependency>
  <groupId>de.flapdoodle.embed</groupId>
  <artifactId>de.flapdoodle.embed.mongo.spring30x</artifactId>
  <version>4.6.2</version>
  <optional>true</optional>
</dependency>

I specified <optional>true</optional> so that embedded MongoDB support won't get pulled in transitively by other libraries that use this one. We'll need to do more to exclude the libraries from this microservice JAR, though; I explain that later.

Note that many tutorials and books indicate <scope>test</scope> test should be used, and that's correct—if you're only wanting to use embedded MongoDB in tests. However the goal here is to use MongoDB in normal execution of the application—but only in the IDE. So we'll leave out the <scope>test</scope>.

Updating the Application Properties

For embedded MongoDB to work, it needs to know which MongoDB version to use. Update the de.flapdoodle.mongodb.embedded.version property in your application application.properties file. Be careful; apparently when earlier versions of Spring Boot supported embedded MongoDB autoconfiguration, the property name was spring.mongodb.embedded.version.

de.flapdoodle.mongodb.embedded.version: 6.0.5

It doesn't matter so much if this property is always set, but if you'd prefer to be precise, you can use application.yaml to limit the property to the ide profile, which we'll specify in the code soon. (I'm using both YAML nested style and properties dotted style just to illustrate that either approach works.) See Working With Multi-Document Files to see how to include multiple profile definitions in one file, or you can instead set up profile-specific files.

# Default properties and other profiles go here.
…

# Separate multiple sections in YAML with three dash - characters.

# Properties only for the `ide` profile.
spring:
  config:
    activate:
      on-profile: ide
de.flapdoodle.mongodb.embedded.version: 6.0.5

Updating the Code

Because we've left out <scope>test</scope>, embedded MongoDB will always run with the application. This is because the de.flapdoodle.embed.mongo.spring.autoconfigure.EmbeddedMongoAutoConfiguration class comes with a host of Spring Boot autoconfiguration annotations to automatically inject itself into Spring Data's MongoDB support. You'll need to disable that in your application. (The curly brackets are optional if you're only excluding one class.) It's important that you exclude the class by name rather than by class reference; otherwise, if you remove the EmbeddedMongoAutoConfiguration class from the distribution JAR (see below), Spring will become confused and unable to exclude the class from autoconfiguration because the class itself is not present at runtime. See No Spring ServletWebServerFactory when excluding embedded MongoDB classes.

…
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(excludeName = {"de.flapdoodle.embed.mongo.spring.autoconfigure.EmbeddedMongoAutoConfiguration"})
public class MyMicroservice {
  …
}

Then you'll need to bring it back—but only in a profile of your choosing. We'll go with ide, indicating that we only want it to run in the IDE, but we could name it anything and enable it under whatever circumstances we want. You don't need to actually add any code; just override the EmbeddedMongoAutoConfiguration class we excluded and specify a profile for enabling it.

…

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import de.flapdoodle.embed.mongo.spring.autoconfigure.EmbeddedMongoAutoConfiguration;

@Profile("ide")
@Configuration
public class CustomEmbeddedMongoAutoConfiguration extends EmbeddedMongoAutoConfiguration {
}

Enable the Profile

Now your application will be ready to use embedded MongoDB, but only when you ask it to. Just use the normal Spring Boot profile activation mechanism. In your IDE you can configure the application run configuration's VM arguments to include ide. For example to set the dev and ide profiles:

-Dspring.profiles.active=dev,ide

Excluding Embedded MongoDB from Production

Embedded MongoDB won't be enabled in production, but if you make no further changes its dependencies will still be included in the JAR you build. Normally you could mark the dependencies as provided, but the Spring Boot Maven Plugin will include even provided dependencies in the executable JAR it generates. You'll need to manually exclude the dependencies using Spring Boot Maven Plugin dependency exclusion.

It's not as simple as simply telling Spring Boot to exclude a dependency and all its transitive dependencies. You'll have to name them one by one. At least there is a facility to exclude them by group ID. Here is the list I came up with after experimentation:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <executions>
        <execution>
          <goals>
            <goal>repackage</goal>
          </goals>
          <configuration>
            <excludeGroupIds>de.flapdoodle,de.flapdoodle.embed,de.flapdoodle.graph,
                de.flapdoodle.java8,de.flapdoodle.reverse</excludeGroupIds>
          </configuration>
        </execution>
      </executions>
    </plugin>
    …

Note that I'm explicitly indicating the repackage goal and specifying the configuration for that execution. If your POM inherits from spring-boot-starter-parent, an execution for repackage is enabled by default, so you could leave out the execution definition altogether and just specify the configuration for the plugin as a whole.

Now since the IDE doesn't care what is in the ultimate JAR, it will happily use embedded MongoDB if the profile ide is specified as active. The generated JAR file, which you may use alone or in a Docker image, won't include the embedded MongoDB files. Deployments outside of the IDE should be the same as if you never used embedded MongoDB at all.

This blog entry was updated on 2023-04-18 to explain how to prevent Spring Boot from failing to start when embedded MongoDB files are excluded from the distribution, as discussed on Stack Overflow.