Dockerfile Reference
This is a comprehensive reference for all Dockerfile instructions. A Dockerfile is a text document containing instructions that Docker reads to build an image automatically.
Syntax Overview
# Comment
INSTRUCTION arguments- Instructions are case-insensitive but conventionally written in UPPERCASE.
- Instructions are executed in order, top to bottom.
- Each instruction creates a new image layer (with some exceptions).
- The first instruction must be
FROM(orARGbeforeFROM).
Parser Directives
Parser directives must appear at the very top of the Dockerfile, before any other instruction or comment:
# syntax=docker/dockerfile:1
# escape=\
FROM node:20-alpine
...| Directive | Description | Example |
|---|---|---|
syntax | Sets the Dockerfile frontend (BuildKit) | # syntax=docker/dockerfile:1 |
escape | Sets the escape character | # escape=\ (default) or # escape= ` `` |
FROM
Sets the base image for the build stage.
# Basic usage
FROM ubuntu:22.04
# With alias for multi-stage builds
FROM node:20-alpine AS builder
# With platform specification
FROM --platform=linux/amd64 golang:1.22-alpine
# Using an ARG variable
ARG BASE_IMAGE=node:20-alpine
FROM ${BASE_IMAGE}
# Start from nothing (for statically compiled binaries)
FROM scratchFROM Options
| Option | Description | Example |
|---|---|---|
--platform | Target platform | --platform=linux/amd64 |
AS name | Name the build stage | AS builder |
RUN
Executes a command during the image build process.
# Shell form (runs in /bin/sh -c)
RUN apt-get update && apt-get install -y curl
# Exec form (runs directly, no shell processing)
RUN ["apt-get", "install", "-y", "curl"]
# Multi-line with line continuation
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
git \
vim && \
rm -rf /var/lib/apt/lists/*
# Heredoc syntax (BuildKit, Dockerfile 1.4+)
RUN <<EOF
apt-get update
apt-get install -y curl git
rm -rf /var/lib/apt/lists/*
EOF
# With mount options (BuildKit)
RUN --mount=type=cache,target=/root/.npm npm ci
RUN --mount=type=secret,id=token TOKEN=$(cat /run/secrets/token) && use-token
RUN --mount=type=ssh git clone [email protected]:user/repo.git
RUN --mount=type=bind,source=.,target=/src make -C /src build
RUN --mount=type=tmpfs,target=/tmp make buildRUN Mount Types
| Type | Purpose | Persists | In Image |
|---|---|---|---|
cache | Cache package managers | ✅ Between builds | ❌ |
secret | Sensitive build data | ❌ | ❌ |
ssh | SSH agent forwarding | ❌ | ❌ |
bind | Host files during build | N/A | ❌ |
tmpfs | Temporary build storage | ❌ | ❌ |
RUN --network
# Run with no network access (security)
RUN --network=none pip install --no-deps -r requirements.txt
# Run with default network
RUN --network=default curl -o file.tar.gz https://example.com/file.tar.gz
# Run on host network
RUN --network=host curl http://localhost:8080/apiCMD
Sets the default command that is executed when a container starts. There can be only one CMD instruction per Dockerfile (the last one takes effect).
# Exec form (preferred)
CMD ["node", "server.js"]
CMD ["python", "-u", "app.py"]
# Shell form
CMD node server.js
# As default parameters for ENTRYPOINT
ENTRYPOINT ["python"]
CMD ["app.py"]
# The CMD can be overridden at runtime:
# docker run my-image custom-commandCMD vs ENTRYPOINT Interaction
| No ENTRYPOINT | ENTRYPOINT exec | ENTRYPOINT shell | |
|---|---|---|---|
| No CMD | Error | /ep | /bin/sh -c /ep |
| CMD exec | /cmd p1 | /ep /cmd p1 | /bin/sh -c /ep |
| CMD shell | /bin/sh -c /cmd | /ep /bin/sh -c /cmd | /bin/sh -c /ep |
ENTRYPOINT
Configures the container to run as an executable. Unlike CMD, ENTRYPOINT is not easily overridden.
# Exec form (preferred)
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["postgres"]
# The container always runs docker-entrypoint.sh
# CMD provides default arguments
# docker run my-postgres → docker-entrypoint.sh postgres
# docker run my-postgres bash → docker-entrypoint.sh bash
# Shell form (cannot receive CMD arguments)
ENTRYPOINT exec java -jar app.jar# Override entrypoint at runtime
docker run --entrypoint /bin/sh my-imageCOPY
Copies files and directories from the build context (or a build stage) into the image.
# Basic file copy
COPY package.json .
COPY package.json /app/
# Copy multiple files
COPY package.json package-lock.json ./
# Copy directory contents
COPY src/ /app/src/
# With wildcard patterns
COPY *.json /app/
COPY hom?.txt /app/
# With ownership
COPY --chown=user:group files/ /app/
# From another build stage
COPY --from=builder /app/dist /srv/public
# From an external image
COPY --from=nginx:latest /etc/nginx/nginx.conf /etc/nginx/
# With chmod (BuildKit)
COPY --chmod=755 scripts/ /app/scripts/
# With link (BuildKit — creates hard links for better cache)
COPY --link package.json /app/COPY Options
| Option | Description | Example |
|---|---|---|
--from | Copy from a named stage or image | --from=builder |
--chown | Set file ownership (Linux only) | --chown=1000:1000 |
--chmod | Set file permissions | --chmod=755 |
--link | Use hard link for better caching | --link |
--parents | Preserve parent directories | --parents |
--exclude | Exclude files matching patterns | --exclude=*.test.js |
ADD
Similar to COPY but with extra features: tar extraction and URL support.
# Copy files (same as COPY)
ADD app.js /app/
# Auto-extract tar archives
ADD archive.tar.gz /app/
# Download from URL
ADD https://example.com/file.txt /app/
# With checksum verification
ADD --checksum=sha256:abc123... https://example.com/file.txt /app/
# With permissions
ADD --chown=user:group --chmod=755 app.tar.gz /app/WARNING
Prefer COPY over ADD unless you specifically need tar extraction or URL download. COPY is more transparent and predictable.
ENV
Sets environment variables that persist in the built image and running containers.
# Key-value syntax
ENV NODE_ENV=production
ENV DB_HOST=localhost DB_PORT=5432
# Legacy syntax (single variable)
ENV NODE_ENV production
# Use in subsequent instructions
ENV APP_HOME=/app
WORKDIR $APP_HOME# Override at runtime
docker run -e NODE_ENV=development my-imageARG
Defines build-time variables that are only available during the image build.
# Define with default value
ARG NODE_VERSION=20
ARG BUILD_DATE
# Use in FROM
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine
# ARG must be redeclared after FROM to be used in the stage
ARG APP_VERSION=1.0
RUN echo "Building version ${APP_VERSION}"# Override during build
docker build --build-arg NODE_VERSION=18 --build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) .Predefined ARGs
| ARG | Description |
|---|---|
HTTP_PROXY / http_proxy | HTTP proxy |
HTTPS_PROXY / https_proxy | HTTPS proxy |
FTP_PROXY / ftp_proxy | FTP proxy |
NO_PROXY / no_proxy | No proxy list |
BUILDKIT_INLINE_CACHE | Enable inline cache |
TARGETPLATFORM | Platform of the build target (e.g., linux/amd64) |
TARGETOS | OS of the target (e.g., linux) |
TARGETARCH | Architecture of the target (e.g., amd64) |
BUILDPLATFORM | Platform of the build host |
WORKDIR
Sets the working directory for subsequent instructions.
# Absolute path
WORKDIR /app
# Relative path (relative to previous WORKDIR)
WORKDIR src
# Now in /app/src
# Using environment variables
ENV APP_HOME=/opt/myapp
WORKDIR $APP_HOMEEXPOSE
Documents the ports that the container listens on at runtime.
# Single port
EXPOSE 80
# Multiple ports
EXPOSE 80 443
# Specific protocol
EXPOSE 80/tcp
EXPOSE 53/udpINFO
EXPOSE does not actually publish the port. Use -p flag at runtime to publish ports: docker run -p 8080:80 my-image.
VOLUME
Creates a mount point and marks it as holding externally mounted volumes.
# Single volume
VOLUME /data
# Multiple volumes
VOLUME ["/data", "/config"]USER
Sets the user (and optionally group) for subsequent instructions and the running container.
# By name
USER appuser
# By UID
USER 1000
# By name with group
USER appuser:appgroup
# By UID:GID
USER 1000:1000
# Create user and switch
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuserLABEL
Adds metadata to the image as key-value pairs.
LABEL maintainer="[email protected]"
LABEL version="1.0"
LABEL description="My application image"
# OCI standard labels
LABEL org.opencontainers.image.title="My App"
LABEL org.opencontainers.image.version="1.0.0"
LABEL org.opencontainers.image.description="Application description"
LABEL org.opencontainers.image.authors="[email protected]"
LABEL org.opencontainers.image.source="https://github.com/org/repo"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.created="2025-01-01T00:00:00Z"HEALTHCHECK
Tells Docker how to test a container to check that it is still working.
# HTTP check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# TCP check
HEALTHCHECK CMD nc -z localhost 3000 || exit 1
# Custom script
HEALTHCHECK CMD /app/healthcheck.sh
# Disable health check (from base image)
HEALTHCHECK NONE| Parameter | Default | Description |
|---|---|---|
--interval | 30s | Time between checks |
--timeout | 30s | Check timeout |
--start-period | 0s | Grace period during startup |
--start-interval | 5s | Interval during start period |
--retries | 3 | Failures before unhealthy |
SHELL
Overrides the default shell used for shell-form commands.
# Default shell on Linux: ["/bin/sh", "-c"]
# Default shell on Windows: ["cmd", "/S", "/C"]
# Change to bash
SHELL ["/bin/bash", "-c"]
RUN echo "Using bash now"
# Change to PowerShell (Windows)
SHELL ["powershell", "-command"]
RUN Write-Output "Using PowerShell"STOPSIGNAL
Sets the system call signal that will be sent to the container to stop it.
# Default is SIGTERM
STOPSIGNAL SIGTERM
# Use SIGQUIT for graceful shutdown
STOPSIGNAL SIGQUIT
# Numeric signal
STOPSIGNAL 9ONBUILD
Adds a trigger instruction that executes when the image is used as a base for another build.
# In base image Dockerfile
FROM node:20-alpine
ONBUILD COPY package.json /app/
ONBUILD RUN npm install
ONBUILD COPY . /app/
WORKDIR /app
# When someone builds FROM this image:
# FROM my-base-image
# (ONBUILD instructions execute automatically)Complete Production Dockerfile
# syntax=docker/dockerfile:1
# ==================================================
# Build Stage
# ==================================================
FROM node:20-alpine AS builder
ARG APP_VERSION=0.0.0
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
# ==================================================
# Production Stage
# ==================================================
FROM node:20-alpine
LABEL org.opencontainers.image.title="My Application"
LABEL org.opencontainers.image.version="${APP_VERSION}"
LABEL org.opencontainers.image.source="https://github.com/org/repo"
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
COPY --from=builder --chown=appuser:appgroup /app/package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --only=production && \
npm cache clean --force
ENV NODE_ENV=production
ENV PORT=3000
USER appuser
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -q --spider http://localhost:${PORT}/health || exit 1
EXPOSE ${PORT}
CMD ["node", "dist/server.js"]Next Steps
- Compose File Format — Docker Compose file reference
- CLI Reference — Docker CLI command reference
- Image Building Best Practices — Optimize your Dockerfiles
- Multi-stage Builds — Advanced multi-stage patterns
- BuildKit Guide — Advanced build features