This project includes a production-ready Dockerfile to build and run the application inside a container. It uses a multi-stage build to keep the final image small and secure.
-
Multi-stage build
- First stage compiles the app (using a full JDK and Maven).
- Second stage runs the app (using a smaller JRE image).
- This reduces the final image size and attack surface.
-
Dependency caching
- The build copies the Maven wrapper and
pom.xmlbefore source code, then pre-fetches dependencies. - This lets Docker reuse layers and speeds up rebuilds when only source changes.
- The build copies the Maven wrapper and
-
JRE-only runtime
- The runtime image uses a JRE (no compilers/tools), making it smaller and safer for production.
-
Non-root user
- The container runs as a non-root user for the principle of least privilege.
-
Runtime configuration via env vars
- JVM flags and Spring profiles are passed via environment variables, so the same image works for dev/staging/prod.
-
# syntax=docker/dockerfile:1- Enables newer Dockerfile syntax and features.
-
FROM eclipse-temurin:21-jdk AS build- Uses the official Java 21 JDK image to compile the application.
-
WORKDIR /app- Sets the working directory inside the image.
-
COPY pom.xml mvnw ./andCOPY .mvn .mvn- Copies Maven wrapper and POM to enable dependency caching.
-
RUN ./mvnw -q -e -DskipTests dependency:go-offline- Pre-fetches dependencies to improve subsequent build times.
-
COPY src src- Copies the application source code.
-
RUN ./mvnw -q -DskipTests package- Builds the application JAR (tests should still run in CI before image build).
-
FROM eclipse-temurin:21-jre- Smaller JRE-only runtime image for production.
-
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError"- Sets JVM flags to respect container memory limits and exit on OOM.
-
WORKDIR /app- Working directory for the runtime container.
-
RUN useradd -r -u 10001 appuserandUSER appuser- Creates and switches to a non-root user for security.
-
COPY --from=build /app/target/*.jar app.jar- Copies the built application JAR from the build stage.
-
EXPOSE 8080- Documents the port the application listens on.
-
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar --spring.profiles.active=\${SPRING_PROFILES_ACTIVE:-prod}"]- Starts the application; Spring profile can be set via
SPRING_PROFILES_ACTIVE(defaults toprod).
- Starts the application; Spring profile can be set via
-
Build image:
docker build -t your-org/your-app:latest .
-
Run (development):
docker run -p 8080:8080 -e SPRING_PROFILES_ACTIVE=dev your-org/your-app:latest
-
Run (production-like):
docker run -p 8080:8080 -e SPRING_PROFILES_ACTIVE=prod -e JAVA_OPTS="-Xms256m -Xmx512m" your-org/your-app:latest
- Keep secrets out of images; pass them as environment variables or use a secrets manager.
- Use a read-only filesystem and run as non-root where possible.
- Add liveness/readiness probes (Kubernetes) and set resource requests/limits.
- Pin base image versions and regularly update for security patches.
- Keep the Docker build context small by using a
.dockerignore.