Repository: jhipster/jhipster-lite
Pull request #12288 (2025-04-10 18:09:31)

Integrate Spring Boot's Docker Compose support
Closes https://github.com/jhipster/jhipster-lite/issues/12038

adem-hassine: Hi @murdos,

I wanted to highlight the changes compared to the previous PR:

- New CI Test Script : I’ve defined a new test CI script where the build tool wrapper is used instead of [java -jar](https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/start.sh#L28) to run the server. As since docker-compose is considered a development tool, it is not included when generating the JAR. This necessitated the creation of a new script file. 

-  Readiness Timeout Fixes : Some services were experiencing readiness timeout issues. To resolve this, I updated their docker-compose configurations to include proper health checks and disabled the default Spring Docker readiness behavior.

murdos: Hi @adem-hassine, thanks for the detailed report!
 
> I wanted to highlight the changes compared to the previous PR:
> 
>     * New CI Test Script : I’ve defined a new test CI script where the build tool wrapper is used instead of [java -jar](https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/start.sh#L28) to run the server. As since docker-compose is considered a development tool, it’s excluded when generating the JAR, along with the root Docker Compose file. This necessitated the creation of a new script file.

I think we should make a choice between testing generated applications in development/local conditions, or in conditions similar to the production. In generator-jhipster there were some ci tests that were testing dev, other prod.
I don't think we should bother with that, and I think we should only test the generated jar, as before.
@pascalgrimaud : WDYT?

>     * Readiness Timeout Fixes : Some services were experiencing readiness timeout issues. To resolve this, I updated their docker-compose configurations to include proper health checks and disabled the default Spring Docker readiness behavior.

Great! You made a huge job here too! :)


pascalgrimaud: I agree with @murdos :
- this feature should not change the current behavior of the CI
- the CI should use java -jar, instead of ./mvnw
- so we can still use the current script to start manually the différent service

Why? Because testing ./mvnw is not relevant, it will be used only locally and the important part is in Real environnement
So we should focus on java jar
adem-hassine: Hi @murdos , 

No problem at all and thanks for the feedback! 
> > ```
> > 1. For now, I’ve rolled back the changes related to the CI and kept only the `java -jar` execution in the ci. That said, this means future CI tests won’t run and test the dev mode.
> > ```
> 
> Yeah, we discussed that with @pascalgrimaud, and we assume it.
> 
Got it, thanks for confirming! 
> > ```
> > 2. After this rollback, I noticed something unexpected with the Gradle app: the Docker Compose dependency remains available on the JAR classpath. This led me to look for a way to prevent that behavior. However, since the developmentOnly scope is currently not supported by the [GradleCommandHandler.java](https://github.com/jhipster/jhipster-lite/blob/main/src/main/java/tech/jhipster/lite/module/infrastructure/secondary/javadependency/gradle/GradleCommandHandler.java#L232), I wanted to get your opinion on how best to proceed. For better visibility, I’ve pushed a sample generated from this PR and added a [small commit ](https://github.com/adem-hassine/jhipster-lite-fullappgradle-sample/commit/7cbe1f1f0dc11fb92ca62c46a987adb11d750a49) demonstrating how it can work for both development and production JAR scenarios.
> > ```
> 
> This seems to be a broader issue with java dependency scopes and their mapping to maven and gradle, that I think we should address separately. I think we should exclude gradle for now (more on that below) in order to not block the work on docker-compose.
> 
> During our last discussion with @pascalgrimaud, I changed a bit my mind, and I think it would be better if:
> 
> * each module that generates a docker compose file would contribute to the global `docker-compose.yml`
> * a new module for the spring boot and docker compose integration was added (it would add the dependency and spring configuration).
>   That way spring boot and docker compose could be an opt-in feature.
> 
> To circumvent the gradle issue, we would enforce a dependency to maven-java for the new module for the spring boot+docker compose integration, until the gradle issue is resolved.
> 
> I'm sorry for the change of direction. If you want, I can iterate over your branch and finish the work. Just let me know.
> 

Noted, no worries as this approach will certainly lead to a better implementation. I'm enjoying the process of contributing to JHipster Lite and am ready to proceed with the necessary changes.
> > ```
> > 3. The dependency on the Cassandra service in the migration Docker Compose file caused an issue. Since the Compose file is [launched separately](https://github.com/jhipster/jhipster-lite/blob/main/tests-ci/start_docker_compose.sh#L47), it creates a conflict when running the [Cassandra migration service](https://github.com/jhipster/jhipster-lite/pull/12288/files#diff-7883dd514d5619f69e09b3a33a78f9518f44ff99e57c6d2a59d27d578a416246R17). To resolve this, I created a copy of the Docker file to separate the one used by spring-boot-docker-compose from the one used manually.
> > ```
> 
> How about launching docker services in the CI from the global docker compose file? This could both simplify the CI scripts, and avoid the double docker file for cassandra?

Yes, we can use the global Docker Compose file and update the CI accordingly. However, maintaining a single file that references an external Docker Compose file will still introduce inconsistencies in the startup commands, e.g.: running the [Cassandra service](https://github.com/jhipster/jhipster-lite/blob/677a64c31ae55a33aaf15fa348d95b25141d562b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactory.java#L52) using the script provided in the README will fail.

Patch:
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/broker/kafka/domain/KafkaModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/broker/kafka/domain/KafkaModuleFactory.java
index 1adc68e85..6b17b0e54 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/broker/kafka/domain/KafkaModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/broker/kafka/domain/KafkaModuleFactory.java
@@ -1,6 +1,7 @@
 package tech.jhipster.lite.generator.server.springboot.broker.kafka.domain;
 
 import static tech.jhipster.lite.module.domain.JHipsterModule.artifactId;
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.documentationTitle;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.groupId;
@@ -69,6 +70,9 @@ public class KafkaModuleFactory {
         .set(propertyKey("kafka.producer.'[value.serializer]'"), propertyValue(STRING_SERIALIZER))
         .set(propertyKey("kafka.polling.timeout"), propertyValue(10000))
         .and()
+      .dockerComposeFile()
+        .append(dockerComposeFile("src/main/docker/kafka.yml"))
+        .and()
       .springTestProperties()
         .set(propertyKey("kafka.bootstrap-servers"), propertyValue("localhost:9092"))
         .and()
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactory.java
index f86cba8ad..56c24dcec 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactory.java
@@ -2,6 +2,7 @@ package tech.jhipster.lite.generator.server.springboot.database.cassandra.domain
 
 import static tech.jhipster.lite.module.domain.JHipsterModule.artifactId;
 import static tech.jhipster.lite.module.domain.JHipsterModule.comment;
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.documentationTitle;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.groupId;
@@ -84,6 +85,9 @@ public class CassandraModuleFactory {
       .springTestFactories()
         .append(propertyKey("org.springframework.context.ApplicationListener"), propertyValue(packageName + "TestCassandraManager"))
         .and()
+      .dockerComposeFile()
+        .append(dockerComposeFile("src/main/docker/cassandra.yml"))
+        .and()
       .springMainLogger("com.datastax", LogLevel.WARN)
       .springTestLogger("com.datastax", LogLevel.WARN)
       .springTestLogger("org.testcontainers", LogLevel.WARN)
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/datasource/domain/DatasourceModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/datasource/domain/DatasourceModuleFactory.java
index c52d006b8..51d154bc0 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/datasource/domain/DatasourceModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/datasource/domain/DatasourceModuleFactory.java
@@ -1,6 +1,7 @@
 package tech.jhipster.lite.generator.server.springboot.database.datasource.domain;
 
 import static tech.jhipster.lite.module.domain.JHipsterModule.artifactId;
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.documentationTitle;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.groupId;
@@ -62,6 +63,7 @@ public class DatasourceModuleFactory {
     //@formatter:off
     return moduleBuilder(properties)
       .apply(dockerContainer(dockerImages, datasourceProperties))
+      .apply(declareDockerComposeService(datasourceProperties))
       .apply(connectionPool(datasourceProperties))
       .apply(testcontainers(dockerImages, properties, datasourceProperties))
       .springMainProperties()
@@ -97,6 +99,7 @@ public class DatasourceModuleFactory {
     //@formatter:off
     return moduleBuilder(properties)
       .apply(dockerContainer(dockerImages, datasourceProperties))
+      .apply(declareDockerComposeService(datasourceProperties))
       .apply(connectionPool( datasourceProperties))
       .apply(testcontainers(dockerImages,  properties, datasourceProperties))
       .springMainProperties()
@@ -121,6 +124,7 @@ public class DatasourceModuleFactory {
     //@formatter:off
     return moduleBuilder(properties)
       .apply(dockerContainer(dockerImages, datasourceProperties))
+      .apply(declareDockerComposeService(datasourceProperties))
       .apply(connectionPool( datasourceProperties))
       .apply(testcontainers(dockerImages,  properties, datasourceProperties))
       .springMainProperties()
@@ -145,6 +149,7 @@ public class DatasourceModuleFactory {
     //@formatter:off
     return moduleBuilder(properties)
       .apply(dockerContainer(dockerImages, datasourceProperties))
+      .apply(declareDockerComposeService(datasourceProperties))
       .apply(connectionPool(datasourceProperties))
       .apply(testcontainers(dockerImages,  properties, datasourceProperties))
       .files()
@@ -187,6 +192,11 @@ public class DatasourceModuleFactory {
     //@formatter:on
   }
 
+  public static Consumer<JHipsterModule.JHipsterModuleBuilder> declareDockerComposeService(DatasourceProperties datasourceProperties) {
+    return moduleBuilder ->
+      moduleBuilder.dockerComposeFile().append(dockerComposeFile("src/main/docker/%s.yml".formatted(datasourceProperties.id())));
+  }
+
   public static Consumer<JHipsterModule.JHipsterModuleBuilder> dockerContainer(
     DockerImages dockerImages,
     DatasourceProperties datasourceProperties
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/mongodb/domain/MongoDbModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/mongodb/domain/MongoDbModuleFactory.java
index 1d74770af..89aea12e7 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/mongodb/domain/MongoDbModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/mongodb/domain/MongoDbModuleFactory.java
@@ -1,6 +1,7 @@
 package tech.jhipster.lite.generator.server.springboot.database.mongodb.domain;
 
 import static tech.jhipster.lite.module.domain.JHipsterModule.artifactId;
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.documentationTitle;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.groupId;
@@ -78,6 +79,9 @@ public class MongoDbModuleFactory {
       .springTestFactories()
         .append(propertyKey("org.springframework.context.ApplicationListener"), propertyValue(packageName + "TestMongoDBManager"))
         .and()
+      .dockerComposeFile()
+        .append(dockerComposeFile("src/main/docker/mongodb.yml"))
+        .and()
       .springMainLogger(REFLECTIONS_GROUP, LogLevel.WARN)
       .springMainLogger("org.mongodb.driver", LogLevel.WARN)
       .springTestLogger(REFLECTIONS_GROUP, LogLevel.WARN)
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/neo4j/domain/Neo4jModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/neo4j/domain/Neo4jModuleFactory.java
index 8f6d652ed..c3c770bf8 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/neo4j/domain/Neo4jModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/neo4j/domain/Neo4jModuleFactory.java
@@ -1,6 +1,7 @@
 package tech.jhipster.lite.generator.server.springboot.database.neo4j.domain;
 
 import static tech.jhipster.lite.module.domain.JHipsterModule.artifactId;
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.documentationTitle;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.groupId;
@@ -68,6 +69,9 @@ public class Neo4jModuleFactory {
       .springTestFactories()
         .append(propertyKey("org.springframework.context.ApplicationListener"), propertyValue(packageName + "TestNeo4jManager"))
         .and()
+      .dockerComposeFile()
+        .append(dockerComposeFile("src/main/docker/neo4j.yml"))
+        .and()
       .springMainLogger("org.neo4j.driver", LogLevel.WARN)
       .springTestLogger("org.neo4j.driver", LogLevel.WARN)
       .springTestLogger("com.github.dockerjava", LogLevel.WARN)
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/redis/domain/RedisModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/redis/domain/RedisModuleFactory.java
index 2be83ab6e..385380d14 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/database/redis/domain/RedisModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/database/redis/domain/RedisModuleFactory.java
@@ -12,7 +12,6 @@ import static tech.jhipster.lite.module.domain.JHipsterModule.propertyValue;
 import static tech.jhipster.lite.module.domain.JHipsterModule.toSrcMainDocker;
 import static tech.jhipster.lite.module.domain.JHipsterModule.toSrcMainJava;
 import static tech.jhipster.lite.module.domain.JHipsterModule.toSrcTestJava;
-import static tech.jhipster.lite.module.domain.javadependency.JavaDependencyScope.RUNTIME;
 
 import tech.jhipster.lite.module.domain.JHipsterModule;
 import tech.jhipster.lite.module.domain.LogLevel;
@@ -53,7 +52,6 @@ public class RedisModuleFactory {
         .and()
       .javaDependencies()
         .addDependency(groupId(SPRING_BOOT_GROUP), artifactId("spring-boot-starter-data-redis"))
-        .addDependency(springBootDockerComposeIntegration())
         .addDependency(reflectionsDependency())
         .addDependency(testContainerDependency())
         .and()
@@ -101,10 +99,6 @@ public class RedisModuleFactory {
       .build();
   }
 
-  private JavaDependency springBootDockerComposeIntegration() {
-    return JavaDependency.builder().groupId(SPRING_BOOT_GROUP).artifactId("spring-boot-docker-compose").scope(RUNTIME).optional().build();
-  }
-
   private JavaDependency reflectionsDependency() {
     return javaDependency().groupId(REFLECTIONS_GROUP).artifactId("reflections").versionSlug("reflections").build();
   }
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/dbmigration/cassandra/domain/CassandraMigrationModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/dbmigration/cassandra/domain/CassandraMigrationModuleFactory.java
index 59872f371..2b38e8a10 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/dbmigration/cassandra/domain/CassandraMigrationModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/dbmigration/cassandra/domain/CassandraMigrationModuleFactory.java
@@ -1,5 +1,6 @@
 package tech.jhipster.lite.generator.server.springboot.dbmigration.cassandra.domain;
 
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.documentationTitle;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.javaDependency;
@@ -51,6 +52,7 @@ public class CassandraMigrationModuleFactory {
         .add(SOURCE.template("TestCassandraMigrationLoader.java"), toSrcTestJava().append(packagePath).append("TestCassandraMigrationLoader.java"))
         .add(SOURCE.template("Cassandra-Migration.Dockerfile"), toSrcMainDocker().append(CASSANDRA).append("Cassandra-Migration.Dockerfile"))
         .add(SOURCE.file("cassandra-migration.yml"), toSrcMainDocker().append("cassandra-migration.yml"))
+        .add(SOURCE.file("cassandra-migration-spring-docker-compose.yml"), toSrcMainDocker().append("cassandra-migration-spring-docker-compose.yml"))
         .add(SOURCE.file("create-migration-keyspace.cql"), toSrcMainResourcesCql().append("create-migration-keyspace.cql"))
         .add(SOURCE.file("README.md"), toSrcMainResourcesCql().append("changelog").append("README.md"))
         .batch(SOURCE, toSrcMainDockerScripts())
@@ -58,6 +60,9 @@ public class CassandraMigrationModuleFactory {
           .addFile("execute-cql.sh")
           .and()
         .and()
+      .dockerComposeFile()
+        .append(dockerComposeFile("src/main/docker/cassandra-migration-spring-docker-compose.yml"))
+        .and()
       .springTestFactories()
         .append(propertyKey("org.springframework.context.ApplicationListener"), propertyValue(packageName + "TestCassandraMigrationLoader"))
         .and()
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/application/SpringBootDockerApplicationService.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/application/SpringBootDockerApplicationService.java
index 40e133c7d..8bbaeffd9 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/application/SpringBootDockerApplicationService.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/application/SpringBootDockerApplicationService.java
@@ -25,4 +25,8 @@ public class SpringBootDockerApplicationService {
   public JHipsterModule buildDockerFileGradleModule(JHipsterModuleProperties properties) {
     return springBootDocker.buildDockerFileGradleModule(properties);
   }
+
+  public JHipsterModule buildSpringBootDockerComposeModule(JHipsterModuleProperties properties) {
+    return springBootDocker.buildSpringBootDockerComposeModule(properties);
+  }
 }
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/domain/SpringBootDockerModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/domain/SpringBootDockerModuleFactory.java
index 7eb31c41f..2a2e01ffd 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/domain/SpringBootDockerModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/domain/SpringBootDockerModuleFactory.java
@@ -4,11 +4,15 @@ import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.gradleCommunityPlugin;
 import static tech.jhipster.lite.module.domain.JHipsterModule.mavenPlugin;
 import static tech.jhipster.lite.module.domain.JHipsterModule.moduleBuilder;
+import static tech.jhipster.lite.module.domain.JHipsterModule.propertyKey;
+import static tech.jhipster.lite.module.domain.JHipsterModule.propertyValue;
 import static tech.jhipster.lite.module.domain.JHipsterModule.to;
+import static tech.jhipster.lite.module.domain.javadependency.JavaDependencyScope.RUNTIME;
 
 import tech.jhipster.lite.module.domain.JHipsterModule;
 import tech.jhipster.lite.module.domain.file.JHipsterSource;
 import tech.jhipster.lite.module.domain.gradleplugin.GradleMainBuildPlugin;
+import tech.jhipster.lite.module.domain.javadependency.JavaDependency;
 import tech.jhipster.lite.module.domain.mavenplugin.MavenPlugin;
 import tech.jhipster.lite.module.domain.mavenplugin.MavenPluginConfiguration;
 import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
@@ -157,4 +161,28 @@ public class SpringBootDockerModuleFactory {
 
     return moduleBuilder(properties).files().add(SOURCE.template("Dockerfile-gradle"), to("Dockerfile")).and().build();
   }
+
+  public JHipsterModule buildSpringBootDockerComposeModule(JHipsterModuleProperties properties) {
+    Assert.notNull(PROPERTIES_FIELD, properties);
+
+    //@formatter:off
+    return moduleBuilder(properties)
+      .javaDependencies()
+        .addDependency(springBootDockerComposeIntegration())
+        .and()
+      .springTestProperties()
+        .set(propertyKey("spring.docker.compose.enabled"), propertyValue(false))
+        .and()
+      .build();
+    //@formatter:on
+  }
+
+  private JavaDependency springBootDockerComposeIntegration() {
+    return JavaDependency.builder()
+      .groupId("org.springframework.boot")
+      .artifactId("spring-boot-docker-compose")
+      .scope(RUNTIME)
+      .optional()
+      .build();
+  }
 }
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/infrastructure/primary/SpringBootDockerModuleConfiguration.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/infrastructure/primary/SpringBootDockerModuleConfiguration.java
index e67c74453..370b2f2ee 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/infrastructure/primary/SpringBootDockerModuleConfiguration.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/docker/infrastructure/primary/SpringBootDockerModuleConfiguration.java
@@ -6,7 +6,10 @@ import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.DOCKERFILE_
 import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.DOCKERFILE_MAVEN;
 import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.GRADLE_WRAPPER;
 import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.JIB;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.MAVEN_JAVA;
 import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.MAVEN_WRAPPER;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.SPRING_BOOT;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.SPRING_BOOT_DOCKER_COMPOSE;
 
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -57,4 +60,15 @@ class SpringBootDockerModuleConfiguration {
       .tags(SERVER_TAG, SPRING_TAG, SPRING_BOOT_TAG, DOCKER_TAG)
       .factory(springBootDocker::buildDockerFileGradleModule);
   }
+
+  @Bean
+  JHipsterModuleResource springBootDockerComposeIntegrationModule(SpringBootDockerApplicationService springBootDocker) {
+    return JHipsterModuleResource.builder()
+      .slug(SPRING_BOOT_DOCKER_COMPOSE)
+      .propertiesDefinition(JHipsterModulePropertiesDefinition.builder().addServerPort().build())
+      .apiDoc(SPRING_BOOT_TOOLS_GROUP, "Configure Spring Boot and docker compose integration, to make local development easier")
+      .organization(JHipsterModuleOrganization.builder().addDependency(MAVEN_JAVA).addDependency(SPRING_BOOT).build())
+      .tags(SERVER_TAG, SPRING_TAG, SPRING_BOOT_TAG, DOCKER_TAG)
+      .factory(springBootDocker::buildSpringBootDockerComposeModule);
+  }
 }
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactory.java
index 28aa9e6e8..1d566b880 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactory.java
@@ -3,6 +3,7 @@ package tech.jhipster.lite.generator.server.springboot.mvc.security.oauth2.core.
 import static tech.jhipster.lite.generator.server.springboot.mvc.security.common.domain.AuthenticationModuleFactory.authenticationModuleBuilder;
 import static tech.jhipster.lite.module.domain.JHipsterModule.JHipsterModuleBuilder;
 import static tech.jhipster.lite.module.domain.JHipsterModule.artifactId;
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.groupId;
 import static tech.jhipster.lite.module.domain.JHipsterModule.lineBeforeText;
@@ -96,6 +97,8 @@ public class OAuth2ModuleFactory {
         DOCKER_SOURCE.template("jhipster-realm.json"),
         DOCKER_DESTINATION.append("keycloak-realm-config").append(realmName + "-realm.json")
       );
+
+    builder.dockerComposeFile().append(dockerComposeFile("src/main/docker/keycloak.yml"));
   }
 
   private static void appendJavaFiles(JHipsterModuleBuilder builder, JHipsterModuleProperties properties) {
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/springcloud/consul/domain/ConsulModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/springcloud/consul/domain/ConsulModuleFactory.java
index 55cc029ac..d47fd2454 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/springcloud/consul/domain/ConsulModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/springcloud/consul/domain/ConsulModuleFactory.java
@@ -2,6 +2,7 @@ package tech.jhipster.lite.generator.server.springboot.springcloud.consul.domain
 
 import static tech.jhipster.lite.module.domain.JHipsterModule.JHipsterModuleBuilder;
 import static tech.jhipster.lite.module.domain.JHipsterModule.artifactId;
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.groupId;
 import static tech.jhipster.lite.module.domain.JHipsterModule.javaDependency;
@@ -92,6 +93,9 @@ public class ConsulModuleFactory {
         .set(propertyKey("spring.cloud.consul.enabled"), FALSE_VALUE)
         .set(propertyKey("spring.cloud.compatibility-verifier.enabled"), FALSE_VALUE)
         .and()
+      .dockerComposeFile()
+        .append(dockerComposeFile("src/main/docker/consul.yml"))
+        .and()
       .springMainLogger("org.apache", LogLevel.ERROR)
       .springTestLogger("org.apache", LogLevel.ERROR);
     //@formatter:on
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/springcloud/eureka/domain/EurekaModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/springcloud/eureka/domain/EurekaModuleFactory.java
index 90f39919c..2c0ce95d7 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/springcloud/eureka/domain/EurekaModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/springcloud/eureka/domain/EurekaModuleFactory.java
@@ -3,6 +3,7 @@ package tech.jhipster.lite.generator.server.springboot.springcloud.eureka.domain
 import static tech.jhipster.lite.generator.server.springboot.springcloud.common.domain.SpringCloudModuleDependencies.SPRING_CLOUD_GROUP;
 import static tech.jhipster.lite.generator.server.springboot.springcloud.common.domain.SpringCloudModuleDependencies.springCloudDependenciesManagement;
 import static tech.jhipster.lite.module.domain.JHipsterModule.artifactId;
+import static tech.jhipster.lite.module.domain.JHipsterModule.dockerComposeFile;
 import static tech.jhipster.lite.module.domain.JHipsterModule.from;
 import static tech.jhipster.lite.module.domain.JHipsterModule.moduleBuilder;
 import static tech.jhipster.lite.module.domain.JHipsterModule.propertyKey;
@@ -84,6 +85,9 @@ public class EurekaModuleFactory {
         .set(propertyKey("eureka.client.enabled"), FALSE_VALUE)
         .set(propertyKey("spring.cloud.compatibility-verifier.enabled"), FALSE_VALUE)
         .and()
+      .dockerComposeFile()
+        .append(dockerComposeFile("src/main/docker/jhipster-registry.yml"))
+        .and()
       .build();
     //@formatter:on
   }
diff --git a/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java b/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
index 6588c5463..2f61ef8de 100644
--- a/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
+++ b/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
@@ -134,6 +134,7 @@ public enum JHLiteModuleSlug implements JHipsterModuleSlugFactory {
   SPRING_BOOT_CUCUMBER_OAUTH_2_AUTHENTICATION("spring-boot-cucumber-oauth2-authentication", RANK_B),
   SPRING_BOOT_DEVTOOLS("spring-boot-devtools", RANK_C),
   SPRING_BOOT_LOCAL_PROFILE("spring-boot-local-profile", RANK_B),
+  SPRING_BOOT_DOCKER_COMPOSE("spring-boot-docker-compose", RANK_C),
   SPRING_BOOT_JWT("spring-boot-jwt", RANK_C),
   SPRING_BOOT_JWT_BASIC_AUTH("spring-boot-jwt-basic-auth", RANK_C),
   SPRING_BOOT_KAFKA("spring-boot-kafka", RANK_B),
diff --git a/src/main/resources/generator/server/springboot/database/cassandra/cassandra.yml.mustache b/src/main/resources/generator/server/springboot/database/cassandra/cassandra.yml.mustache
index 578847907..f55d16417 100644
--- a/src/main/resources/generator/server/springboot/database/cassandra/cassandra.yml.mustache
+++ b/src/main/resources/generator/server/springboot/database/cassandra/cassandra.yml.mustache
@@ -11,3 +11,8 @@ services:
       - CASSANDRA_DC={{ DC }}
     #volumes:
     #  - /custom/cassandra/data/dir:/var/lib/cassandra
+    healthcheck:
+      test: ["CMD-SHELL", "[ $$(nodetool statusgossip) = running ]"]
+      interval: 30s
+      timeout: 10s
+      retries: 5
diff --git a/src/main/resources/generator/server/springboot/dbmigration/cassandra/cassandra-migration-spring-docker-compose.yml b/src/main/resources/generator/server/springboot/dbmigration/cassandra/cassandra-migration-spring-docker-compose.yml
new file mode 100644
index 000000000..5de8267a5
--- /dev/null
+++ b/src/main/resources/generator/server/springboot/dbmigration/cassandra/cassandra-migration-spring-docker-compose.yml
@@ -0,0 +1,19 @@
+x-cassandra-migration-common: &cassandra-migration-common
+  container_name: cassandra-migration
+  environment:
+    - CASSANDRA_CONTACT_POINT=cassandra
+    - USER=docker-cassandra-migration
+    - CREATE_MIGRATION_KEYSPACE_SCRIPT=create-migration-keyspace.cql
+    #- DEBUG_LOG=1 # uncomment to show debug logs during the migration process
+  build:
+    context: .
+    dockerfile: cassandra/Cassandra-Migration.Dockerfile
+  volumes:
+    - ../resources/config/cql:/cql:ro
+
+services:
+  cassandra-migration-spring-docker-compose:
+    <<: *cassandra-migration-common
+    depends_on:
+      cassandra:
+        condition: service_healthy
diff --git a/src/main/resources/generator/server/springboot/dbmigration/cassandra/cassandra-migration.yml b/src/main/resources/generator/server/springboot/dbmigration/cassandra/cassandra-migration.yml
index 56f7886ee..22593c0df 100644
--- a/src/main/resources/generator/server/springboot/dbmigration/cassandra/cassandra-migration.yml
+++ b/src/main/resources/generator/server/springboot/dbmigration/cassandra/cassandra-migration.yml
@@ -1,13 +1,16 @@
+x-cassandra-migration-common: &cassandra-migration-common
+  container_name: cassandra-migration
+  environment:
+    - CASSANDRA_CONTACT_POINT=cassandra
+    - USER=docker-cassandra-migration
+    - CREATE_MIGRATION_KEYSPACE_SCRIPT=create-migration-keyspace.cql
+    #- DEBUG_LOG=1 # uncomment to show debug logs during the migration process
+  build:
+    context: .
+    dockerfile: cassandra/Cassandra-Migration.Dockerfile
+  volumes:
+    - ../resources/config/cql:/cql:ro
+
 services:
   cassandra-migration:
-    container_name: cassandra-migration
-    environment:
-      - CASSANDRA_CONTACT_POINT=cassandra
-      - USER=docker-cassandra-migration
-      - CREATE_MIGRATION_KEYSPACE_SCRIPT=create-migration-keyspace.cql
-      #- DEBUG_LOG=1 # uncomment to show debug logs during the migration process
-    build:
-      context: .
-      dockerfile: cassandra/Cassandra-Migration.Dockerfile
-    volumes:
-      - ../resources/config/cql:/cql:ro
+    <<: *cassandra-migration-common
diff --git a/src/main/resources/generator/server/springboot/mvc/security/oauth2/core/docker/keycloak.yml.mustache b/src/main/resources/generator/server/springboot/mvc/security/oauth2/core/docker/keycloak.yml.mustache
index bca8f7dcb..1dd0609a4 100644
--- a/src/main/resources/generator/server/springboot/mvc/security/oauth2/core/docker/keycloak.yml.mustache
+++ b/src/main/resources/generator/server/springboot/mvc/security/oauth2/core/docker/keycloak.yml.mustache
@@ -12,8 +12,16 @@ services:
       - KC_FEATURES=scripts
       - KC_HTTP_PORT=9080
       - KC_HTTPS_PORT=9443
+      - KC_HEALTH_ENABLED=true
     # If you want to expose these ports outside your dev PC,
     # remove the "127.0.0.1:" prefix
     ports:
       - '127.0.0.1:9080:9080'
       - '127.0.0.1:9443:9443'
+    healthcheck:
+      test: ['CMD-SHELL', '[ -f /tmp/JHipsterKeycloakHealthCheck.java ] || echo "public class JHipsterKeycloakHealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/JHipsterKeycloakHealthCheck.java && java /tmp/JHipsterKeycloakHealthCheck.java http://localhost:9000/health/live']
+      interval: 5s
+      timeout: 5s
+      retries: 30
+    labels:
+      org.springframework.boot.readiness-check.tcp.disable: true
diff --git a/src/main/resources/generator/server/springboot/springcloud/consul/src/consul.yml.mustache b/src/main/resources/generator/server/springboot/springcloud/consul/src/consul.yml.mustache
index 4a884d673..73500a291 100644
--- a/src/main/resources/generator/server/springboot/springcloud/consul/src/consul.yml.mustache
+++ b/src/main/resources/generator/server/springboot/springcloud/consul/src/consul.yml.mustache
@@ -9,6 +9,13 @@ services:
       - '127.0.0.1:8500:8500'
       - '127.0.0.1:8600:8600'
     command: consul agent -dev -ui -client 0.0.0.0 -log-level=INFO
+    healthcheck:
+      test: ["CMD-SHELL", "consul info | awk '/health_score/{if ($3 >=1) exit 1; else exit 0}'"]
+      interval: 30s
+      timeout: 10s
+      retries: 5
+    labels:
+      org.springframework.boot.readiness-check.tcp.disable: true
 
   consul-config-loader:
     image: {{dockerImageConfigLoader}}


Test patch:
diff --git a/src/test/features/server/springboot/spring-boot-docker.feature b/src/test/features/server/springboot/spring-boot-docker.feature
index b62ed867d..1ee7a91d7 100644
--- a/src/test/features/server/springboot/spring-boot-docker.feature
+++ b/src/test/features/server/springboot/spring-boot-docker.feature
@@ -16,3 +16,7 @@ Feature: Spring boot docker
     When I apply "dockerfile-gradle" module to default project without parameters
     Then I should have files in "."
       | Dockerfile |
+
+  Scenario: Should apply spring boot docker compose module
+    When I apply "spring-boot-docker-compose" module to default project with maven file without parameters
+    Then I should have "<artifactId>spring-boot-docker-compose</artifactId>" in "pom.xml"
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/broker/kafka/domain/KafkaModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/broker/kafka/domain/KafkaModuleFactoryTest.java
index cc0e247ca..d914272b7 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/broker/kafka/domain/KafkaModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/broker/kafka/domain/KafkaModuleFactoryTest.java
@@ -55,6 +55,9 @@ class KafkaModuleFactoryTest {
       .containing("image: apache/kafka-native")
       .containing("CLUSTER_ID: 'my-cluster'")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/kafka.yml")
+      .and()
       .hasFile("src/main/resources/config/application.yml")
       .containing(
         """
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactoryTest.java
index 8cdff4d86..0937ea5ab 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/cassandra/domain/CassandraModuleFactoryTest.java
@@ -74,6 +74,9 @@ class CassandraModuleFactoryTest {
       .containing("cassandra:4.0.7")
       .containing("CASSANDRA_DC=" + DC)
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/cassandra.yml")
+      .and()
       .hasFile("src/main/resources/config/application.yml")
       .containing(
         // language=yaml
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/datasource/domain/DatasourceModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/datasource/domain/DatasourceModuleFactoryTest.java
index fe08cda74..64866a215 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/datasource/domain/DatasourceModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/datasource/domain/DatasourceModuleFactoryTest.java
@@ -120,6 +120,9 @@ class DatasourceModuleFactoryTest {
       .containing("<groupId>org.testcontainers</groupId>")
       .containing("<artifactId>mariadb</artifactId>")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/mariadb.yml")
+      .and()
       .hasFile("src/main/resources/config/application.yml")
       .containing(
         """
@@ -182,6 +185,9 @@ class DatasourceModuleFactoryTest {
       .containing("<groupId>org.testcontainers</groupId>")
       .containing("<artifactId>mysql</artifactId>")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/mysql.yml")
+      .and()
       .hasFile("src/main/resources/config/application.yml")
       .containing(
         // language=yaml
@@ -251,6 +257,9 @@ class DatasourceModuleFactoryTest {
       .containing("<groupId>org.testcontainers</groupId>")
       .containing("<artifactId>mssqlserver</artifactId>")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/mssql.yml")
+      .and()
       .hasFile("src/main/resources/config/application.yml")
       .containing(
         // language=yaml
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/mongodb/domain/MongoDbModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/mongodb/domain/MongoDbModuleFactoryTest.java
index dc86d9af7..d8e2799a8 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/mongodb/domain/MongoDbModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/mongodb/domain/MongoDbModuleFactoryTest.java
@@ -79,6 +79,9 @@ class MongoDbModuleFactoryTest {
       .hasFile("src/main/docker/mongodb.yml")
       .containing("mongo:1.1.1")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/mongodb.yml")
+      .and()
       .hasPrefixedFiles(
         "src/main/java/tech/jhipster/jhlitest/wire/mongodb/infrastructure/secondary",
         "MongodbDatabaseConfiguration.java",
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/neo4j/domain/Neo4jModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/neo4j/domain/Neo4jModuleFactoryTest.java
index ca4daf36c..44f481b1c 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/neo4j/domain/Neo4jModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/neo4j/domain/Neo4jModuleFactoryTest.java
@@ -70,6 +70,9 @@ class Neo4jModuleFactoryTest {
       .hasFile("src/main/docker/neo4j.yml")
       .containing("neo4j:4.4.11-community")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/neo4j.yml")
+      .and()
       .hasFiles("src/test/java/tech/jhipster/jhlitest/TestNeo4jManager.java")
       .hasFile("src/test/resources/META-INF/spring.factories")
       .containing("org.springframework.context.ApplicationListener=tech.jhipster.jhlitest")
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/redis/domain/RedisModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/redis/domain/RedisModuleFactoryTest.java
index 03619136e..37caf0a2e 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/database/redis/domain/RedisModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/database/redis/domain/RedisModuleFactoryTest.java
@@ -75,16 +75,6 @@ class RedisModuleFactoryTest {
             </dependency>
         """
       )
-      .containing(
-        """
-            <dependency>
-              <groupId>org.springframework.boot</groupId>
-              <artifactId>spring-boot-docker-compose</artifactId>
-              <scope>runtime</scope>
-              <optional>true</optional>
-            </dependency>
-        """
-      )
       .and()
       .hasFile("src/main/docker/redis.yml")
       .containing("redis:1.1.1")
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/dbmigration/cassandra/domain/CassandraMigrationModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/dbmigration/cassandra/domain/CassandraMigrationModuleFactoryTest.java
index 8a94abf4f..4da3f412e 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/dbmigration/cassandra/domain/CassandraMigrationModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/dbmigration/cassandra/domain/CassandraMigrationModuleFactoryTest.java
@@ -50,6 +50,10 @@ class CassandraMigrationModuleFactoryTest {
       )
       .and()
       .hasFiles("src/main/docker/cassandra-migration.yml")
+      .hasFiles("src/main/docker/cassandra-migration-spring-docker-compose.yml")
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/cassandra-migration-spring-docker-compose.yml")
+      .and()
       .hasFiles("src/main/resources/config/cql/create-migration-keyspace.cql")
       .hasFiles("src/main/resources/config/cql/changelog/README.md")
       .hasFiles("documentation/cassandra-migration.md")
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/docker/domain/SpringBootDockerModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/docker/domain/SpringBootDockerModuleFactoryTest.java
index 662534b46..9c84b4e2b 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/docker/domain/SpringBootDockerModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/docker/domain/SpringBootDockerModuleFactoryTest.java
@@ -185,4 +185,39 @@ class SpringBootDockerModuleFactoryTest {
       assertThatModule(module).hasFile("Dockerfile").containing("EXPOSE 9000").containing("./gradlew");
     }
   }
+
+  @Nested
+  class SpringBootDockerComposeModuleTest {
+
+    @Test
+    void shouldBuildSpringBootDockerComposeIntegrationModule() {
+      JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest())
+        .put("serverPort", 9000)
+        .build();
+
+      JHipsterModule module = factory.buildSpringBootDockerComposeModule(properties);
+      assertThatModuleWithFiles(module, pomFile(), logbackFile(), testLogbackFile(), readmeFile())
+        .hasFile("pom.xml")
+        .containing(
+          """
+              <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-docker-compose</artifactId>
+                <scope>runtime</scope>
+                <optional>true</optional>
+              </dependency>
+          """
+        )
+        .and()
+        .hasFile("src/test/resources/config/application-test.yml")
+        .containing(
+          """
+          spring:
+            docker:
+              compose:
+                enabled: false
+          """
+        );
+    }
+  }
 }
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactoryTest.java
index 263c87010..d552fba93 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactoryTest.java
@@ -82,6 +82,9 @@ class OAuth2ModuleFactoryTest {
       .hasFile("src/main/docker/keycloak.yml")
       .containing("quay.io/keycloak/keycloak:1.1.1")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/keycloak.yml")
+      .and()
       .hasFile("src/main/docker/keycloak-realm-config/beer-realm.json")
       .containing("1.1.1")
       .and()
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/springcloud/consul/domain/ConsulModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/springcloud/consul/domain/ConsulModuleFactoryTest.java
index 3f6f1198c..63fbd80ce 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/springcloud/consul/domain/ConsulModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/springcloud/consul/domain/ConsulModuleFactoryTest.java
@@ -42,6 +42,9 @@ class ConsulModuleFactoryTest {
       .containing("consul:1.12.2")
       .containing("jhipster/consul-config-loader:v0.4.1")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/consul.yml")
+      .and()
       .hasFile("src/main/docker/central-server-config/application.yml")
       .and()
       .hasFile("pom.xml")
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/springcloud/eureka/domain/EurekaModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/springcloud/eureka/domain/EurekaModuleFactoryTest.java
index 25b0aaf53..b8d36dc28 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/springcloud/eureka/domain/EurekaModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/springcloud/eureka/domain/EurekaModuleFactoryTest.java
@@ -118,6 +118,9 @@ class EurekaModuleFactoryTest {
       .hasFile("src/main/docker/jhipster-registry.yml")
       .containing("jhipster/jhipster-registry:1.1.1")
       .and()
+      .hasFile("docker-compose.yml")
+      .containing("src/main/docker/jhipster-registry.yml")
+      .and()
       .hasFile("src/main/docker/central-server-config/localhost-config/application.properties");
   }
 }
diff --git a/tests-ci/start_docker_compose.sh b/tests-ci/start_docker_compose.sh
index 4bc3ce6a4..f48a40860 100755
--- a/tests-ci/start_docker_compose.sh
+++ b/tests-ci/start_docker_compose.sh
@@ -16,57 +16,11 @@ fi
 # Start docker container
 #-------------------------------------------------------------------------------
 cd "$JHI_FOLDER_APP"
-if [ -a src/main/docker/keycloak.yml ]; then
-  docker compose -f src/main/docker/keycloak.yml up -d
+if [ -a docker-compose.yml ]; then
+  docker compose up -d
   echo "*** wait 20sec"
   sleep 20
+  docker ps -a
+else
+  echo "No 'docker-compose.yml' file found — this application does not require containerized components to be launched."
 fi
-if [ -a src/main/docker/kafka.yml ]; then
-  docker compose -f src/main/docker/kafka.yml up -d
-fi
-if [ -a src/main/docker/consul.yml ]; then
-  docker compose -f src/main/docker/consul.yml up -d
-fi
-if [ -a src/main/docker/mongodb.yml ]; then
-  docker compose -f src/main/docker/mongodb.yml up -d
-fi
-if [ -a src/main/docker/redis.yml ]; then
-  docker compose -f src/main/docker/redis.yml up -d
-fi
-if [ -a src/main/docker/cassandra.yml ]; then
-  docker compose -f src/main/docker/cassandra.yml up -d
-  echo "*** wait until cassandra instance is UP"
-  retryCount=0
-  maxRetry=20
-  while ! docker exec cassandra cqlsh &> /dev/null && [ "$retryCount" -ne "$maxRetry" ]; do
-    echo " Cassandra not reachable yet. sleep and retry. retryCount =" $retryCount
-    sleep 5
-    ((retryCount += 1))
-  done
-fi
-if [ -a src/main/docker/cassandra-migration.yml ]; then
-  docker compose -f src/main/docker/cassandra-migration.yml up -d
-fi
-if [ -a src/main/docker/mysql.yml ]; then
-  docker compose -f src/main/docker/mysql.yml up -d
-fi
-if [ -a src/main/docker/postgresql.yml ]; then
-  docker compose -f src/main/docker/postgresql.yml up -d
-fi
-if [ -a src/main/docker/mariadb.yml ]; then
-  docker compose -f src/main/docker/mariadb.yml up -d
-fi
-if [ -a src/main/docker/neo4j.yml ]; then
-  docker compose -f src/main/docker/neo4j.yml up -d
-fi
-if [ -a src/main/docker/mssql.yml ]; then
-  docker compose -f src/main/docker/mssql.yml up -d
-  echo "*** wait 40sec"
-  sleep 40
-fi
-if [ -a src/main/docker/jhipster-registry.yml ]; then
-  docker compose -f src/main/docker/jhipster-registry.yml up -d
-fi
-echo "*** wait 20sec"
-sleep 20
-docker ps -a