Communitygithub.com

Nuvraxis/nuvraxis-plugins

Règles Dockerfile pour applications Next.js standalone dans un monorepo pnpm + Turborepo — construction multi-étapes, sécurisée et optimisée.

Qu'est-ce que nuvraxis-plugins ?

Ce skill fournit des règles précises et éprouvées pour créer des images Docker optimisées et sécurisées pour des applications Next.js en mode `standalone`, spécifiquement conçues pour un monorepo utilisant pnpm et Turborepo. Il guide l'utilisateur à travers une construction en cinq étapes (`base`, `pruner`, `deps`, `builder`, `runner`) qui exploite le prunning de Turborepo et la mise en cache des dépendances pour des builds rapides et fiables. La sécurité est une priorité : l'image utilise un utilisateur non-root (uid 1001), un système de fichiers racine en lecture seule avec un volume tmpfs pour le cache, des secrets injectés via BuildKit (jamais dans l'historique de l'image), et une vérification de l'intégrité du binaire pnpm via SHA-256. Le skill détaille également la gestion multi-architecture, les labels OCI, le `HEALTHCHECK`, et fournit un tableau de débogage pour les problèmes courants, ce qui en fait un guide complet pour la conteneurisation d'applications Next.js en production.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Nuvraxis/nuvraxis-plugins/tree/HEAD/plugins/nuvraxis-frontend/skills/docker-nextjs

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Docker pour Next.js

Utilisez-le pour l'image de l'application. Les faits relatifs au standalone Next.js se trouvent dans nextjs-production (references/self-hosting.md) ; ce qui exécute l'image dans Kubernetes est helm-nextjs ; comment le CI construit et pousse l'image est github-actions.

Le Dockerfile du template dans apps/<app>/ est la source de vérité ; references/Dockerfile.md le contient en entier avec des notes. Construisez à partir de la racine du dépôt : docker build -f apps/<app>/Dockerfile -t <app> .

