Every frontend developer should understand Docker basics. At Google, all our CI/CD pipelines run in containers.
Why Docker?
"Works on my machine" is eliminated. Docker packages your app with its exact environment — Node version, OS libraries, everything.
Dockerfile for a React App
# Build stage
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Multi-Stage Builds
The Dockerfile above uses multi-stage builds. The first stage (900MB+ with node_modules) builds the app. The second stage (25MB with nginx) serves it. Your final image is tiny.
Essential Commands
# Build image
docker build -t my-app .
# Run container
docker run -p 3000:80 my-app
# Development with hot reload
docker run -v $(pwd)/src:/app/src -p 3000:5173 my-app-dev
# Docker Compose for full stack
docker-compose up -dDocker Compose for Full Stack
version: "3.8"
services:
frontend:
build: ./frontend
ports: ["3000:80"]
depends_on: [api]
api:
build: ./api
ports: ["8080:8080"]
environment:
DATABASE_URL: postgres://db:5432/app
depends_on: [db]
db:
image: postgres:16-alpine
volumes: [pgdata:/var/lib/postgresql/data]
environment:
POSTGRES_DB: app
volumes:
pgdata:Tips for Frontend Devs
- Use
.dockerignoreto exclude node_modules, .git, and build artifacts - Pin exact versions in FROM (not
latest) - Use
npm cinotnpm installfor deterministic builds - Layer ordering matters: copy package.json first, then source code
- Use multi-stage builds to keep production images small