Communitygithub.com

Nuvraxis/nuvraxis-plugins

pnpm + Turborepo 모노레포 Next.js 독립 실행형 앱을 위한 Dockerfile 규칙 — 멀티 스테이지 빌드(turbo prune → pnpm fetch/offline install → turbo build → 최소 runner), 다이제스트로 고정된 Node 26 Alpine, 체크섬 검증된 pnpm 바이너리, 1001번 비루트 사용자, 읽기 전용 루트 파일시스템과 쓰기 가능한 .next/cache, /api/health HEALTHCHECK, NEXT_PUBLIC_* 값만을 위한 빌드 인자, BuildKit 시크릿 및 캐시 마운트, 멀티 아키텍처, OCI 레이블 및 .dockerignore. 저장소에 Dockerfile, .dockerignore, compose 파일 또는 output이 "standalone"인 next.config가 있거나, 이미지 컨테이너화, 축소, 강화 또는 디버깅이 요청될 때 사용합니다.

nuvraxis-plugins란 무엇인가요?

이 스킬은 pnpm + Turborepo 모노레포 구조에서 Next.js 독립 실행형(standalone) 애플리케이션의 Docker 이미지를 생성하기 위한 포괄적인 규칙과 모범 사례를 제공합니다. 다중 스테이지 빌드(base, pruner, deps, builder, runner)를 사용하여 소스 변경 시 재설치 없이 빠른 증분 빌드를 가능하게 하며, Node 26 Alpine 이미지를 다이제스트로 고정하고 pnpm 바이너리를 체크섬 검증하여 보안과 재현성을 보장합니다. 또한 비루트 사용자 실행, 읽기 전용 루트 파일시스템, HEALTHCHECK, BuildKit 시크릿 및 캐시 마운트, 멀티 아키텍처 빌드, OCI 레이블 등 프로덕션에 적합한 보안 및 성능 최적화를 포함합니다. 주로 Claude Code, Cursor, Codex와 같은 AI 코딩 에이전트가 Next.js 프로젝트의 Dockerfile을 생성, 수정 또는 디버깅할 때 사용되며, 저장소의 Dockerfile, .dockerignore, docker-compose 파일, 또는 next.config의 standalone 출력 설정과 관련된 작업 시 자동으로 활성화됩니다.

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

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Next.js용 Docker

앱 이미지에 사용합니다. Next-side standalone 사실은 nextjs-production(references/self-hosting.md)에 있습니다. Kubernetes에서 이미지를 실행하는 방법은 helm-nextjs에, CI에서 빌드 및 푸시하는 방법은 github-actions에 있습니다.

템플릿의 apps/<app>/Dockerfile이 진실 공급원(source of truth)입니다. references/Dockerfile.md에 전체 내용과 주석이 있습니다. 저장소 루트에서 빌드: docker build -f apps/<app>/Dockerfile -t <app> .

