Spring Boot Docker Compose Example

Docker has revolutionized application development with easy-to-use containers that can run on any machine and provide seamlessly consistent behavior in each environment. Docker Compose allows defining the multiple containers in a YAML file that the application depends on. These containers can be databases, message queues, or even network services.

Spring Boot supports a dockerized environment in two ways:

This article discusses how to start all the docker containers when the application starts; and shut down all containers when the application stops during development and local testing as well.

1. Docker Compose Support in Spring Boot 3

Since version 3.1, Spring boot provides a new module ‘spring-boot-docker-compose‘ that directly integrates with Docker Compose.

As a prequisite, the ‘docker compose‘ or ‘docker-compose CLI‘ application must be present on the path. Spring boot uses the commands that are executed using this application.

1.1. Dependency

The Docker Compose support can be enabled by including the ‘spring-boot-docker-componse‘ dependency.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-docker-compose</artifactId>
  <optional>true</optional>
</dependency>

For Gradle, add this dependency:

dependencies {
    developmentOnly("org.springframework.boot:spring-boot-docker-compose")
}

When this module is included as a dependency Spring Boot, automatically, will do the following:

  • Search for a compose.yml and other common compose filenames in your application directory
  • Call docker compose up with the discovered compose.yml.
  • Create service connection beans for each supported container
  • Call docker compose stop when the application is shutdown.

1.2. Docker Compose File

Next, create a docker-compose.yaml file with the following content:

services:
  redis:
    image: 'redis'
    ports:
      - '6379'

1.3. Test the Application

Next, we can use the RedisTemplate similar to a normal application and we will be able to connect to the Docker containerized Redis instance.

Now, start the application and notice the logs. The application downloads the docker images, starts the container and then the application is fully started.

INFO 25644 --- [           main] com.howtodoinjava.demo.App               : Starting App using Java 17.0.1 with PID 25644 (C:\Users\lokes\IdeaProjects\Spring-Boot-Examples\spring-boot-docker-compose\target\classes started by lokesh in C:\Users\lokes\IdeaProjects\Spring-Boot-Examples)
2023-05-30T13:08:00.218+05:30  INFO 25644 --- [           main] com.howtodoinjava.demo.App               : No active profile set, falling back to 1 default profile: "default"
2023-05-30T13:08:00.321+05:30  INFO 25644 --- [           main] .s.b.d.c.l.DockerComposeLifecycleManager : Using Docker Compose file 'C:\Users\lokes\IdeaProjects\Spring-Boot-Examples\spring-boot-docker-compose\docker-compose.yaml'
2023-05-30T13:08:01.742+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  redis Pulling 
2023-05-30T13:08:06.034+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  f03b40093957 Pulling fs layer 
2023-05-30T13:08:06.034+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  8db26c5e8435 Pulling fs layer 

...
...
...

2023-05-30T13:08:12.639+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  635073d8ccd5 Pull complete 
2023-05-30T13:08:12.704+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  redis Pulled 
2023-05-30T13:08:12.718+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  Network spring-boot-docker-compose_default  Creating
2023-05-30T13:08:12.785+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  Network spring-boot-docker-compose_default  Created
2023-05-30T13:08:12.785+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  Container spring-boot-docker-compose-redis-1  Creating
2023-05-30T13:08:13.618+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  Container spring-boot-docker-compose-redis-1  Created
2023-05-30T13:08:13.624+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  Container spring-boot-docker-compose-redis-1  Starting
2023-05-30T13:08:14.149+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  Container spring-boot-docker-compose-redis-1  Started
2023-05-30T13:08:14.149+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  Container spring-boot-docker-compose-redis-1  Waiting
2023-05-30T13:08:14.668+05:30  INFO 25644 --- [utReader-stderr] o.s.boot.docker.compose.core.DockerCli   :  Container spring-boot-docker-compose-redis-1  Healthy
2023-05-30T13:08:16.091+05:30  INFO 25644 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-05-30T13:08:16.100+05:30  INFO 25644 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-05-30T13:08:16.101+05:30  INFO 25644 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.8]
2023-05-30T13:08:16.194+05:30  INFO 25644 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-05-30T13:08:16.195+05:30  INFO 25644 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1100 ms
2023-05-30T13:08:16.623+05:30  INFO 25644 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-05-30T13:08:16.633+05:30  INFO 25644 --- [           main] com.howtodoinjava.demo.App               : Started App in 16.819 

2. How does Docker Compose Support Work?

