Writing a Dockerfile

A Dockerfile is a step-by-step recipe for building your application image.

Basic structure

dockerfile
# Start from a base image
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy dependency files first (for caching)
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the source
COPY . .

# Expose the port your app uses
EXPOSE 3000

# Command to run the app
CMD ["node", "server.js"]

Building and running

bash
# Build the image
docker build -t my-app .

# Run it
docker run -d -p 3000:3000 my-app

# Check logs
docker logs <container-id>

Layer caching

Docker caches each step. Order matters:

dockerfile
# GOOD: dependencies change less often than source
COPY package*.json ./
RUN npm install
COPY . .

# BAD: reinstalls dependencies every time source changes
COPY . .
RUN npm install

Multi-stage builds

Keep your production image small:

dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build

# Production stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

This can reduce your image from 1GB+ to under 200MB.