Vue d’ensemble
Le Dockerfile définit comment Docker construit une image. Cet aide-mémoire couvre les 17 instructions primaires, les patterns avancés comme les builds multi-étages et les cache mounts, les bonnes pratiques de sécurité et de performance, et les techniques d’optimisation. Toute la syntaxe est à jour pour Docker 27.
Instructions principales
FROM
| Instruction | Objectif | Exemple |
|---|---|---|
FROM <image>:<tag> |
Définir l’image de base (doit être la première ligne, sauf commentaires/ARG) | FROM ubuntu:24.04 |
FROM <image>:<tag> AS <stage> |
Définir une étape nommée dans un build multi-étages | FROM golang:1.22 AS builder |
FROM scratch |
Commencer à partir d’une base vide (pour les binaires statiques seulement) | FROM scratch |
Notes :
– Vous pouvez utiliser plusieurs déclarations FROM pour créer plusieurs étapes de build.
– Chaque FROM réinitialise le système de fichiers ; les couches précédentes sont supprimées à moins d’être explicitement copiées.
– scratch ne contient que ce que vous ajoutez explicitement ; la plus petite image possible.
RUN
| Instruction | Objectif | Exemple |
|---|---|---|
RUN <command> |
Exécuter une commande pendant le build (forme shell) | RUN apt-get update && apt-get install -y curl |
RUN ["executable", "param1", "param2"] |
Exécuter une commande en forme exec (pas de shell) | RUN ["python3", "-m", "pip", "install", "django"] |
Bonnes pratiques :
– Utilisez la forme exec quand possible pour éviter les problèmes d’interprétation shell.
– Chaînez les commandes avec && pour garder les couches petites : RUN apt-get update && apt-get install && apt-get clean
– Nettoyez les caches du gestionnaire de paquets : rm -rf /var/lib/apt/lists/* (apt), apk del .build-deps (apk)
– Utilisez les flags --no-cache dans les gestionnaires de paquets pour réduire la taille de la couche.
COPY
| Instruction | Objectif | Exemple |
|---|---|---|
COPY <source> <dest> |
Copier les fichiers/répertoires du contexte de build vers l’image | COPY app.py /app/app.py |
COPY --chown=<user>:<group> <source> <dest> |
Copier et définir la propriété | COPY --chown=appuser:appuser . /app/ |
COPY --chmod=<mode> <source> <dest> |
Copier et définir les permissions des fichiers | COPY --chmod=755 script.sh /app/script.sh |
Bonnes pratiques :
– COPY est préféré à ADD pour la copie simple de fichiers.
– Utilisez .dockerignore pour exclure les artefacts de build, les tests, .git.
– Copiez seulement ce dont vous avez besoin ; divisez en plusieurs lignes COPY pour l’efficacité du cache.
ADD
| Instruction | Objectif | Exemple |
|---|---|---|
ADD <source> <dest> |
Copier les fichiers/répertoires, avec des fonctionnalités supplémentaires (extraction tar, récupération d’URL) | ADD archive.tar.gz /app/ |
ADD --chown=<user>:<group> <source> <dest> |
Ajouter avec la propriété | ADD --chown=appuser:appuser file.txt /app/ |
ADD --chmod=<mode> <source> <dest> |
Ajouter avec les permissions des fichiers | ADD --chmod=644 config.txt /etc/config.txt |
Notes :
– Si la source est une archive tar/zip/gzip, elle est extraite automatiquement.
– Si la source est une URL, elle est téléchargée (pas d’extraction).
– COPY est généralement plus clair ; utilisez ADD seulement pour l’extraction tar ou la récupération d’URL.
WORKDIR
| Instruction | Objectif | Exemple |
|---|---|---|
WORKDIR <path> |
Définir le répertoire de travail pour toutes les commandes ultérieures (RUN, CMD, ENTRYPOINT, COPY, ADD) | WORKDIR /app |
Notes :
– Si le répertoire n’existe pas, il est créé.
– Utilisez des chemins absolus pour éviter la confusion.
– Les instructions WORKDIR multiples sont cumulatives : WORKDIR /app puis WORKDIR src résulte en /app/src.
ENV
| Instruction | Objectif | Exemple |
|---|---|---|
ENV <key>=<value> |
Définir une variable d’environnement (disponible dans le conteneur en cours d’exécution) | ENV NODE_ENV=production |
ENV <key> <value> |
Syntaxe alternative (dépréciée, à éviter) | ENV DEBUG true |
Notes :
– Persiste dans les conteneurs ; défini une fois pendant le build, disponible au runtime.
– Utilisez pour la configuration au niveau de l’application (URLs de base de données, clés API pour le test).
– N’utilisez pas ENV pour les secrets ; utilisez les secrets Docker ou passez au runtime.
ARG
| Instruction | Objectif | Exemple |
|---|---|---|
ARG <name>=<default> |
Définir une variable de build (disponible seulement pendant le build, pas dans le conteneur en cours d’exécution) | ARG PYTHON_VERSION=3.12 |
ARG <name> |
Définir une variable sans défaut (doit être fournie avec --build-arg) |
ARG BUILD_NUMBER |
Usage dans le build :
docker build --build-arg PYTHON_VERSION=3.11 -t myapp:1.0 .
Notes :
– Les variables ARG sont utilisées seulement pendant le build ; elles n’apparaissent pas dans l’image finale.
– Pour passer une valeur ARG au conteneur en cours d’exécution, réassignez-la avec ENV : ENV PYTHON_VERSION=$PYTHON_VERSION
EXPOSE
| Instruction | Objectif | Exemple |
|---|---|---|
EXPOSE <port> |
Documenter qu’un conteneur écoute sur un port (ne publie pas) | EXPOSE 8080 |
EXPOSE <port>/udp |
Documenter un port UDP | EXPOSE 53/udp |
EXPOSE <port1> <port2> |
Documenter plusieurs ports | EXPOSE 8080 8443 |
Notes :
– EXPOSE est seulement des métadonnées ; ne publie pas réellement le port.
– Pour publier, utilisez docker run -p <host>:<container> ou Docker Compose ports:.
– Utile pour la documentation et pour docker run -P (ports aléatoires de haut niveau).
USER
| Instruction | Objectif | Exemple |
|---|---|---|
USER <user> |
Définir l’utilisateur (ou UID) pour les commandes ultérieures (RUN, CMD, ENTRYPOINT, COPY, ADD) | USER appuser |
USER <uid>:<gid> |
Utiliser directement UID et GID | USER 1000:1000 |
Bonne pratique de sécurité :
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser
Notes :
– Exécuter en tant que root dans un conteneur est un risque de sécurité.
– Utilisez un utilisateur non-root dédié pour limiter le rayon d’explosion en cas d’échappement du conteneur.
– Doit être créé avant l’instruction USER (utilisez RUN).
HEALTHCHECK
| Instruction | Objectif | Exemple |
|---|---|---|
HEALTHCHECK --interval=<seconds> --timeout=<seconds> --start-period=<seconds> --retries=<count> CMD <command> |
Définir une commande de vérification de santé | HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD curl -f http://localhost:8000/health \|\| exit 1 |
HEALTHCHECK NONE |
Désactiver une vérification de santé héritée de l’image de base | HEALTHCHECK NONE |
Options :
– --interval : Fréquence d’exécution (par défaut : 30s)
– --timeout : Temps d’attente (par défaut : 30s)
– --start-period : Période de grâce avant la première vérification (par défaut : 0s)
– --retries : Échecs consécutifs avant de marquer unhealthy (par défaut : 3)
Notes :
– Code de sortie 0 = healthy, 1 = unhealthy, 2 = réservé.
– Docker Compose et les orchestrateurs utilisent le statut de santé pour redémarrer/remplacer les conteneurs.
ENTRYPOINT
| Instruction | Objectif | Exemple |
|---|---|---|
ENTRYPOINT ["executable", "param1"] |
Définir le processus principal du conteneur (forme exec, préférée) | ENTRYPOINT ["python3", "-u", "app.py"] |
ENTRYPOINT command param1 |
Définir le processus principal (forme shell, à éviter) | ENTRYPOINT python3 -u app.py |
Forme exec vs. forme shell :
– Forme exec : Exécute le fichier exécutable directement ; les signaux (SIGTERM) sont délivrés correctement ; pas d’interprétation shell.
– Forme shell : Enveloppe la commande dans /bin/sh -c ; le shell traite les signaux, pas votre app ; peut causer des processus zombies.
Avec CMD :
ENTRYPOINT ["python3"]
CMD ["app.py"]
# docker run image arg → python3 app.py arg
Notes :
– ENTRYPOINT définit ce qui ne peut pas être facilement remplacé.
– Utilisez toujours la forme exec pour ENTRYPOINT.
CMD
| Instruction | Objectif | Exemple |
|---|---|---|
CMD ["executable", "param1", "param2"] |
Fournir les arguments ou la commande par défaut (forme exec, préférée) | CMD ["python3", "app.py"] |
CMD command param1 |
Fournir les arguments par défaut (forme shell, à éviter) | CMD python3 app.py |
CMD ["param1", "param2"] |
Fournir les arguments par défaut à ENTRYPOINT | CMD ["production"] |
Notes :
– Si aucun ENTRYPOINT n’est défini, CMD est la commande.
– Si ENTRYPOINT est défini, CMD fournit les arguments à celui-ci.
– Utilisez toujours la forme exec pour CMD.
VOLUME
| Instruction | Objectif | Exemple |
|---|---|---|
VOLUME <path> |
Marquer un répertoire comme point de montage de volume | VOLUME /data |
VOLUME ["<path1>", "<path2>"] |
Marquer plusieurs points de montage | VOLUME ["/var/log", "/data"] |
Notes :
– VOLUME est déclaratif ; elle documente qu’un répertoire devrait être persisté ou monté.
– Ne crée pas réellement un volume ; le volume est créé au runtime.
– Au runtime : docker run -v myvolume:/data image ou docker run -v /host/path:/data image
LABEL
| Instruction | Objectif | Exemple |
|---|---|---|
LABEL <key>=<value> |
Ajouter des métadonnées à une image | LABEL maintainer="team@example.com" |
LABEL <key1>=<value1> <key2>=<value2> |
Ajouter plusieurs labels | LABEL version="1.0" description="My app" |
Notes :
– Les labels sont recherchables : docker images --filter "label=version=1.0"
– Labels communs : maintainer, version, description, org.opencontainers.image.*
STOPSIGNAL
| Instruction | Objectif | Exemple |
|---|---|---|
STOPSIGNAL SIGTERM |
Signal à envoyer lors de l’arrêt du conteneur (par défaut : SIGTERM) | STOPSIGNAL SIGTERM |
STOPSIGNAL SIGKILL |
Signal de forçage (rarement nécessaire) | STOPSIGNAL SIGKILL |
Notes :
– La plupart des apps gèrent SIGTERM (15) pour un arrêt gracieux.
– Utilisez SIGKILL (9) seulement si votre app ne gère pas SIGTERM.
SHELL
| Instruction | Objectif | Exemple |
|---|---|---|
SHELL ["<executable>", "<param>"] |
Remplacer le shell par défaut (par défaut : ["/bin/sh", "-c"] sur Linux) |
SHELL ["/bin/bash", "-c"] |
Cas d’usage :
FROM mcr.microsoft.com/windows/servercore:ltsc2022
SHELL ["powershell", "-Command", "-ErrorActionPreference", "Stop"]
RUN Get-ChildItem
ONBUILD
| Instruction | Objectif | Exemple |
|---|---|---|
ONBUILD <instruction> |
Déclencher une instruction dans les images enfants (rarement utilisé) | ONBUILD RUN npm install |
Notes :
– Quand une image enfant utilise FROM <parent>, les instructions ONBUILD du parent sont exécutées.
– Utile pour les images de base qui orchestrent les builds ; à éviter dans les Dockerfiles d’application.
Builds multi-étages
Les builds multi-étages réduisent la taille de l’image finale en utilisant des étapes intermédiaires pour construire et une étape finale avec seulement les dépendances runtime.
Pattern : Builder + Runtime
# Étape 1 : Build
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN go build -o app .
# Étape 2 : Runtime (minimal)
FROM alpine:3.20
COPY --from=builder /src/app /app
ENTRYPOINT ["/app"]
Avantages clés :
– Les dépendances de build (compilateurs, outils dev) ne sont pas dans l’image finale.
– L’image finale contient seulement le binaire compilé.
– Peut réduire la taille de l’image de 500MB à 10MB ou moins.
Avancé : étapes nommées, builds conditionnels
FROM golang:1.22 AS builder
ARG BUILD_TARGET=linux
WORKDIR /src
COPY . .
RUN GOOS=linux GOARCH=amd64 go build -o app .
FROM alpine:3.20 AS runtime
COPY --from=builder /src/app /app
USER nobody
ENTRYPOINT ["/app"]
# Utiliser : docker build --target runtime -t myapp:latest .
Cache mounts (Docker 19.03+)
Les cache mounts préservent les fichiers entre les builds, permettant des installations de paquets plus rapides et la mise en cache des dépendances.
FROM python:3.12-alpine
WORKDIR /app
# Monter le cache pour les paquets pip
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
# Utiliser : docker build --build-context . -t myapp:1.0 .
Cas d’usage :
– Caches du gestionnaire de paquets (pip, npm, cargo, etc.)
– Caches d’artefacts de build (cache Go, cible Rust, Maven .m2)
Commande de build :
docker build -t myapp:1.0 --build-context .
.dockerignore
Utilisez .dockerignore pour exclure les fichiers du contexte de build, réduisant la taille de la couche et le temps de build.
# .dockerignore
.git
.gitignore
.env
.env.local
*.pyc
__pycache__
.pytest_cache
node_modules
dist
build
*.log
README.md
.vscode
.idea
.DS_Store
Notes :
– Les patterns fonctionnent comme .gitignore.
– Réduit la taille du contexte envoyée au daemon Docker.
– Peut accélérer significativement les builds sur les grands projets.
Bonnes pratiques de sécurité
Exécuter en tant que non-root
FROM alpine:3.20
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --chown=appuser:appgroup . /app
USER appuser
ENTRYPOINT ["./app"]
Supprimer les capacités inutiles
RUN setcap -r /bin/app # Supprimer toutes les capacités du fichier exécutable
# Ou au runtime : docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE image
Utiliser un système de fichiers en lecture seule
# Au runtime : docker run --read-only --tmpfs /tmp image
Images de base minimales
alpine:3.20(~7 MB) — Recommandé pour la plupart des casdistroless/python3-nonroot(~50 MB) — Pré-renforcé, pas de gestionnaire de paquetsscratch(~0 MB) — Pour les binaires statiques seulement
Bonnes pratiques de taille
Nettoyer les caches du gestionnaire de paquets
# Alpine
RUN apk add --no-cache python3 py3-pip
# Debian/Ubuntu
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
# pip
RUN pip install --no-cache-dir django
Combiner les commandes RUN
# Mauvais (3 couches)
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# Bon (1 couche)
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
Utiliser COPY stratégiquement
# Copier seulement ce dont vous avez besoin, en ordre de fréquence de changement
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY src ./src
# Changer src n'invalide pas la couche pip
Patterns à réutiliser
# Image de base avec ARG pour la version
FROM python:${PYTHON_VERSION:-3.12}-alpine AS base
# Multi-étages avec arguments de build
FROM node:20-alpine AS builder
ARG NODE_ENV=production
RUN npm ci --only=production
# Vérification de santé avec curl
HEALTHCHECK --interval=30s --timeout=5s \
CMD curl -f http://localhost:3000/health || exit 1
# Utilisateur non-root avec UID statique pour la reproductibilité
RUN adduser -D -u 1000 appuser
# Copier avec propriété pour non-root
COPY --chown=appuser:appuser . /app
# Utiliser le cache de build pour les gestionnaires de paquets
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
# Forme exec ENTRYPOINT + CMD
ENTRYPOINT ["python3", "-u"]
CMD ["app.py"]
FAQ
Quelle est la différence entre COPY et ADD ?
COPY est plus simple et préféré pour les fichiers locaux. ADD peut extraire les archives tar et récupérer les URLs, mais ces fonctionnalités peuvent causer un comportement inattendu. Utilisez COPY à moins que vous n’ayez spécifiquement besoin d’extraction tar ou de récupération d’URL.
Dois-je utiliser ENV ou ARG ?
ARG est pour les variables de build (disponibles seulement pendant le build, pas dans le conteneur en cours d’exécution). ENV est pour les variables runtime (disponibles dans le conteneur en cours d’exécution). Utilisez ARG pour la configuration de build et ENV pour la configuration d’application.
Quelle est la différence entre ENTRYPOINT et CMD ?
ENTRYPOINT définit le processus principal du conteneur et ne peut généralement pas être remplacé. CMD fournit les arguments par défaut à ENTRYPOINT, ou est la commande si aucun ENTRYPOINT n’est défini. Utilisez la forme exec pour les deux.
Comment écrire un Dockerfile qui construise une petite image ?
Utilisez une image de base comme alpine:latest ou scratch. Utilisez les builds multi-étages pour copier seulement les artefacts compilés. Supprimez les dépendances de build. Nettoyez les caches du gestionnaire de paquets (rm -rf /var/lib/apt/lists/*). Évitez d’inclure des fichiers inutiles (utilisez .dockerignore).
Pourquoi dois-je exécuter en tant qu’utilisateur non-root ?
Exécuter en tant que root dans un conteneur signifie qu’une brèche ou une fuite pourrait donner à un attaquant l’accès root à l’hôte. Créez un utilisateur dédié et supprimez les capacités pour réduire le risque. Ajoutez USER <uid> avant la commande finale.
Comment ce guide a été vérifié
Rédigé à la main à partir de la documentation officielle, chaque commande revérifiée, puis relu une seconde fois avant publication. Dernière relecture le .
Sources officielles consultées pour cet article

