What is a Dockerfile?
A Dockerfile is a text file containing a set of instructions that Docker uses to build an image. Each instruction creates a new layer in the image, and Docker caches these layers to speed up subsequent builds.
Basic Dockerfile Instructions
FROM — Set Base Image
# Every Dockerfile starts with FROM
FROM node:20-alpine
# Multi-stage build (more on this later)
FROM node:20-alpine AS builder
WORKDIR — Set Working Directory
# Set the working directory inside the container
WORKDIR /app
# All subsequent commands run relative to /app
COPY and ADD — Copy Files
# Copy files from host to container
COPY package.json package-lock.json ./
COPY src/ ./src/
# Copy everything (use .dockerignore to exclude files)
COPY . .
# ADD can also extract tar archives and fetch URLs
ADD archive.tar.gz /app/
# Prefer COPY over ADD for simple file copying
RUN — Execute Commands
# Install dependencies
RUN npm ci --production
# Run multiple commands (creates one layer)
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
ENV — Set Environment Variables
# Set environment variables
ENV NODE_ENV=production
ENV PORT=3000
# Use variables in subsequent instructions
RUN echo "Running in $NODE_ENV mode"
EXPOSE — Declare Ports
# Document which ports the container listens on
EXPOSE 3000
EXPOSE 80 443
# This is documentation only — you still need -p when running
CMD and ENTRYPOINT — Define Startup Command
# CMD — default command (can be overridden)
CMD ["node", "server.js"]
CMD ["npm", "start"]
# ENTRYPOINT — always runs (CMD becomes arguments)
ENTRYPOINT ["node"]
CMD ["server.js"]
# Runs: node server.js
# Shell form vs Exec form
CMD node server.js # Shell form (runs in /bin/sh -c)
CMD ["node", "server.js"] # Exec form (preferred)
Complete Example: Node.js App
# Use official Node.js Alpine image
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Copy dependency files first (better caching)
COPY package.json package-lock.json ./
# Install dependencies
RUN npm ci --production
# Copy application code
COPY . .
# Set environment variables
ENV NODE_ENV=production
ENV PORT=3000
# Expose the port
EXPOSE 3000
# Start the application
CMD ["node", "server.js"]
The .dockerignore File
Like .gitignore, the .dockerignore file prevents unnecessary files
from being sent to the Docker daemon during builds.
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
Dockerfile
docker-compose.yml
README.md
.DS_Store
coverage
.next
Building Images
# Build an image from the current directory
docker build -t myapp:latest .
# Build with a specific Dockerfile
docker build -f Dockerfile.prod -t myapp:prod .
# Build with build arguments
docker build --build-arg NODE_ENV=production -t myapp .
# Build without cache
docker build --no-cache -t myapp .
Layer Caching Tips
- ✅ Copy
package.jsonbefore source code for better caching - ✅ Combine related RUN commands to reduce layers
- ✅ Order instructions from least to most frequently changing
- ✅ Use
.dockerignoreto exclude unnecessary files