Dockerfile Guide
Learn how to create Dockerfiles to build custom container images for your applications.
What is a Dockerfile?
A Dockerfile is a text file that contains step-by-step instructions for building a Docker image. Each instruction creates a new layer in the image, making builds efficient and reusable.
Basic Dockerfile Structure
Here’s a simple Dockerfile for a Node.js application:
# Use an official Node.js runtime as the base image
FROM node:18-alpine
# Set the working directory inside the container
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Expose the application port
EXPOSE 3000
# Command to run the application
CMD ["node", "app.js"]Common Dockerfile Instructions
FROM
Specifies the base image to build upon. Always the first instruction in a Dockerfile.
FROM node:18-alpine
FROM ubuntu:22.04
FROM python:3.11-slimWORKDIR
Sets the working directory for subsequent instructions. Similar to cd in a shell.
WORKDIR /app
WORKDIR /usr/src/appCOPY
Copies files from the host into the container image.
COPY . . # Copy everything
COPY package.json ./ # Copy specific file
COPY src/ ./app/ # Copy directoryADD
Similar to COPY but can also handle remote URLs and automatically extract compressed files.
ADD https://example.com/file.tar.gz /app/
ADD archive.tar.gz /tmp/ # Automatically extractsRUN
Executes commands during the image build process.
RUN npm install
RUN apt-get update && apt-get install -y curl
RUN python -m pip install -r requirements.txtCMD
Provides the default command to run when the container starts. Can be overridden from the command line.
CMD ["node", "app.js"]
CMD ["python", "server.py"]
CMD ["nginx", "-g", "daemon off;"]ENTRYPOINT
Configures the container to run as an executable. Cannot be easily overridden.
ENTRYPOINT ["python", "app.py"]
ENTRYPOINT ["docker-entrypoint.sh"]EXPOSE
Documents which ports the container listens on. More for documentation than actual port mapping.
EXPOSE 3000
EXPOSE 8080
EXPOSE 80ENV
Sets environment variables in the container.
ENV NODE_ENV=production
ENV API_URL=https://api.example.com
ENV PORT=3000ARG
Defines build-time variables that can be passed during the build process.
ARG VERSION=latest
ARG BUILD_NUMBERBest Practices
Use .dockerignore
Create a .dockerignore file to exclude unnecessary files from the build context:
node_modules
.git
.gitignore
README.md
.env
*.logMinimize Layers
Combine related RUN commands to reduce the number of layers:
# Bad - multiple layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
# Good - single layer
RUN apt-get update && apt-get install -y curl && apt-get cleanUse Specific Versions
Avoid using latest tags for better reproducibility:
# Bad
FROM node:latest
# Good
FROM node:18.17.0-alpineLeverage Build Cache
Order instructions from least to most likely to change:
# Install dependencies first (less likely to change)
COPY package*.json ./
RUN npm install
# Copy application code last (more likely to change)
COPY . .Multi-Stage Builds
Multi-stage builds help create smaller, more secure images by separating build and runtime environments:
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]Building and Running
Build an Image
docker build -t my-app:1.0 .Build with Build Arguments
docker build --build-arg VERSION=1.0 -t my-app .Run the Container
docker run -p 3000:3000 my-app:1.0Example: Python Web Application
Here’s a complete Dockerfile for a Flask application:
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y gcc && rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create non-root user
RUN useradd --create-home --shell /bin/bash app
USER app
# Expose port
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/ || exit 1
# Run the application
CMD ["python", "app.py"]