Sur cette page
Dockerfile multi-stage
Un Dockerfile multi-stage déclare plusieurs étapes (stages) dans un seul fichier. Chaque environnement construit l’étape dont il a besoin.
Exemple Python (FastAPI)
# ---------- Base commune ----------
FROM python:3.12-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# ---------- Développement ----------
FROM base AS dev
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements-dev.txt .
RUN pip install --no-cache-dir -r requirements-dev.txt
COPY . .
CMD ["uvicorn","app.main:app","--host","0.0.0.0","--port","8000","--reload"]
# ---------- Build des dépendances ----------
FROM base AS builder
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip wheel \
--no-cache-dir \
--wheel-dir /wheels \
-r requirements.txt
# ---------- Production ----------
FROM base AS production
COPY --from=builder /wheels /wheels
RUN pip install \
--no-cache-dir \
/wheels/* \
&& rm -rf /wheels
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
flowchart TD Base["base<br/>Python + config commune"] Base --> Dev["dev<br/>outils dev + reload"] Base --> Builder["builder<br/>gcc + libpq-dev<br/>compile les dépendances"] Builder --> Production["production<br/>runtime minimal"] Dev --> Local["Développement local"] Production --> Staging["Staging"] Production --> Prod["Production"]
builder ne se déploie pas. C’est une étape intermédiaire qui compile les dépendances en wheels.
En local, Compose demande un stage précis avec target :
services:
api:
build:
context: .
target: dev
Docker construit alors jusqu’au stage dev, qui lance Uvicorn avec --reload.
Exemple Node : le Dockerfile en un seul stage
FROM node:24
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
CMD ["npm", "start"]
Ce Dockerfile fonctionne, mais l’image finale embarque des outils inutiles en production :
- TypeScript ;
- Vite ;
- ESLint ;
- Vitest ;
- fichiers source ;
- outils de build ;
devDependencies.
Le multi-stage découpe ce fichier en étapes spécialisées.
Stage base
FROM node:24-alpine AS base
WORKDIR /app
COPY package.json package-lock.json ./
base contient ce que les autres stages partagent : Node.js, le dossier de travail et les fichiers package.json.
Stage dev
FROM base AS dev
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
Cette image peut contenir :
- dépendances de développement ;
- TypeScript ;
- Vite ;
- hot reload ;
- code source ;
- outils de debug.
Compose sélectionne ce stage avec target :
build:
context: .
target: dev
Stage build
FROM base AS build
RUN npm ci
COPY . .
RUN npm run build
Ce stage compile les sources TypeScript dans dist/. Il ne fait rien d’autre.
Stage production
FROM node:24-alpine AS production
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
CMD ["node", "dist/server.js"]
La ligne qui compte :
COPY --from=build /app/dist ./dist
Elle copie seulement le résultat du stage build.
flowchart TD Base["base"] Base --> Dev["dev"] Base --> Build["build"] Build -->|"COPY --from=build"| Production["production"] Dev --> DevInfo["sources<br/>devDependencies<br/>Vite<br/>tests"] Production --> ProdInfo["dist/<br/>prod dependencies<br/>runtime"]
L’image de production n’a donc pas besoin de contenir :
- les tests ;
- ESLint ;
- Vite ;
- TypeScript ;
- les outils de build ;
- les devDependencies.
Résultat :
- des images plus petites ;
- moins de surface d’attaque ;
- des démarrages et transferts plus rapides ;
- une séparation claire entre build et runtime.