Running ReactJS + Spring Boot Application with Docker Compose

docker Sep 09, 2020

Dockerfile for Spring Boot Application

FROM openjdk:11.0.4-jre
COPY build/libs/*.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

Running gradle build generates the artifact under the build/libs directory. Spring boot application exposes REST endpoint on port 8080, and lets imagine a scenario that endpoints starts with the /api/* path.

Dockerfile for React Application

React project can be build by yarn build or npm run build. It should be deployed on nginx server. So we can use ngnix as the base image. We can configure the nginx with the following config:

server {

  listen 80;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  location /api/ {
      proxy_pass http://backend:8080;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_cache_bypass $http_upgrade;
   }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }

}
nginx.conf

The paths starts with / will be redirected to nginx path and if it start with /api/ it is redirected to backend. We need to have this config as both backend and frontend is running on the same server.  Now we can copy the build directory to nginx folder and and configuration to /etc/nginx/ folder.

FROM nginx:1.16.0-alpine
COPY --from=build /app/build /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx/nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

We can go one step further and build the project within container as well, and we can do in 2 steps in a Dockerfile:

# build environment
FROM node:12.4.0-alpine as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install --silent
RUN npm install react-scripts@3.0.1 -g --silent
COPY . /app
RUN npm run build

# production environment
FROM nginx:1.16.0-alpine
COPY --from=build /app/build /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx/nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Docker-compose file

version: "3"
services:
  db:
    image: "postgres"
    container_name: "postgres"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=root
      - POSTGRES_DB=postgres
    ports:
      - "5432:5432"
    volumes:
      - ./db-data:/var/lib/postgresql/data

  backend:
    image: "turkogluc/backend"
    container_name: "backend"
    ports:
      - "8080:8080"
    depends_on:
      - db

  frontend:
    image: "turkogluc/frontend"
    container_name: "frontend"
    ports:
      - "80:80"
    depends_on:
      - backend

Note that all containers work in the same network, and they can communicate with each by using the service names as db, backend or frontend. For example in the spring application database connection string should be as jdbc:postgresql://db:5432/postgres. And React application can make the rest calls to http://backend/api/* . Now we can run all the services:

docker-compose up
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.