Docker Multi-Stage Build
This project uses a multi-stage Docker build to create an optimized production image. The build process is divided into three stages: frontend build, backend build, and final production assembly.Why Multi-Stage Builds?
Multi-stage builds allow you to:- Keep the final image size small by excluding build tools and dependencies
- Separate build-time dependencies from runtime dependencies
- Improve security by reducing the attack surface
- Organize the build process into logical stages
Complete Dockerfile
Build Stages Explained
Stage 1: Frontend Build
The first stage builds the React frontend application.What happens here:
- Uses
node:18-alpineas the base image (lightweight Alpine Linux) - Names this stage
frontend-buildfor later reference - Copies
package.jsonandpackage-lock.jsonfirst (Docker layer caching) - Installs all frontend dependencies with
npm install - Copies the entire client source code
- Runs
npm run buildto create optimized production assets inclient/build
The frontend build creates static files (HTML, CSS, JS) that will be served by the Express backend.
Stage 2: Backend Build
The second stage prepares the Express backend.What happens here:
- Uses a fresh
node:18-alpineimage for isolation - Names this stage
backend-build - Copies backend package files and installs dependencies
- Copies all backend source code (including
index.js)
Each stage starts fresh, ensuring clean separation between frontend and backend builds.
Stage 3: Production Assembly
The final stage creates the minimal production image.What happens here:
- Uses a final fresh
node:18-alpineimage (only this layer becomes the final image) - Copies the complete backend (code + dependencies) from
backend-buildstage - Copies only the built frontend files from
frontend-buildstage - Exposes port
5000for the Express server - Sets the startup command to run the Node.js server
Building the Image
To build the Docker image locally:- Docker executes Stage 1 (frontend build)
- Docker executes Stage 2 (backend build)
- Docker assembles Stage 3 (production image)
- Final image is tagged as
react-express-app
Running the Container
Once built, run the container:-d- Run in detached mode (background)-p 5000:5000- Map host port 5000 to container port 5000--name react-express-app- Give the container a friendly namereact-express-app(last argument) - The image to run
Port Configuration
The application exposes port 5000, which is the Express server port. The React frontend is served as static files from the Express server, so only one port is needed.
Application Startup
node server/index.js, which:
- Starts the Express server
- Serves the React build files from
server/client/build - Handles API requests on port 5000
Benefits of This Approach
Small Image Size
Only production files are included, no build tools or source code
Fast Builds
Docker layer caching speeds up rebuilds when dependencies don’t change
Security
Minimal attack surface with only necessary runtime files
Clean Separation
Frontend and backend builds are isolated and organized
Next Steps
Docker Compose
Learn how to orchestrate the container with Docker Compose
Jenkins Pipeline
Automate deployment with Jenkins CI/CD