Розгортання додатків Next.js
Розгортання Next.js додатку означає вибрати, де і як запускати те, що видає next build: статичні файли, серверні маршрути та API endpoints.
Теорія
TL;DR
- Vercel автоматично розділяє вихід: статичні сторінки йдуть на CDN edge, SSR та API маршрути запускаються як serverless функції
- Docker упаковує все в один Node.js процес - масштабування ти налаштовуєш сам
- Статичний експорт (static export) генерує HTML/CSS/JS без сервера, але SSR та API маршрути не працюватимуть
- Правило вибору: Vercel для швидкого старту без операційного overhead; Docker для власної інфраструктури (Kubernetes, on-prem); статичний експорт якщо нема серверної логіки
Швидкий приклад
Найшвидший шлях до продакшену - одна команда через Vercel CLI:
npm install -g vercel@latest
vercel --prod
# Вивід:
# > Vercel CLI 33.6.2
# > ✅ Production: https://your-app-abc123.vercel.app
# > 📝 Deployed in 45sVercel зчитує next.config.js, розділяє статичні і динамічні сторінки та відправляє кожну до відповідної інфраструктури. Жодного додаткового конфіга не потрібно.
Головна різниця
Vercel нативно підтримує гібридний вихід Next.js: SSG сторінки йдуть на CDN edge, SSR та API маршрути запускаються як serverless Lambda. Docker дає один Node.js процес що запускає server.js - повний контроль, але HTTPS, CDN і масштабування ти налаштовуєш сам. Статичний експорт прибирає сервер повністю: дешевий хостинг, але без динамічного рендерингу.
Коли що використовувати
- Прототипи і лендінги: Vercel free tier - миттєвий deploy, HTTPS вже є, preview URL для кожної гілки
- E-commerce або дашборди з SSR: Vercel або Docker з
output: 'standalone' - Власна інфраструктура на Kubernetes або on-prem: Docker standalone - деплоїш на EC2, Fly.io або будь-який кластер
- Документація і статичні блоги: статичний експорт на Netlify, S3 або GitHub Pages за майже нульову вартість
- Існуючий VPS з Node.js:
npm startабоpm2, але для продакшену переходь на standalone режим
Таблиця порівняння
| Характеристика | Vercel | Docker (Standalone) | Статичний експорт | Node сервер |
|---|---|---|---|---|
| Налаштування | Одна команда | Dockerfile + build | next build + upload | pm2/node cluster |
| Підтримка SSR | Повна (Edge + Serverless) | Повна (один процес) | Відсутня | Повна |
| Вартість | Безкоштовний тир, потім $20+/міс | EC2 ~$10/міс | ~$0 (S3/Netlify) | Вартість сервера |
| Масштабування | Автоматичне | Вручну (Swarm/K8s) | Безмежне (CDN) | Вручну |
| Власний middleware | Edge Middleware | Будь-яка Node.js бібліотека | Відсутній | Повний Node.js |
| Підходить для | Команди без DevOps | Монолити на AWS/GCP | Сайти-документації | Внутрішні інструменти |
Як працює збірка всередині
next build сканує директорії app/ або pages/, попередньо рендерить SSG маршрути в .next/static/ і збирає SSR та API маршрути в .next/server/app/. При output: 'standalone' Next.js додатково копіює мінімальні залежності для запуску server.js без повної папки node_modules під час роботи. Саме тому multi-stage Docker build скорочує образ з ~1.2GB до менш ніж 150MB - фінальний контейнер отримує тільки те, що реально потрібно.
Типові помилки
Відсутність output: 'standalone' для Docker
Без цього параметра Docker образу потрібна повна папка .next і node_modules підключені окремо. Локально може працювати, але в більшості контейнерних середовищ, де копіюють тільки результат збірки, - ні.
// next.config.js
const nextConfig = {
output: 'standalone', // обов'язково для Docker і Fly.io
};
module.exports = nextConfig;Прив'язка до localhost всередині Docker
Якщо контейнер запускається, але запити ззовні не проходять - сервер прив'язаний до 127.0.0.1 замість 0.0.0.0. Я бачив як команди витрачали кілька годин на це - а рішення це два рядки в Dockerfile. Standalone server.js читає process.env.HOSTNAME:
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
CMD ["node", "server.js"]getServerSideProps зі статичним експортом
Статичний експорт і getServerSideProps несумісні - збірка впаде з помилкою. Переходь на generateStaticParams в App Router або на серверне розгортання.
Відсутній NODE_ENV=production в Docker
Dev режим завантажує більше коду, запускає додаткові перевірки і може відкривати налагоджувальну інформацію. Завжди додавай:
ENV NODE_ENV=productionEdge Runtime + Node.js модуль crypto
Якщо перемістити API маршрут на Edge Runtime і він використовує Node.js модуль crypto - отримаєш помилку. Edge запускається на V8 isolates без повного ядра Node.js. Використовуй Web Crypto API:
// ❌ Не працює на Edge Runtime
import crypto from 'crypto';
const hash = crypto.createHmac('sha256', secret).update(body).digest('hex');
// ✅ Працює на Edge Runtime
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));Де зустрічається в реальних проектах
- nextjs.org запущений на Vercel і обслуговує мільйони запитів на день через Edge
- Vercel Commerce (офіційний Shopify-подібний стартер) використовує Docker standalone для self-hosted розгортань на Fly.io та EC2
- Документація Tailwind CSS - статичний експорт на S3 + CloudFront
- Leonardo.ai запускає Next.js фронтенд на власному Kubernetes кластері для GPU workloads
Питання на співбесіді
Q: Яка різниця між output: 'standalone' і стандартною збіркою?
A: Стандартна збірка виводить .next/ і очікує node_modules під час запуску. Standalone копіює тільки файли потрібні для запуску server.js в .next/standalone/ - можна розгортати один каталог без повторного встановлення залежностей.
Q: Як Vercel обробляє ISR на відміну від SSR при великому навантаженні?
A: ISR оновлює сторінки через webhook до Edge кешу, тому більшість користувачів отримують кешовану відповідь. SSR викликає Lambda на кожен запит - на маршрутах з низьким трафіком можливі cold starts.
Q: Навіщо використовувати Edge Runtime замість Node.js runtime на Vercel?
A: Edge запускається на V8 isolates розподілених глобально - приблизно 10ms latency по всьому світу. Node.js runtime прив'язаний до конкретних регіонів AWS і має більші cold starts. Компроміс: Edge не має доступу до повного Node.js API, включаючи вбудовані модулі fs та crypto.
Q: Які реальні цифри зменшення розміру Docker образу при multi-stage build?
A: Single-stage build з повним node_modules важить близько 1.2GB. З output: 'standalone' і multi-stage Dockerfile фінальний образ зазвичай 140-160MB. Це приблизно 85% менше.
Q: Твій деплой на Vercel впирається в 15-секундний timeout через важку ML бібліотеку. Що робити?
A: Рознеси важку обробку в окрему serverless функцію або background job. Використовуй reserved concurrency щоб тримати Lambda теплою. Або виноси ML inference на зовнішній сервіс (наприклад Replicate) і звертайся до нього через HTTP з легкого API маршруту - в продакшені це скорочувало cold start з 7s до 1.2s.
Приклади
Базовий: розгортання на Vercel однією командою
# В корені Next.js проекту
npm install -g vercel@latest
vercel --prod
# Vercel визначає Next.js, збирає його, розділяє статичний/динамічний вихід
# і повертає production URL з HTTPS і глобальним CDNJodного конфіга не потрібно. Vercel читає існуючий next.config.js і сам вирішує що куди направити.
Середній рівень: Docker Standalone для self-hosted SSR
// next.config.js
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;FROM node:20-alpine AS base
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
FROM base AS builder
WORKDIR /app
COPY /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY /app/.next/standalone ./
COPY /app/.next/static ./.next/static
COPY /app/public ./public
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]docker build -t my-nextjs-app .
docker run -p 3000:3000 my-nextjs-appMulti-stage build скорочує образ з ~1.2GB до приблизно 150MB. SSR сторінки, API маршрути і статичні ресурси - все працює. Деплоїш цей образ на EC2, Fly.io або будь-який Kubernetes кластер.
Просунутий рівень: webhook валідація на Edge Runtime
Типова помилка - перемістити Stripe webhook handler на Edge Runtime без оновлення crypto коду.
// app/api/webhook/route.ts
// ❌ Не працює на Edge Runtime - Node crypto недоступний
import crypto from 'crypto';
export const runtime = 'edge';
export async function POST(req: Request) {
const sig = req.headers.get('x-signature') ?? '';
const body = await req.text();
// Кидає: "The 'crypto' module is not available in Edge Runtime"
const hash = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET!)
.update(body)
.digest('hex');
}// ✅ Рішення: Web Crypto API працює і на Edge, і на Node.js runtime
export const runtime = 'edge';
export async function POST(req: Request) {
const sig = req.headers.get('x-signature') ?? '';
const body = await req.text();
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(process.env.WEBHOOK_SECRET!),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(body));
const hashHex = Array.from(new Uint8Array(signature))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
if (sig !== `sha256=${hashHex}`) {
return Response.json({ error: 'Invalid signature' }, { status: 401 });
}
return Response.json({ ok: true });
}crypto.subtle доступний і на Edge Runtime, і в Node.js - тому цей код також працює якщо пізніше переключитись назад на Node runtime без жодних змін.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.