Docker provides a consistent environment for your React application from development through production. Here’s a comprehensive guide to containerizing your React app effectively:
Basic Docker Setup
1. Minimal Dockerfile (Development)
# Stage 1: Build the application
FROM node:18-alpine as builder
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build
# Stage 2: Serve the application
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
2. Corresponding nginx.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Advanced Configurations
1. Multi-stage Production Build
# Stage 1: Build the application
FROM node:18-alpine as builder
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production
COPY . .
RUN yarn build
# Stage 2: Optimize the build
FROM node:18-alpine as optimizer
WORKDIR /app
RUN npm install -g serve
COPY --from=builder /app/build ./build
# Stage 3: Final lightweight image
FROM alpine:latest
RUN apk add --no-cache nodejs
COPY --from=optimizer /usr/local/lib/node_modules ./usr/local/lib/node_modules
COPY --from=optimizer /app/build ./app/build
ENV PATH="/usr/local/lib/node_modules/.bin:${PATH}"
EXPOSE 3000
CMD ["serve", "-s", "build", "-l", "3000"]
2. Docker Compose for Full Stack
version: '3.8'
services:
frontend:
build:
context: .
dockerfile: Dockerfile.prod
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- REACT_APP_API_URL=http://backend:5000
depends_on:
- backend
backend:
image: your-backend-image
ports:
- "5000:5000"
environment:
- DB_HOST=database
- DB_PORT=5432
database:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=yourpassword
volumes:
postgres_data:
Environment-Specific Configurations
1. Dynamic Environment Variables
# Stage 1: Build with ARG to create env-specific build
FROM node:18-alpine as builder
ARG REACT_APP_API_URL
ENV REACT_APP_API_URL=$REACT_APP_API_URL
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build
# ... rest of Dockerfile
Build command:
docker build --build-arg REACT_APP_API_URL=https://api.example.com -t your-image .
2. Runtime Environment Variables
# Using entrypoint script
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
entrypoint.sh:
#!/bin/sh
# Set environment variables
export REACT_APP_API_URL=${API_URL:-http://localhost:5000}
# Execute CMD
exec "$@"
Optimization Techniques
1. Caching Dependencies
# Improved caching layer
FROM node:18-alpine as builder
WORKDIR /app
# Copy only files needed for dependency installation
COPY package.json yarn.lock tsconfig.json ./
# Install dependencies (including devDependencies)
RUN yarn install --frozen-lockfile
# Copy remaining files
COPY . .
# Build the application
RUN yarn build
# ... rest of Dockerfile
2. Multi-architecture Builds
# Build for multiple platforms
docker buildx build --platform linux/amd64,linux/arm64 -t your-image:latest .
Security Best Practices
1. Non-root User
FROM nginx:alpine
# Add non-root user
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
# Copy files and change ownership
COPY --from=builder --chown=appuser:appgroup /app/build /usr/share/nginx/html
COPY --chown=appuser:appgroup nginx.conf /etc/nginx/conf.d/default.conf
USER appuser
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]
2. Security Headers in Nginx
server {
# ... existing config
# Security headers
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "geolocation=(), microphone=()";
# ... rest of config
}
Development-Specific Setup
1. Docker Compose for Development
version: '3.8'
services:
frontend:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./:/app
- /app/node_modules
environment:
- CHOKIDAR_USEPOLLING=true # For file watching in Docker
command: yarn start
2. Development Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
EXPOSE 3000
CMD ["yarn", "start"]
Deployment Strategies
1. Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: react-app
spec:
replicas: 3
selector:
matchLabels:
app: react-app
template:
metadata:
labels:
app: react-app
spec:
containers:
- name: react-app
image: your-image:latest
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: react-app
spec:
selector:
app: react-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
2. CI/CD Pipeline Example
# GitHub Actions workflow
name: Build and Deploy
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: your-username/your-image:latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Install kubectl
uses: azure/setup-kubectl@v3
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/react-app react-app=your-username/your-image:latest
Monitoring and Maintenance
1. Health Checks
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost/ || exit 1
2. Logging Configuration
# For nginx-based images
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
Best Practices Checklist
- [ ] Use multi-stage builds to minimize image size
- [ ] Implement proper layer caching for dependencies
- [ ] Set up non-root user for production containers
- [ ] Configure proper security headers
- [ ] Use environment variables for configuration
- [ ] Set resource limits in production
- [ ] Implement health checks
- [ ] Regularly update base images
- [ ] Scan images for vulnerabilities
- [ ] Document your Docker setup
