How to deploy a Spring Boot application with Docker and Java 21

Updated: 2023-12-19

Dockerfile

For our image we use the eclipse temurin alpine JRE distribution.

Eclipse Temurin is a de facto standard for Java development and doesn't present licensing surpises ;-)

To deploy we use simply a JRE distribution, generally we don't need a JDK.
And we use Alpine Linux as base image, this is minimalistic and fast. If you need more features you can opt for a different Linux distribution.

We copy the jar file to the /opt directory as suggested by the FHS

## alpine linux with JRE 
FROM docker pull eclipse-temurin:21-jre-alpine 
 
## create a nonroot user and group 
RUN addgroup -S spring && adduser -S spring -G spring 
 
## copy the spring jar 
COPY target/*.jar /opt/myApp.jar 
 
## set the nonroot user as the default user 
USER spring:spring 
 
## set the working directory 
WORKDIR /opt 
 
ENTRYPOINT ["java", "-jar", "myApp.jar"] 
 
## expose the port to the external world 
EXPOSE 8080 

To have a runnable jar you need to repackage the default jar.

Here you can find the official documentation:
spring-boot:repackage

<build> 
    <plugins> 
        <plugin> 
            <groupId>org.springframework.boot</groupId> 
                <artifactId>spring-boot-maven-plugin</artifactId> 
                    <executions> 
                        <execution> 
                            <goals> 
                                <goal>repackage</goal> 
                            </goals> 
                        </execution> 
                    </executions> 
        </plugin> 
    </plugins> 
</build> 

Build and run the image

You can build your Dockerfile with e.g.:

docker build -t spring-example .

and run it with:

docker run -it -p 8080:8080 spring-example

Error without repackaging

If you get this error no main manifest attribute, in [name of your app] something went wrong with the spring-boot-maven-plugin.

Verifiy that it is correctly configured.

Alpine image size

The alpine image size is smaller compared to other distributions.

The version with JRE (21 -jre-alpine) is 66MB.

Healthcheck potential issue with Docker compose

If you are using Docker compose and you add an healthcheck to check if your image started correctly before to start the next container you need to test with wget and not curl.

curl is not available in this image, if you want use it you need to install it manually.

Example of healthcheck in Docker Compose:

services: 
  my-app: 
    build: 
      context: ../app-backend/target 
      dockerfile: Dockerfile 
    ports: 
      - "8080:8080" 
    healthcheck: 
      test: wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1 
      interval: 1m30s 
      timeout: 10s 
      retries: 3 

Here is an explanation of the wget options used:

  • --no-verbose: this option turns off verbose mode, which means wget's output will be minimized. This may be desired so as not to clog up your logs with unnecessary data.
  • --tries=1: this option sets the number retries to 1. wget will retry network operations if they failed due to temporary network errors.
  • --spider: wget will not download the pages, just check that they are there. This is known as spider mode. This is useful for checking link validity.

Add more parameters to the Spring application

If you want to add more start parameters to your Spring application you can add them to the ENTRYPOINT:
Example of ENTRYPOINT with more Spring Boot parameters:

ENTRYPOINT ["java", \ 
  "-Dspring.profiles.active=dev", \ 
  "-XX:InitialRAMPercentage=50", \ 
  "-XX:MaxRAMPercentage=80", \ 
  "-jar", "/opt/myApp.jar"] 

WebApp built by Marco using SpringBoot 3.2.4 and Java 21, in a Server in Switzerland