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-slim

WORKDIR

Sets the working directory for subsequent instructions. Similar to cd in a shell.

WORKDIR /app
WORKDIR /usr/src/app

COPY

Copies files from the host into the container image.

COPY . .                    # Copy everything
COPY package.json ./        # Copy specific file
COPY src/ ./app/            # Copy directory

ADD

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 extracts

RUN

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.txt

CMD

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 80

ENV

Sets environment variables in the container.

ENV NODE_ENV=production
ENV API_URL=https://api.example.com
ENV PORT=3000

ARG

Defines build-time variables that can be passed during the build process.

ARG VERSION=latest
ARG BUILD_NUMBER

Best Practices

Use .dockerignore

Create a .dockerignore file to exclude unnecessary files from the build context:

node_modules
.git
.gitignore
README.md
.env
*.log

Minimize 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 clean

Use Specific Versions

Avoid using latest tags for better reproducibility:

# Bad
FROM node:latest

# Good
FROM node:18.17.0-alpine

Leverage 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.0

Example: 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"]

Resources

Last updated on