skip to Main Content

I’m working on setting up an application with a React front-end and a Spring Boot back-end, alongside PgAdmin and PostgreSQL. I’ve successfully set up everything in Docker containers, including hot-reload for the front-end. However, setting up hot reload for the back-end has proven to be challenging.

I followed https://panigrahi-pratap.medium.com/spring-boot-live-reload-with-docker-db585fcca37f guide thoroughly, but still haven’t achieved my goal. Changes made to the Spring Boot code aren’t reflected in the running application within the Docker container.

Here is my setup:

Dockerfile in /backend2

# Use Maven with OpenJDK 21
FROM maven:3.9.9 AS build

# Set the working directory inside the container
WORKDIR /app

# Copy the pom.xml and the source code to the working directory
COPY pom.xml .
COPY src ./src

# Package the application (skip tests if necessary)
RUN mvn clean package -DskipTests

# Second stage: create a lightweight image for running the application
FROM eclipse-temurin:21-jdk-alpine

# Set the working directory inside the container
WORKDIR /app

# Copy the JAR file from the build stage to the runtime stage
COPY --from=build /app/target/*.jar /app/app.jar

# Expose the port the application runs on
EXPOSE 8080

# Run the Spring Boot application with DevTools enabled
ENTRYPOINT ["java", 
            "-Dspring.devtools.restart.enabled=true", 
            "-Dspring.devtools.livereload.enabled=true", 
            "-Dspring.devtools.remote.secret=mysecret", 
            "-jar", "/app/app.jar"]

application.properties in backend2

spring.application.name=backend2
spring.devtools.remote.secret=mysecret

Pom.xml in backend2:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.albion_tooling</groupId>
    <artifactId>backend2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>backend2</name>
    <description>backend2</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>21</java.version>
    </properties>
    <dependencies>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludeDevtools>false</excludeDevtools>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

docker-compose.yml

version: '3.9'

services:

  postgres:
    container_name: database
    image: postgres:16.4
    hostname: localhost
    ports:
      - "${POSTGRES_PORT}:5432"
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - albion_tooling_postgres-data:/var/lib/postgresql/data
      - ./database/docker-entrypoint-initdb.d/:/docker-entrypoint-initdb.d/
    restart: unless-stopped

  pgadmin:
    container_name: pgadmin
    image: dpage/pgadmin4
    depends_on:
      - postgres
    ports:
      - "${PGADMIN_PORT}:80"
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
    restart: unless-stopped

  frontend:
    container_name: frontend
    build:
      context: ./frontend
      dockerfile: Dockerfile
    environment:
      CHOKIDAR_USEPOLLING: "true"
    ports:
      - "${FRONTEND_PORT}:3000"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    restart: unless-stopped

  backend:
    container_name: backend
    build:
      context: ./backend2
      dockerfile: Dockerfile
    ports:
      - "${BACKEND_PORT}:8080"
    volumes:
      - ./backend2/src:/app/src
      - ./backend2/target:/app/target
      - ~/.m2:/root/.m2
    environment:
      - JAVA_OPTS=-Dspring.devtools.restart.enabled=true -Dspring.devtools.livereload.enabled=true
      - SPRING_PROFILES_ACTIVE=dev
    depends_on:
      - postgres
    restart: unless-stopped

volumes:
  albion_tooling_postgres-data:

2

Answers


  1. Chosen as BEST ANSWER

    I was able to resolve the issue on my own by making a few adjustments to my Docker setup. Specifically, I mapped the entire backend directory into the Docker container, which simplified the Dockerfile for the backend.

    Backend Dockerfile:

    # Use Maven with OpenJDK 21
    FROM maven:3.9.9 AS build
    
    # Set the working directory inside the container
    WORKDIR /app
    
    # Copy the pom.xml and the source code to the working directory
    COPY . .
    
    # Run the Spring Boot application with DevTools enabled
    CMD ["mvn", "spring-boot:run", "-Dspring-boot.run.profiles=dev", 
         "-Dspring.devtools.restart.enabled=true", 
         "-Dspring.devtools.livereload.enabled=true", 
         "-Dspring.devtools.remote.secret=mysecret"]
    

    docker-compose (only backend related)

      backend:
        container_name: backend
        build:
          context: ./backend2
          dockerfile: Dockerfile
        ports:
          - "${BACKEND_PORT}:8080"
        volumes:
          - ./backend2:/app
          - ~/.m2:/root/.m2
        environment:
          - JAVA_OPTS=-Dspring.devtools.restart.enabled=true -Dspring.devtools.livereload.enabled=true
          - SPRING_PROFILES_ACTIVE=dev
        depends_on:
          - postgres
        restart: unless-stopped
    

    The key was enabling hot-reload in IntelliJ. To do this, go to File > Settings > Build, Execution, Deployment > Compiler and check the option for "Build project automatically". With this setting enabled, the container will automatically restart whenever changes are made to the code.


  2. I had the same requirement as you, as I was hoping to run my app in development as similar as possible to how it runs in production. Using the example provided in this article by JetBrains, I was able to (a) run/debug the app from Intellij, (b) trigger breakpoints (and switch to Intellij when this happened), (c) recompile automatically after changes when switching back to the browser, and (d) trigger LiveReload in the browser.

    Aside from the "Build project automatically" setting, I also had the following additional configuration in docker-compose.yml:

    app:
      env_file:
      - ./.env
      ports:
      - "35729:35729"
      # ...
    

    This allowed me to put all of my environment in the .env file, making it similar to how the app gets configured in production but allowing local customisations:

    SPRING_PROFILES_ACTIVE=dev
    SPRING_DEVTOOLS_RESTART_ENABLED=true
    

    The spring.devtools.restart.enabled setting was required in .env because Spring Boot turns this off, as noted in the documentation:

    Developer tools are automatically disabled when running a fully packaged application. If your application is launched from java -jar or if it is started from a special classloader, then it is considered a “production application”. You can control this behavior by using the spring.devtools.restart.enabled system property.

    The additional ports configuration ensured that the LiveReload instance enabled by Spring Boot could be seen outside the container when accessing the running app at http://localhost:8080.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search