Пошук уроків, статей та іншого контенту
Створите Docker-образ Next.js із multi-stage build і підготуєте його до запуску в production.
Docker-образ для production має містити лише те, що необхідно для запуску застосунку:
production-залежності;
зібраний код Next.js;
статичні файли;
мінімальне середовище виконання Node.js;
окремого користувача без root-привілеїв.
Якщо перенести в образ увесь проєкт разом із кешами, вихідними файлами та dev-залежностями, образ буде більшим, а його складання — повільнішим.
Для цього використовується multi-stage build — Dockerfile із кількома етапами. Кожен етап виконує окреме завдання, але у фінальний образ копіюються лише потрібні результати.
Next.js має спеціальний режим standalone. Він створює мінімальний сервер і набір залежностей, необхідних для production-запуску.
У корені проєкту створіть або змініть next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;Після виконання next build у директорії .next з’явиться:
.next/standalone — мінімальний сервер застосунку;
.next/static — статичні файли Next.js;
інші файли, створені під час збірки.
Файл server.js усередині .next/standalone є точкою входу production-сервера.
Створіть у корені Next.js-проєкту файл Dockerfile:
# Етап 1: встановлення залежностей
FROM node:20-alpine AS deps
WORKDIR /app
# Додаємо бібліотеку сумісності для деяких Node.js-залежностей
RUN apk add --no-cache libc6-compat
# Копіюємо лише файли залежностей, щоб використовувати кеш Docker
COPY package.json package-lock.json ./
# Встановлюємо точні версії залежностей із lock-файлу
RUN npm ci
# Етап 2: збирання застосунку
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Гарантуємо наявність директорії public для наступного етапу
RUN mkdir -p public
# Створюємо production-збірку Next.js
RUN npm run build
# Етап 3: фінальний production-образ
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
# Створюємо користувача без root-привілеїв
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs
# Копіюємо мінімальний сервер і його залежності
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
# Копіюємо статичні файли Next.js
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Копіюємо публічні статичні файли застосунку
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]depsНа цьому етапі:
використовується базовий образ Node.js;
копіюються тільки package.json і package-lock.json;
виконується npm ci.
Це дає змогу Docker повторно використовувати кеш. Якщо змінюються файли застосунку, але залежності залишаються такими самими, етап встановлення залежностей не запускається повторно.
builderНа цьому етапі:
копіюються встановлені залежності;
копіюється код застосунку;
виконується npm run build.
Результатом є production-збірка Next.js, зокрема директорія .next/standalone.
runnerЦе фінальний образ, який запускатиметься в production.
У нього не копіюються:
вихідні файли застосунку;
dev-залежності;
кеш npm;
інші проміжні файли зі стадії збирання.
У фінальний образ потрапляють лише standalone-сервер, статичні ресурси та директорія public.
.dockerignoreСтворіть у корені проєкту файл .dockerignore:
node_modules
.next
.git
.gitignore
Dockerfile
.dockerignore
npm-debug.log*
.env*Цей файл визначає, які файли не потрібно передавати в Docker build context.
Зокрема, локальна директорія node_modules не повинна копіюватися в образ. Залежності встановлюються всередині контейнера командою npm ci.
Файли .env* також не варто копіювати автоматично. Секрети не повинні потрапляти в Docker-образ під час збирання.
Переконайтеся, що в package.json є скрипт build:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}Зберіть образ командою:
docker build -t next-app:production .Параметри команди:
-t next-app:production задає ім’я та тег образу;
. визначає поточну директорію як build context.
Після завершення збирання перевірте створені образи:
docker imagesЗапустіть контейнер:
docker run --name next-app -p 3000:3000 next-app:productionПорт 3000 усередині контейнера підключається до порту 3000 на локальному комп’ютері.
Застосунок буде доступний за адресою:
http://localhost:3000Зупинити контейнер можна командою:
docker stop next-appВидалити зупинений контейнер:
docker rm next-appПереглянути журнали контейнера:
docker logs next-appstandaloneБез output: 'standalone' для запуску Next.js у фінальному образі зазвичай потрібно копіювати більше файлів і залежностей.
Режим standalone:
створює мінімальний сервер;
визначає залежності, потрібні для запуску;
зменшує розмір фінального образу;
дає змогу запускати застосунок командою node server.js.
Важливо копіювати не лише .next/standalone, а й .next/static. Без цього браузер не отримає JavaScript, CSS та інші статичні ресурси Next.js.
Так само потрібно копіювати public, якщо застосунок використовує файли з цієї директорії.
Порівняйте розмір production-образу:
docker image ls next-app:productionMulti-stage build зазвичай дає менший образ, ніж підхід, у якому всі залежності, вихідні файли та інструменти збирання залишаються у фінальному контейнері.
Розмір може відрізнятися залежно від:
базового образу;
кількості production-залежностей;
наявності нативних модулів;
статичних файлів застосунку.
Змінні середовища, які потрібні серверу під час запуску, можна передати через docker run:
docker run --name next-app \
-p 3000:3000 \
-e DATABASE_URL="postgresql://user:password@db:5432/app" \
next-app:productionУ коді Node.js серверні змінні доступні через process.env.
Не записуйте паролі, токени та ключі безпосередньо в Dockerfile. Значення, передані через ENV у Dockerfile, можуть стати частиною метаданих образу.
Для оновлення застосунку потрібно повторити процес:
docker build -t next-app:production .
docker stop next-app
docker rm next-app
docker run --name next-app -p 3000:3000 next-app:productionПід час збирання Docker використовує кеш, якщо відповідні інструкції та вхідні файли не змінилися.
Наприклад, зміна файлів у app або pages не повинна спричиняти повторне виконання npm ci, оскільки package.json і package-lock.json копіюються окремим кроком до копіювання всього коду.
package-lock.jsonКоманда npm ci потребує lock-файл. Якщо його немає, створіть його локально:
npm installПісля цього додайте package-lock.json до репозиторію.
.next/staticЯкщо скопіювати тільки .next/standalone, сторінка може завантажитися без стилів або JavaScript.
У фінальному етапі має бути:
COPY --from=builder /app/.next/static ./.next/staticpublicФайли з public не входять до .next/standalone. Якщо застосунок використовує зображення, favicon або інші публічні файли, потрібно скопіювати цю директорію окремо.
Запуск від root не потрібен для звичайного Next.js-сервера. У Dockerfile створено користувача nextjs і вибрано його за допомогою:
USER nextjsЦе зменшує наслідки потенційної уразливості в застосунку або його залежностях.
Перевірте, що:
Next.js слухає адресу 0.0.0.0;
у контейнері задано PORT=3000;
під час запуску використовується -p 3000:3000.
Значення HOSTNAME=0.0.0.0 у Dockerfile дає змогу серверу приймати підключення не лише з localhost усередині контейнера.
Multi-stage build розділяє встановлення залежностей, збирання та запуск.
У deps встановлюються залежності, у builder створюється production-збірка, а runner містить лише необхідні файли.
output: 'standalone' створює мінімальний сервер Next.js.
Для запуску потрібно скопіювати .next/standalone, .next/static і public.
Файл .dockerignore зменшує build context і не допускає потрапляння зайвих файлів до образу.
Production-контейнер має запускатися від непривілейованого користувача.
Образ збирається командою docker build, а контейнер запускається через docker run.