During the startup, when this module detects a ‘docker-compose.yml‘ or ‘compose.yml‘ in the root of your application folder, it uses that to start the containers before starting the application. The new module provides the following features:

  • Checks for a docker-compose file in the project’s root directory. The file is searched with names:
    • compose.yaml
    • compose.yml
    • docker-compose.yaml
    • docker-compose.yml
  • When the application starts, containers/services defined in the compose file are started using the command “docker compose up“.
  • The service connection beans are created for each supported container.
  • When the application stops, containers/services defined in the compose file are shut down using the command “docker compose down“.
docker compose up  # when the application starts

docker compose down # when the application shuts down

Internally, it will use the ‘ConnectionProvider‘ abstraction and @ServiceConnection support to determine which containers are started and will make the metadata available to Spring Boot.

By default, the Docker Compose support is disabled when running tests, in favor of using Testcontainers. To enable it, set spring.docker.compose.skip.in-tests to false.

3. Configuring the Docker Compose File

3.1. File Name and Location of Compose File

By default, the compose file is looked into the project’s root. To use a custom file with a different name or location, set the spring.docker.compose.file property. The file path can be exact or relative to the root.

spring.docker.compose.file = ./docker/compose.yaml

If the default or specified compose file is not found, java.lang.IllegalStateException is thrown:

java.lang.IllegalStateException: No Docker Compose file found in directory 
  'C:\Users\lokesh\IdeaProjects\Spring-Boot-Examples\.'

3.2. Active Profile-specific Compose File

Docker Compose profiles are similar to Spring profiles in that they let us adjust the Docker Compose configuration for specific environments. If we want to activate a specific Docker Compose profile we can use the ‘spring.docker.compose.profiles.active‘ property in the ‘application.properties‘ or ‘application.yaml‘ file:

We can activate a specific profile using the following property:

spring.docker.compose.profiles.active=dev

Define the profile in ‘docker-compose.yaml‘ as follows. This configuration starts a MySQL database for the development profile and starts an Oracle database for the production profile.

services:

  mysql-db:
    image: mysql
    profiles: [dev]
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: mydatabase

  oracle-db:
    image: oracle
    profiles: [prod]
    ports:
      - "1521:1521"
    environment:
      ORACLE_ROOT_PASSWORD: root
      ORACLE_DATABASE: mydatabase

This way we can assign profiles to all the services and when we start the application, only the services in active profiles will be started.

4. Docker Compose Supported Containers

The Docker Compose support in Spring Boot uses the names of the Docker images to determine what to configure. See the following table for the names and supported technologies.

TechnologyContainer Image Name(s) Matched
Cassandracassandra
ElasticSearchelasticsearch
JDBC and R2DBCgvenzl/oracle-xe, mariadb, mssql/server, MySQL, postgres
MongoDBmongo
RabbitMQrabbitmq
Redisredis
Zipkinopenzipkin/zipkin

Suppose we want to use a customized container, for instance with a preconfigured and filled PostgreSQL database or a preconfigured and secured message broker. In that case, we can do some additional configuration in the Docker Compose file.

For example, in the following configuration, Spring Boot Docker Compose support will read the ‘org.springframework.boot.service-connection label‘ and the value it has will be used as the name of the container:

services:
  redis:
    image: 'mycompany/mycustomredis:7.0'
    ports:
      - '6379'
    labels:
      org.springframework.boot.service-connection: mycustomredis

5. Customizing the Docker Support

The module supports a few more customizations using the properties:

5.1. Waiting for Containers to Start

Docker containers may take time to install/start. By default, Spring boot waits for all the services and containers to start. Note that a container is considered ready when a TCP/IP connection can be established to its mapped port.

If we want to turn off this behavior for a particular container(s), we can use the org.springframework.boot.readiness-check.tcp.disable property as follows:

services:
  redis:
    image: 'redis'
    ports:
      - '6379'
    labels:
      org.springframework.boot.readiness-check.tcp.disable: true

5.2. Connection Timeouts

It is also possible to configure different timeouts using the respective properties.

spring.docker.compose.readiness.tcp.read-timeout=200ms        #Timeout for reads
spring.docker.compose.readiness.tcp.connect-timeout=200ms     #Timeout for connections

spring.docker.compose.readiness.timeout=2m     #Timeout of the readiness checks
spring.docker.compose.readiness.wait=always    #Wait strategy to use

5.3. Managing the Container Lifecycle

The following property controls the Docker Compose lifecycle:

spring.docker.compose.lifecycle-management = start-and-stop

It can have either of the three values:

  • none – Do not start or stop Docker Compose
  • start-only – Start Docker Compose when the application starts and leave it running
  • start-and-stop – Start Docker Compose when the application starts and stop it when the JVM exits

6. Conclusion

In this Spring boot docker-compose tutorial, we learned to use the inbuilt docker-compose module in Spring boot. We learned to customize the compose file and its lifecycle with an example.

Happy Learning !!

Source Code on Github

Comments

Subscribe
Notify of
guest
2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.