Règles

  1. Cinq étapes : base → pruner → deps → builder → runner.

    • base : Node + pnpm, rien d'autre.
    • pruner : turbo prune <app> --docker sur l'ensemble du dépôt, ne laissant que l'application et ses dépendances d'espace de travail.
    • deps : copie seulement out/json/ (manifestes) et out/pnpm-lock.yaml, puis pnpm fetch + pnpm install --frozen-lockfile --offline. Cette couche est réutilisée jusqu'à ce qu'un manifeste ou le fichier de verrouillage change, donc une modification du code source ne déclenche pas une réinstallation.
    • builder : copie out/full/ (sources), pnpm exec turbo run build --filter=<app>.
    • runner : une image Node fraîche avec seulement la sortie standalone. Pourquoi : les modifications du code source ne reconstruisent que les deux dernières étapes, et le runner n'a ni pnpm, ni sources, ni dépendances de développement.
  2. Épinglez l'image Node par version exacte et empreinte (digest), en faisant correspondre .node-version. node:26.12.0-alpine3.24@sha256:6f08… dans un seul ARG NODE_IMAGE, utilisé à la fois par base et runner. L'empreinte est l'index multi-arch, donc amd64 et arm64 se résolvent tous les deux. Pourquoi Alpine plutôt que Distroless : Distroless Node ne publie que des tags flottants (latest, nonroot), donc la version exacte de Node ne peut pas être mise en correspondance avec .node-version, et il n'a pas de wget pour le HEALTHCHECK. Alpine est légère, s'épingle exactement, et sharp livre des binaires musl préconstruits, donc l'optimisation d'image fonctionne sans paquets supplémentaires.

  3. Installez pnpm à partir du binaire de la version GitHub, vérifié par SHA-256. Jamais npm i -g pnpm, jamais Corepack, jamais un curl | sh non vérifié. Un ARG PNPM_VERSION égal à packageManager, plus une somme de contrôle par architecture pour pnpm-linux-{x64,arm64}-musl.tar.gz, sélectionnée par TARGETARCH. Pour mettre à jour : changez les trois ARGs, en prenant les valeurs sha256 des empreintes des assets de la version (gh release view v<version> --repo pnpm/pnpm --json assets), et mettez à jour packageManager dans le même commit. Solution de repli si le format de publication change : basez les étapes pruner/deps/builder sur ghcr.io/pnpm/pnpm:<version> (pas de Node, ajoutez-le) plutôt que d'inventer une nouvelle méthode d'installation.

  4. Le runner copie exactement trois choses.

    COPY --from=builder /repo/apps/<app>/.next/standalone ./
    COPY --from=builder /repo/apps/<app>/.next/static ./apps/<app>/.next/static
    COPY --from=builder /repo/apps/<app>/public ./apps/<app>/public
    

    Le mode standalone n'inclut pas static ou public ; sans eux, l'application ne sert aucun JS ou CSS. Dans le monorepo, le serveur est apps/<app>/server.js. CMD ["node", "apps/<app>/server.js"], forme exec, donc Node reçoit SIGTERM directement.

  5. Exécutez avec uid/gid 1001, les fichiers appartenant à root. Créez nextjs:nodejs (1001:1001), laissez les fichiers copiés appartenir à root (l'application peut lire mais pas modifier son propre code), et chown seulement apps/<app>/.next/cache. USER 1001:1001, numérique, afin que Kubernetes puisse vérifier runAsNonRoot sans résoudre les noms.

  6. L'image doit fonctionner avec un système de fichiers racine en lecture seule. Les seules écritures à l'exécution vont dans .next/cache (optimisation d'image, cache ISR/fetch). Kubernetes y monte un emptyDir ; localement, utilisez --tmpfs. Testez-le :

    docker run --rm -p 3000:3000 --read-only \
      --tmpfs /app/apps/<app>/.next/cache:uid=1001,gid=1001 \
      --cap-drop ALL --security-opt no-new-privileges <app>
    
  7. Définissez NODE_ENV=production, HOSTNAME=0.0.0.0, PORT=3000, NEXT_TELEMETRY_DISABLED=1 dans le runner. Sans HOSTNAME=0.0.0.0, le serveur standalone se lie au nom d'hôte du conteneur et les sondes échouent.

  8. HEALTHCHECK cible /api/health avec wget de busybox. C'est pour docker run/Compose ; Kubernetes l'ignore et utilise les sondes du chart sur le même chemin.

  9. Les arguments de construction (build args) sont uniquement pour les valeurs intégrées à la construction : NEXT_PUBLIC_* et NEXT_DEPLOYMENT_ID. NEXT_PUBLIC_* est compilé dans le JS, donc il doit être un ARG + ENV dans builder, et il est identique dans tous les environnements où l'image s'exécute. Tout ce qui est spécifique à l'environnement est une variable d'environnement d'exécution provenant du chart. Pour en ajouter un :

    ARG NEXT_PUBLIC_APP_NAME
    ENV NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME}
    

    … plus build-args dans les deux workflows et NEXT_PUBLIC_* est déjà dans l'env de turbo.

  10. Les secrets ne vont jamais dans un ARG, ENV ou COPY. Ils resteraient dans l'historique de l'image. Utilisez un montage de secret BuildKit, qui n'existe que pour ce RUN :

    RUN --mount=type=secret,id=npm_token,env=NPM_TOKEN pnpm fetch   # registry privé
    

    L'étape de construction du template déclare déjà le secret optionnel next_server_actions_key de cette façon.

  11. Mettez en cache le store pnpm avec un cache mount BuildKit. --mount=type=cache,id=pnpm-store,target=/pnpm/store sur fetch/install/dlx, avec pnpm_config_store_dir=/pnpm/store. Le store survit entre les constructions sur le même builder sans atterrir dans une couche. En CI, la mise en cache de couche est type=gha (voir github-actions).

  12. .dockerignore à la racine du dépôt maintient le contexte petit et les secrets à l'écart. Il exclut node_modules, .next, .turbo, dist, coverage, .env*, .git, .github, charts. Un contexte qui inclut node_modules de Windows ou macOS casse les paquets natifs dans l'image Linux.

  13. Multi-architecture est linux/amd64,linux/arm64, construit par le CI. Localement, construisez pour votre propre architecture. Chaque étape est neutre en termes d'architecture à l'exception du téléchargement de pnpm, qui choisit sa somme de contrôle à partir de TARGETARCH.

  14. Étiquettes : le Dockerfile définit org.opencontainers.image.title et licenses ; le CI ajoute source, revision, version, created via docker/metadata-action. Ne codez pas en dur une étiquette de version.

  15. Gardez les étapes exemptes de hacks spécifiques à l'application. Pas de apk add dans le runner (l'application n'a pas besoin d'une boîte à outils shell), pas de npm, pas de node_modules copiés depuis l'hôte. Si l'application a besoin d'un outil natif à l'exécution, justifiez-le dans un commentaire.

