LANTORIANDocker, pas à pas

Chapitre 835 min2 exercices

Next.js sous Docker

Deux usages, deux configurations. En développement, le conteneur lit ton code en direct. En production, on livre une image compacte et figée.

Créer un projet sans Node installé

Terminal
cd projet
docker run --rm -it --user "$(id -u):$(id -g)" -e HOME=/tmp \
  -v "$PWD":/app -w /app node:24-slim npx create-next-app@latest front

Réponds aux questions (les valeurs par défaut conviennent). HOME=/tmp donne à npm un dossier où écrire son cache, puisque ton utilisateur n'existe pas dans l'image.

Le mode développement

Le code est partagé par un bind mount, next dev tourne dans le conteneur. Tu modifies un fichier, la page se recharge.

front/Dockerfile.dev
FROM node:24-slim
WORKDIR /app

# Prépare les dossiers pour l'utilisateur node (uid 1000)
RUN mkdir -p node_modules .next && chown -R node:node /app
USER node

EXPOSE 3000
CMD ["sh", "-c", "npm install && npm run dev"]
compose.yaml
services:
  front:
    build:
      context: ./front
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - ./front:/app
      - front-modules:/app/node_modules
      - front-next:/app/.next

volumes:
  front-modules:
  front-next:
Terminal
docker compose up -d --build
docker compose logs -f front          # attends « Ready »
docker compose exec front npm install zod   # ajouter une dépendance

L'image de production

En production, on ne monte pas le code : il est copié et compilé dans l'image. Le mode standalone de Next.js produit un serveur autonome qui n'embarque que les fichiers réellement utilisés.

front/next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "standalone",
};

export default nextConfig;
Trois étapes dans un seul Dockerfile. Chacune ne récupère que ce dont elle a besoin grâce à COPY --from.
front/Dockerfile
# Étape 1 : installer les dépendances
FROM node:24-slim AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Étape 2 : construire l'application
FROM node:24-slim AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
RUN npm run build

# Étape 3 : l'image finale, avec le strict nécessaire
FROM node:24-slim AS run
WORKDIR /app
ENV NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0
COPY --from=build --chown=node:node /app/public ./public
COPY --from=build --chown=node:node /app/.next/standalone ./
COPY --from=build --chown=node:node /app/.next/static ./.next/static
USER node
EXPOSE 3000
CMD ["node", "server.js"]
front/.dockerignore
node_modules
.next
.git
.env*
Dockerfile*
Terminal
cd front
docker build -t front:prod --build-arg NEXT_PUBLIC_API_URL=https://api.exemple.com .
docker run --rm -p 3000:3000 front:prod

Le piège NEXT_PUBLIC_

Les variables qui commencent par NEXT_PUBLIC_ sont écrites en dur dans le JavaScript envoyé au navigateur, au moment du npm run build. Les passer au docker run ne change rien : il est trop tard.

VariableLue quandComment la passer à Docker
NEXT_PUBLIC_API_URLAu build, figée dans le code client--build-arg et ARG dans le Dockerfile
API_URL_INTERNALÀ l'exécution, côté serveur uniquement-e ou environment: dans Compose
Exercice

Next.js en développement dans Docker

  1. Crée un projet Next.js dans projet/front sans installer Node.
  2. Ajoute Dockerfile.dev et un compose.yaml avec le service front.
  3. Démarre, ouvre localhost:3000 et remplace le contenu de app/page.tsx par un titre à toi.
Ce que tu dois obtenir

localhost:3000 affiche ta page. Quand tu enregistres app/page.tsx, le navigateur se met à jour en une ou deux secondes, sans relancer le conteneur.

Un indice

La page ne se met pas à jour ? Vérifie que ton projet n'est pas dans /mnt/c sous Windows. En dernier recours, ajoute WATCHPACK_POLLING: "true" dans l'environment du service.

Voir la solution
Terminal
cd projet
# Crée front/ avec create-next-app, puis front/Dockerfile.dev et compose.yaml
docker compose up -d --build
docker compose logs -f front
# Modifie front/app/page.tsx et regarde le navigateur
Exercice

Une image de production légère

  1. Active output: "standalone", ajoute le Dockerfile de production et le .dockerignore.
  2. Affiche process.env.NEXT_PUBLIC_API_URL dans la page d'accueil.
  3. Construis l'image avec une URL en --build-arg, lance-la et vérifie sa taille.
Ce que tu dois obtenir

docker images front affiche une image de l'ordre de 200 à 300 Mo, et localhost:3000 sert le site compilé. La page affiche l'URL passée en --build-arg.

Un indice

Erreur Cannot find module server.js ? Il manque output: "standalone" dans next.config.ts.

Voir la solution
front/app/page.tsx
export default function Home() {
  return <h1>API : {process.env.NEXT_PUBLIC_API_URL}</h1>;
}
Terminal
cd front
docker build -t front:prod --build-arg NEXT_PUBLIC_API_URL=https://api.exemple.com .
docker images front
docker run --rm -p 3000:3000 front:prod
Échap