I am new to Docker and am attempting to have a single compose.yaml for my spring-react app. I created individual Dockerfiles for the backend (spring) and frontend (react) using docker init
and some stackoverflow threads during debugging.
.
├── backend-spring
│ ├── ...
│ ├── Dockerfile
├── frontend-react
│ ├── ...
│ └── Dockerfile
├── ...
└── compose.yaml
I am trying to get the app working locally before I attempt deploying it onto AWS.
When I run docker compose up -d
in my root directory, it states that both the backend and frontend docker containers are created. However, when I run docker container ls
, only the spring backend container appears. This is an issue as I am not able to access my react app via localhost (since it is not running).
Image of docker compose up -d
result
The frontend-react container does not appear when I run docker container ls
– why is this so?
I suspect that the issue maybe the frontend Dockerfile, and I have tinkered with the RUN/CMD commands of my frontend-react Dockerfile to no avail.
The React app runs when I manually run serve -s build
or npm start
in the directory, so the app has no issues. I would like the end result of just running one docker compose up -d
command to have both my frontend and backend running. I have verified that I can access my spring backend api on localhost after the docker compose command.
Greatly appreciate your help.
compose.yaml
services:
backend:
build: backend-spring
restart: always
ports:
- 8080:8080
frontend:
build:
context: frontend-react
# target: development # defines the stage to build as defined inside a multi-stage Dockerfile. Since backend-spring Dockerfile is only single stage, commented this out.
stdin_open: true
tty: true
ports:
- 3000:3000
volumes:
- /frontend-react/src
- /frontend-react/node_modules
depends_on:
- backend
Frontend DockerFile
# syntax=docker/dockerfile:1
# Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/
ARG NODE_VERSION=20.13.1
################################################################################
# Use node image for base image for all stages.
FROM node:${NODE_VERSION}-alpine as base
# Set working directory for all build stages.
WORKDIR /usr/src/app
################################################################################
# Create a stage for installing production dependecies.
FROM base as deps
# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.npm to speed up subsequent builds.
# Leverage bind mounts to package.json and package-lock.json to avoid having to copy them
# into this layer.
RUN --mount=type=bind,source=package.json,target=package.json
--mount=type=bind,source=package-lock.json,target=package-lock.json
--mount=type=cache,target=/root/.npm
npm ci --omit=dev
################################################################################
# Create a stage for building the application.
FROM deps as build
# Download additional development dependencies before building, as some projects require
# "devDependencies" to be installed to build. If you don't need this, remove this step.
RUN --mount=type=bind,source=package.json,target=package.json
--mount=type=bind,source=package-lock.json,target=package-lock.json
--mount=type=cache,target=/root/.npm
npm ci
# Copy the rest of the source files into the image.
COPY . .
# Install `serve` to run the application.
RUN npm install -g serve
# Run the build script.
RUN npm run build --omit=dev
################################################################################
# Create a new stage to run the application with minimal runtime dependencies
# where the necessary files are copied from the build stage.
FROM base as final
# Use production node environment by default.
ENV NODE_ENV production
# Run the application as a non-root user.
USER node
# Copy package.json so that package manager commands can be used.
COPY package.json .
# Copy the production dependencies from the deps stage and also
# the built application from the build stage into the image.
COPY --from=deps /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/build ./build
## Expose the port that the application listens on.
EXPOSE 3000
# Run application # switch between npm start (debug) and serve (for production).
CMD [ "npm", "start" ]
#CMD serve -s build #does not work
#RUN serve -s build #does not work
Backend DockerFile
# syntax=docker/dockerfile:1
# Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/
################################################################################
# Create a stage for resolving and downloading dependencies.
FROM eclipse-temurin:17-jdk-jammy as deps
WORKDIR /build
# Copy the mvnw wrapper with executable permissions.
COPY --chmod=0755 mvnw mvnw
COPY .mvn/ .mvn/
# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.m2 so that subsequent builds don't have to
# re-download packages.
RUN --mount=type=bind,source=pom.xml,target=pom.xml
--mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests
################################################################################
# Create a stage for building the application based on the stage with downloaded dependencies.
# This Dockerfile is optimized for Java applications that output an uber jar, which includes
# all the dependencies needed to run your app inside a JVM. If your app doesn't output an uber
# jar and instead relies on an application server like Apache Tomcat, you'll need to update this
# stage with the correct filename of your package and update the base image of the "final" stage
# use the relevant app server, e.g., using tomcat (https://hub.docker.com/_/tomcat/) as a base image.
FROM deps as package
WORKDIR /build
COPY ./src src/
RUN --mount=type=bind,source=pom.xml,target=pom.xml
--mount=type=cache,target=/root/.m2
./mvnw package -DskipTests &&
mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar
################################################################################
# Create a stage for extracting the application into separate layers.
# Take advantage of Spring Boot's layer tools and Docker's caching by extracting
# the packaged application into separate layers that can be copied into the final stage.
# See Spring's docs for reference:
# https://docs.spring.io/spring-boot/docs/current/reference/html/container-images.html
FROM package as extract
WORKDIR /build
RUN java -Djarmode=layertools -jar target/app.jar extract --destination target/extracted
################################################################################
# Create a new stage for running the application that contains the minimal
# runtime dependencies for the application. This often uses a different base
# image from the install or build stage where the necessary files are copied
# from the install stage.
#
# The example below uses eclipse-turmin's JRE image as the foundation for running the app.
# By specifying the "17-jre-jammy" tag, it will also use whatever happens to be the
# most recent version of that tag when you build your Dockerfile.
# If reproducability is important, consider using a specific digest SHA, like
# eclipse-temurin@sha256:99cede493dfd88720b610eb8077c8688d3cca50003d76d1d539b0efc8cca72b4.
FROM eclipse-temurin:17-jre-jammy AS final
# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
ARG UID=10001
RUN adduser
--disabled-password
--gecos ""
--home "/nonexistent"
--shell "/sbin/nologin"
--no-create-home
--uid "${UID}"
appuser
USER appuser
# Copy the executable from the "package" stage.
COPY --from=extract build/target/extracted/dependencies/ ./
COPY --from=extract build/target/extracted/spring-boot-loader/ ./
COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
COPY --from=extract build/target/extracted/application/ ./
EXPOSE 8080
ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]
ALVIN is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.