Débogage

SymptômeCause
extends: … doesn't resolve pendant la constructionUn fichier au niveau racine dont l'application a besoin n'était pas dans l'arbre élagué : déplacez-le dans un package d'espace de travail
ERR_PNPM_OUTDATED_LOCKFILE dans depsFichier de verrouillage non commité après un changement de manifeste
ERR_PNPM_IGNORED_BUILDSUne nouvelle dépendance veut un script d'installation : décidez dans allowBuilds
L'application démarre, pas de CSS/JS.next/static ou public pas copié dans apps/<app>/
La vérification de santé échoue, les logs sont bonsHOSTNAME n'est pas 0.0.0.0
EROFS à l'exécutionQuelque chose écrit en dehors de .next/cache ; trouvez-le, ne supprimez pas --read-only
NEXT_PUBLIC_X videNon passé comme argument de construction (c'est cuit à la construction)

Vérification

docker build -f apps/<app>/Dockerfile -t <app>:local --build-arg NEXT_DEPLOYMENT_ID=local .
docker run -d --name <app> -p 3000:3000 --read-only --tmpfs /app/apps/<app>/.next/cache:uid=1001,gid=1001 --cap-drop ALL <app>:local
curl -fsS localhost:3000/api/health
docker exec <app> id                     # uid=1001(nextjs)
docker inspect --format '{{.State.Health.Status}}' <app>   # healthy after ~30s
docker rm -f <app>

Sources

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Nuvraxis/nuvraxis-plugins

>- Biome 2.5 setup and rules for Nuvraxis monorepos — root biome.json with pinned $schema, VCS ignore, formatter settings, the recommended preset with a11y and the react/next domains, nursery policy, organize imports, overrides, nested per-package configs ("extends": "//"), biome check --write locally, biome ci in CI, VS Code settings, and migrating from ESLint/Prettier. Use when the repo has biome.json or biome.jsonc, @biomejs/biome in package.json, .eslintrc*/eslint.config.*/.prettierrc* to migrate, or when fixing lint and formatting errors.

Nuvraxis/nuvraxis-plugins

>- GitHub Actions conventions for Nuvraxis Next.js apps — exactly three workflows: pr.yaml (every PR to any branch: Biome, stylelint, typecheck/test/build with turbo --affected, Docker build + health check, helm lint + kubeconform; publishes nothing), main.yaml (push to main: one multi-arch image to GHCR tagged sha-SHORTSHA and main, provenance + SBOM, gha cache), release.yaml (tag vX.Y.Z: verify on main, retag the existing image without rebuilding, package and push the Helm chart to OCI, GitHub Release with chart, digest and SBOM). Minimal permissions per job, GITHUB_TOKEN only, pinned action majors. Use when editing .github/workflows/*, adding a CI step, or debugging a failed run or release.

Nuvraxis/nuvraxis-plugins

>- Helm chart rules for Nuvraxis Next.js apps — one chart per app in charts/ (named after the app), published to oci://ghcr.io/nuvraxis/charts and synced by Argo CD; Chart.yaml version/appVersion set only by release.yaml; Deployment with startup/liveness/readiness probes on /api/health, non-root read-only security context, emptyDir for .next/cache, ConfigMap + existing Secret via envFrom, Service, Ingress with cert-manager, HPA, PDB, ServiceAccount, topology spread; no Helm hooks; helm lint --strict + kubeconform. Use when the repo has charts/, Chart.yaml, values*.yaml, templates/*.yaml with Helm syntax, or an Argo CD Application, or when asked to deploy, scale, expose or configure the app on Kubernetes.

Nuvraxis/nuvraxis-plugins

>- Production rules for Next.js 16 App Router apps at Nuvraxis — Server/Client Component boundaries, Cache Components ("use cache", cacheLife, cacheTag, updateTag), Server Actions, route handlers, env handling, instrumentation, output standalone and the /api/health route. Use when the project has "next" in package.json, an app/ or src/app/ directory, next.config.ts, proxy.ts, instrumentation.ts, or files named page.tsx, layout.tsx, route.ts, loading.tsx, error.tsx.

Skills associés