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:
- WORKDIR /app: Sets working directory inside container
- *COPY package.json ./**: Copies package.json and package-lock.json first
- RUN npm install: Installs all dependencies (leverages Docker layer caching)
- COPY . .: Copies all application source code
- 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:
- COPY --from=build: Copies built React app from previous stage
- EXPOSE 3000: Documents that container listens on port 3000
- 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:
- WORKDIR /app: Sets working directory
- COPY target/MovieApi.jar: Copies pre-built JAR file from Maven build
- 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
- Multi-Stage Build: Reduces final image size by 95%
- Alpine Base: Uses minimal Linux distribution
- Layer Caching: Optimizes dependency installation
- Static File Serving: Nginx efficiently serves React build
Backend Optimizations
- Slim JDK: Uses minimal Java runtime
- Single JAR: Spring Boot creates executable JAR with all dependencies
- No Build Tools: Runtime image doesn't include Maven
- 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
- Technology-Specific Optimization: Each Dockerfile optimized for its stack
- Size Efficiency: Multi-stage builds minimize image sizes
- Build Speed: Layer caching speeds up development
- Security: Minimal runtime images reduce attack surface
- Portability: Consistent behavior across environments
- Maintainability: Simple, focused Dockerfiles