Package your app into a container — has everything it needs, runs anywhere.
FROM node:18-alpine # Base image
WORKDIR /app # Working folder inside container
COPY . . # Copy your files in
RUN npm install # Install dependencies
EXPOSE 3000 # Declare port
CMD ["node", "src/index.js"] # Start the app
# Stage 1: BUILD
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build # Compiles React → HTML/CSS/JS
# Stage 2: SERVE
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Why 2 stages? Build stage compiles code, then gets thrown away. Only the final HTML/CSS/JS is served — smaller, cleaner, safer.
daemon off;keeps Nginx in foreground so Docker knows the container is still alive.
FROM # 1. Always first
WORKDIR # 2. Set folder
COPY # 3. Copy files
RUN # 4. Install / build
EXPOSE # 5. Declare port
CMD # 6. Always last
# ✅ Good (cache friendly)
COPY package*.json ./ # rarely changes → cached
RUN npm install
COPY . . # your code changes often → at the bottom
# ❌ Bad (slow — reruns npm install on every code change)
COPY . .
RUN npm install