Front-end developers often think Docker is just a back-end tool. But when you're working with React.js, Docker Compose can do much more than spin up services — it can stabilize your workflow, speed up builds, isolate environments, and reduce setup friction for every team member.
Let’s break down why each of these techniques is worth adding to your workflow.
1. Use Profiles to Run Optional Tools
Why: Don't run Storybook, mock APIs, or test servers unless needed. Profiles let you selectively launch tools.
docker-compose.yml
version: "3.9"
services:
react-app:
build: .
ports:
- "3000:3000"
storybook:
image: node:18-alpine
working_dir: /app
command: yarn storybook
volumes:
- .:/app
ports:
- "6006:6006"
profiles:
- dev
Run Storybook only when needed:
docker compose --profile dev up
2. Use .env
and --env-file
for Clean Config
Why: Manage multiple environments without touching code or Compose files.
.env.development
REACT_APP_API_URL=http://localhost:4000
.env.production
REACT_APP_API_URL=https://api.example.com
docker-compose.yml
services:
react-app:
build:
context: .
args:
- REACT_APP_API_URL=${REACT_APP_API_URL}
environment:
- REACT_APP_API_URL=${REACT_APP_API_URL}
Switch environments:
docker compose --env-file .env.production up --build
3. Use Cache and Multi-Stage Dockerfiles
Why: Cut build times and produce clean images without dev dependencies.
Dockerfile
# Stage 1: Build
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
FROM nginx:1.25-alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
docker-compose.yml
services:
react-app:
build:
context: .
dockerfile: Dockerfile
cache_from:
- myorg/react-app:latest
image: myorg/react-app:latest
ports:
- "80:80"
4. Add Healthchecks for Dependent Services
Why: Ensure services (like APIs) are ready before the React app loads.
docker-compose.yml
services:
react-app:
image: myorg/react-app:latest
depends_on:
backend:
condition: service_healthy
ports:
- "80:80"
backend:
image: myorg/api:latest
healthcheck:
test: curl --fail http://localhost:4000/health || exit 1
interval: 10s
retries: 5
5. Name Containers and Manage Logs
Why: Easier debugging and log management.
docker-compose.yml
services:
react-app:
container_name: react-frontend
image: myorg/react-app:latest
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Access logs:
docker logs react-frontend
6. Use Named Volumes for node_modules
Why: Avoid cross-platform mount issues and speed up builds.
docker-compose.yml
services:
react-app:
build:
context: .
volumes:
- react_node_modules:/app/node_modules
- ./src:/app/src
ports:
- "3000:3000"
volumes:
react_node_modules:
7. Split Files for Modular Environments
Why: Cleanly separate dev, staging, and prod configs.
docker-compose.yml
(base)
services:
react-app:
image: myorg/react-app:latest
docker-compose.override.yml
(dev)
services:
react-app:
build:
context: .
ports:
- "3000:3000"
volumes:
- ./src:/app/src
Start in dev:
docker compose up
Start in prod:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up
Final Thoughts
These 7 Docker Compose features aren't just tricks—they're front-end engineering best practices for React developers. They reduce setup friction, speed up your workflow, and make it easier to scale your app from dev to production.
Upgrade your Compose game and bring order to your React projects.