Skip to main content

Dockerfile Configuration Details

Overview

Each service in the Movie List Application has its own Dockerfile that defines how to build the container image. These Dockerfiles use different base images and build processes optimized for their specific technology stack.

Frontend Dockerfile (React Application)

Multi-Stage Build Process

# Stage 1: Build the React application
FROM node:16 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Stage 2: Serve with Nginx
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]

Stage 1: Build Stage

Base Image: node:16

  • Official Node.js image with version 16
  • Includes npm and all Node.js development tools
  • Used only for building the React application

Build Process:

  1. WORKDIR /app: Sets working directory inside container
  2. *COPY package.json ./**: Copies package.json and package-lock.json first
  3. RUN npm install: Installs all dependencies (leverages Docker layer caching)
  4. COPY . .: Copies all application source code
  5. RUN npm run build: Creates optimized production build in /app/build

Why Copy package.json First?

  • Docker Layer Caching: If source code changes but dependencies don't, Docker reuses the npm install layer
  • Faster Builds: Only re-installs dependencies when package.json changes
  • Efficiency: Common Docker best practice for Node.js applications

Stage 2: Production Stage

Base Image: nginx:alpine

  • Lightweight Alpine Linux with Nginx web server
  • Much smaller than Node.js image (only ~23MB vs ~900MB)
  • Perfect for serving static files

Production Setup:

  1. COPY --from=build: Copies built React app from previous stage
  2. EXPOSE 3000: Documents that container listens on port 3000
  3. CMD: Starts Nginx in foreground mode

Benefits of Multi-Stage Build:

  • Smaller Image Size: Final image only contains Nginx and built files
  • Security: No development tools in production image
  • Performance: Nginx is optimized for serving static content
  • Clean Separation: Build tools separate from runtime environment

Backend Dockerfile (Spring Boot Application)

FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/MovieApi.jar /app/MovieApi.jar
CMD ["java", "-jar", "/app/MovieApi.jar"]

Simple Single-Stage Build

Base Image: openjdk:17-jdk-slim

  • Official OpenJDK 17 image with slim variant
  • Includes Java Runtime Environment (JRE) and Java Development Kit (JDK)
  • Slim variant reduces image size while keeping essential tools

Build Process:

  1. WORKDIR /app: Sets working directory
  2. COPY target/MovieApi.jar: Copies pre-built JAR file from Maven build
  3. CMD: Runs the Spring Boot application using Java

Pre-Build Requirement

Important: This Dockerfile expects the JAR file to already exist in target/MovieApi.jar

Maven Build Process:

# This must be run before Docker build
mvn clean package

Maven Configuration (pom.xml):

<build>
<finalName>MovieApi</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

Alternative Multi-Stage Approach

A more complete Dockerfile might include the Maven build:

# Build stage
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests

# Runtime stage
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /app/target/MovieApi.jar /app/MovieApi.jar
CMD ["java", "-jar", "/app/MovieApi.jar"]

Database Container (MongoDB)

No Custom Dockerfile: Uses official MongoDB image directly

mongo-local:
image: mongo:latest

Why No Custom Dockerfile?

  • MongoDB image is already optimized
  • Configuration handled through environment variables and volumes
  • Initialization scripts mounted as volumes
  • Keeps setup simple and maintainable

Build Context and .dockerignore

Frontend Build Context

Context: ./movie-list-front

Typical .dockerignore:

node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.cache

Backend Build Context

Context: ./movie-api

Typical .dockerignore:

target/
.mvn/
mvnw
mvnw.cmd
.git
.gitignore
README.md

Image Optimization Strategies

Frontend Optimizations

  1. Multi-Stage Build: Reduces final image size by 95%
  2. Alpine Base: Uses minimal Linux distribution
  3. Layer Caching: Optimizes dependency installation
  4. Static File Serving: Nginx efficiently serves React build

Backend Optimizations

  1. Slim JDK: Uses minimal Java runtime
  2. Single JAR: Spring Boot creates executable JAR with all dependencies
  3. No Build Tools: Runtime image doesn't include Maven
  4. Fast Startup: Spring Boot optimized for container environments

Container Runtime Behavior

Frontend Container

  • Nginx Process: Runs as PID 1 in container
  • Static Files: Serves React build from /usr/share/nginx/html
  • Port 80: Nginx default port (mapped to 3000 externally)
  • Configuration: Uses default Nginx configuration

Backend Container

  • Java Process: Spring Boot application runs as PID 1
  • Embedded Tomcat: Web server included in Spring Boot
  • Port 8080: Spring Boot default port
  • Profiles: Uses environment variable to select configuration

Security Considerations

Frontend Security

  • Non-Root User: Nginx runs as nginx user (not root)
  • Read-Only Files: Static files don't need write permissions
  • Minimal Attack Surface: Only Nginx and static files in final image

Backend Security

  • JVM Security: OpenJDK includes security updates
  • Application Security: Spring Boot security features
  • Environment Variables: Sensitive data through environment variables
  • Network Isolation: Container network provides isolation

Development vs Production

Development Differences

  • Volume Mounts: Source code mounted for hot reloading
  • Debug Ports: Additional ports for debugging
  • Development Tools: May include additional debugging tools

Production Differences

  • Optimized Builds: Production React build with minification
  • Health Checks: Container health monitoring
  • Resource Limits: CPU and memory constraints
  • Logging: Structured logging for monitoring

Benefits of This Docker Strategy

  1. Technology-Specific Optimization: Each Dockerfile optimized for its stack
  2. Size Efficiency: Multi-stage builds minimize image sizes
  3. Build Speed: Layer caching speeds up development
  4. Security: Minimal runtime images reduce attack surface
  5. Portability: Consistent behavior across environments
  6. Maintainability: Simple, focused Dockerfiles