Пошук уроків, статей та іншого контенту
Створите production-образ Next.js із підтримкою standalone-збірки та оптимальним розподілом етапів Dockerfile.
Звичайна production-збірка Next.js створює каталог .next, але для запуску застосунку можуть також знадобитися:
node_modules;
package.json;
залежності, які використовує сервер;
файли конфігурації;
статичні ресурси з каталогу public.
Якщо скопіювати все це в production-образ, він буде більшим, ніж потрібно. Next.js має режим standalone, який формує мінімальний набір файлів для запуску серверної частини застосунку.
У standalone-збірці Next.js створює:
.next/standalone/
├── server.js
├── node_modules/
└── потрібні файли застосункуЗапуск відбувається без команди next start:
node .next/standalone/server.jsСтатичні файли та каталог public потрібно скопіювати окремо.
Увімкніть standalone-режим у файлі next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;Після цього команда npm run build створюватиме .next/standalone.
У package.json мають бути доступні стандартні скрипти:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}Для запуску standalone-збірки скрипт start не використовується безпосередньо. У Docker-контейнері запускатиметься файл .next/standalone/server.js.
Для production-образу зручно використовувати кілька етапів:
deps — встановлення залежностей;
builder — збирання Next.js;
runner — мінімальний production-образ.
Такий підхід називається багатоетапним збиранням. Файли та інструменти, потрібні лише під час збирання, не потрапляють у фінальний образ.
Створіть Dockerfile у корені проєкту:
# Етап 1: встановлення залежностей
FROM node:20-alpine AS deps
WORKDIR /app
# Копіюємо файли залежностей окремо для кращого кешування
COPY package.json package-lock.json ./
RUN npm ci
# Етап 2: збирання застосунку
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Створюємо production-збірку Next.js
RUN npm run build
# Етап 3: мінімальний production-образ
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
# Запускаємо застосунок від непривілейованого користувача
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs
# Копіюємо мінімальну standalone-збірку
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
# Статичні файли Next.js не копіюються в standalone автоматично
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Каталог public також потрібно копіювати окремо
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]У фінальному етапі робочим каталогом є /app, а standalone-файл:
/app/server.jsТому команда запуску має вигляд:
CMD ["node", "server.js"]а не:
CMD ["npm", "start"]publicКоманда COPY завершується помилкою, якщо джерельного каталогу не існує. Якщо у вашому проєкті немає public, видаліть цей рядок:
COPY --from=builder --chown=nextjs:nodejs /app/public ./publicЯкщо каталог public існує, його потрібно залишити, навіть якщо він поки що порожній.
.dockerignoreСтворіть .dockerignore, щоб непотрібні файли не потрапляли в Docker-контекст і не копіювалися на етапі збирання:
node_modules
.next
.git
.gitignore
Dockerfile
docker-compose.yml
npm-debug.log*
.env*
coverageКаталоги node_modules і .next не потрібно переносити з локального комп’ютера:
залежності встановлюються всередині контейнера;
production-збірка створюється на етапі builder.
Файли .env* не копіюються автоматично. Це допомагає не додати секрети до Docker-образу випадково.
Виконайте команду з кореня Next.js-проєкту:
docker build -t next-app:production .Docker послідовно виконає три етапи, але до фінального образу потраплять лише файли з етапу runner.
Перевірити розмір образу можна командою:
docker images next-app:productionЗапустіть контейнер і прив’яжіть порт контейнера 3000 до порту 3000 на комп’ютері:
docker run --rm -p 3000:3000 next-app:productionПісля запуску Next.js-сервер буде доступний на:
http://localhost:3000Порт можна змінити під час запуску контейнера:
docker run --rm -p 8080:3000 next-app:productionУ цьому випадку застосунок буде доступний на порту 8080 хоста, але всередині контейнера все ще працюватиме на 3000.
Змінні середовища для серверного коду можна передати під час запуску контейнера:
docker run --rm \
-p 3000:3000 \
-e API_URL=https://api.example.com \
next-app:productionУ серверному коді змінна буде доступна через process.env.API_URL.
Важливо розрізняти змінні:
звичайні змінні можуть використовуватися сервером під час роботи контейнера;
змінні з префіксом NEXT_PUBLIC_ вбудовуються у клієнтський JavaScript під час збирання.
Наприклад:
docker build \
--build-arg NEXT_PUBLIC_API_URL=https://api.example.com \
-t next-app:production .Але для використання ARG його потрібно явно оголосити в Dockerfile, а значення клієнтських змінних усе одно стає частиною зібраного застосунку. Секрети не можна зберігати у NEXT_PUBLIC_*.
У Dockerfile файли залежностей копіюються до вихідного коду:
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run buildЦе важливо для кешування:
якщо змінюється код, але не package-lock.json, Docker може повторно використати шар із npm ci;
якщо змінюються файли залежностей, залежності буде встановлено заново.
Не копіюйте спочатку весь проєкт, а потім запускайте npm ci, якщо хочете ефективно використовувати кеш.
Після локального виконання:
npm run buildмають з’явитися такі каталоги:
.next/standalone
.next/staticУ .next/standalone повинен бути файл:
.next/standalone/server.jsСаме цей файл запускається у фінальному контейнері.
next start замість standalone-сервераДля standalone-збірки використовуйте:
CMD ["node", "server.js"]Якщо запустити next start, у фінальному образі можуть бути відсутні потрібні файли або команда не зможе знайти повну .next-збірку.
outputЯкщо в next.config.js немає:
output: 'standalone'каталог .next/standalone не буде створений, а Dockerfile не знайде файли для копіювання.
.next/staticStandalone-збірка не містить статичні файли Next.js автоматично. Без цього рядка:
COPY --from=builder /app/.next/static ./.next/staticстилі, JavaScript-чанки або інші статичні ресурси можуть не завантажуватися.
publicФайли з public також потрібно переносити у фінальний образ:
COPY --from=builder /app/public ./publicІнакше зображення, favicon та інші публічні ресурси будуть недоступні.
node_modulesНе копіюйте локальний node_modules у Docker-контекст. Він може бути встановлений для іншої операційної системи або іншої архітектури.
Додайте його до .dockerignore і встановлюйте залежності всередині контейнера за допомогою:
RUN npm ciЗа замовчуванням процес у контейнері може працювати від користувача root. Для production-застосунку краще створити окремого користувача і вказати його через:
USER nextjsЦе обмежує наслідки можливих проблем із безпекою застосунку.
Для мінімального production-образу Next.js використовуйте output: 'standalone'.
Розділяйте Dockerfile на етапи встановлення залежностей, збирання та запуску.
У фінальний образ копіюйте .next/standalone, .next/static і public.
Запускайте standalone-сервер через node server.js.
Використовуйте .dockerignore, щоб не переносити локальні залежності та зайві файли.
Встановлюйте залежності всередині контейнера та запускайте застосунок від непривілейованого користувача.