규칙

  1. 다섯 개의 스테이지: base → pruner → deps → builder → runner.

    • base: Node + pnpm, 그 외에는 아무것도 없음.
    • pruner: 전체 저장소에서 turbo prune <app> --docker를 실행하여 앱과 해당 워크스페이스 종속성만 남깁니다.
    • deps: out/json/(매니페스트)과 out/pnpm-lock.yaml만 복사한 다음, pnpm fetch + pnpm install --frozen-lockfile --offline을 실행합니다. 이 레이어는 매니페스트나 lockfile이 변경될 때까지 재사용되므로 소스 코드 편집이 재설치를 유발하지 않습니다.
    • builder: out/full/(소스)을 복사하고 pnpm exec turbo run build --filter=<app>를 실행합니다.
    • runner: standalone 출력만 포함하는 새로운 Node 이미지입니다. 이유: 소스 변경은 마지막 두 단계만 다시 빌드하며, runner에는 pnpm, 소스, devDependencies가 없습니다.
  2. Node 이미지를 정확한 버전과 다이제스트로 고정하고, .node-version과 일치시킵니다. node:26.12.0-alpine3.24@sha256:6f08…을 하나의 ARG NODE_IMAGE에 사용하며, base와 runner 모두에서 사용합니다. 다이제스트는 멀티 아키텍처 인덱스이므로 amd64와 arm64가 모두 확인됩니다. Alpine을 distroless 대신 사용하는 이유: distroless Node는 부동 태그(latest, nonroot)만 게시하므로 .node-version과 정확한 Node 버전을 일치시킬 수 없고 HEALTHCHECK를 위한 wget도 없습니다. Alpine은 작고, 정확하게 고정할 수 있으며, sharp가 사전 빌드된 musl 바이너리를 제공하므로 추가 패키지 없이 이미지 최적화가 작동합니다.

  3. pnpm을 GitHub 릴리스 바이너리에서 설치하고 SHA-256으로 검증합니다. 절대 npm i -g pnpm, Corepack, 또는 검증되지 않은 curl | sh를 사용하지 마십시오. packageManager와 동일한 하나의 ARG PNPM_VERSION과 아키텍처별 pnpm-linux-{x64,arm64}-musl.tar.gz에 대한 체크섬 하나, TARGETARCH로 선택합니다. 업데이트하려면: 릴리스의 자산 다이제스트(gh release view v<version> --repo pnpm/pnpm --json assets)에서 sha256 값을 가져와 세 개의 ARG를 변경하고, 같은 커밋에서 packageManager도 업데이트합니다. 릴리스 형식이 변경될 경우의 대비: pruner/deps/builder 스테이지를 ghcr.io/pnpm/pnpm:<version>(Node 없음, 추가 필요) 기반으로 하여 새로운 설치 방법을 고안하지 않습니다.

  4. runner는 정확히 세 가지를 복사합니다.

    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
    

    standalone은 static 또는 public을 포함하지 않습니다. 이것들이 없으면 앱은 JS나 CSS를 제공하지 않습니다. 모노레포에서 서버는 apps/<app>/server.js입니다. CMD ["node", "apps/<app>/server.js"], exec 형식이므로 Node가 직접 SIGTERM을 수신합니다.

  5. uid/gid 1001로 실행하고 파일은 root가 소유합니다. nextjs:nodejs (1001:1001) 사용자를 생성하고, 복사된 파일은 root 소유로 둡니다(앱은 읽을 수 있지만 자체 코드를 변경할 수 없음). apps/<app>/.next/cache만 chown합니다. USER 1001:1001, 숫자 형식이므로 Kubernetes가 이름 확인 없이 runAsNonRoot를 확인할 수 있습니다.

  6. 이미지는 읽기 전용 루트 파일시스템에서 작동해야 합니다. 유일한 런타임 쓰기는 .next/cache(이미지 최적화, ISR/패치 캐시)로 이동합니다. Kubernetes는 여기에 emptyDir을 마운트합니다. 로컬에서는 --tmpfs를 사용합니다. 다음 명령으로 테스트:

    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. runner에서 NODE_ENV=production, HOSTNAME=0.0.0.0, PORT=3000, NEXT_TELEMETRY_DISABLED=1을 설정합니다. HOSTNAME=0.0.0.0이 없으면 standalone 서버가 컨테이너 호스트 이름에 바인딩되어 프로브가 실패합니다.

  8. HEALTHCHECK는 busybox wget으로 /api/health를 확인합니다. docker run/Compose용입니다. Kubernetes는 이를 무시하고 동일한 경로에 차트의 프로브를 사용합니다.

  9. 빌드 인자는 빌드 시 인라인 처리되는 값(NEXT_PUBLIC_* 및 NEXT_DEPLOYMENT_ID)으로만 제한됩니다. NEXT_PUBLIC_*는 JS에 컴파일되므로 builder에서 ARG + ENV여야 하며, 이미지가 실행되는 모든 환경에서 동일합니다. 환경별 모든 것은 차트의 런타임 환경 변수입니다. 추가하려면:

    ARG NEXT_PUBLIC_APP_NAME
    ENV NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME}
    

    …그리고 두 워크플로우에 build-args를 추가하고, NEXT_PUBLIC_*는 이미 turbo의 env에 있습니다.

  10. 시크릿은 절대 ARG, ENV 또는 COPY에 넣지 않습니다. 이미지 히스토리에 남게 됩니다. BuildKit 시크릿 마운트를 사용하면 해당 RUN 동안에만 존재합니다:

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

    템플릿의 빌드 단계는 이미 선택적 next_server_actions_key 시크릿을 이 방식으로 선언합니다.

  11. BuildKit 캐시 마운트로 pnpm 스토어를 캐시합니다. fetch/install/dlx에서 --mount=type=cache,id=pnpm-store,target=/pnpm/store를 사용하고 pnpm_config_store_dir=/pnpm/store를 설정합니다. 스토어는 동일한 빌더에서 빌드 간에 유지되며 레이어에 포함되지 않습니다. CI에서는 레이어 캐싱이 type=gha입니다(github-actions 참조).

  12. 저장소 루트의 .dockerignore는 컨텍스트를 작게 유지하고 시크릿을 제외합니다. node_modules, .next, .turbo, dist, coverage, .env*, .git, .github, charts를 제외합니다. Windows 또는 macOS에서 node_modules를 포함하는 컨텍스트는 Linux 이미지에서 네이티브 패키지를 손상시킵니다.

  13. 멀티 아키텍처는 linux/amd64,linux/arm64이며 CI에서 빌드합니다. 로컬에서는 자신의 아키텍처로 빌드하십시오. 모든 스테이지는 pnpm 다운로드를 제외하고 아키텍처 중립적이며, pnpm 다운로드는 TARGETARCH에서 체크섬을 선택합니다.

  14. 레이블: Dockerfile은 org.opencontainers.image.title 및 licenses를 설정합니다. CI는 docker/metadata-action을 통해 source, revision, version, created를 추가합니다. 버전 레이블을 하드코딩하지 마십시오.

  15. 스테이지를 앱별 핵(hack) 없이 유지합니다. runner에 apk add(앱에 셸 도구 세트가 필요하지 않음), npm, 호스트에서 복사된 node_modules가 없어야 합니다. 앱이 런타임에 네이티브 도구를 필요로 하는 경우 주석으로 정당화하십시오.

디버깅

증상원인
빌드 중 extends: … doesn't resolve앱에 필요한 루트 수준 파일이 정리된 트리에 없음: 워크스페이스 패키지로 이동
deps에서 ERR_PNPM_OUTDATED_LOCKFILE매니페스트 변경 후 lockfile이 커밋되지 않음
ERR_PNPM_IGNORED_BUILDS새 종속성이 install 스크립트를 필요로 함: allowBuilds에서 결정
앱 시작, CSS/JS 없음.next/static 또는 public이 apps/<app>/에 복사되지 않음
상태 확인 실패, 로그는 정상HOSTNAME이 0.0.0.0이 아님
런타임 EROFS.next/cache 외부에 쓰는 항목이 있음: 찾아서 수정, --read-only를 포기하지 마십시오
NEXT_PUBLIC_X가 비어 있음빌드 인자로 전달되지 않음(빌드 시 주입됨)

검증

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>   # ~30초 후 healthy
docker rm -f <app>

출처

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.

관련 스킬