Skip to main content

Розгортання додатків 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:

bash
npm install -g vercel@latest vercel --prod # Вивід: # > Vercel CLI 33.6.2 # > ✅ Production: https://your-app-abc123.vercel.app # > 📝 Deployed in 45s

Vercel зчитує 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 режим

Таблиця порівняння

ХарактеристикаVercelDocker (Standalone)Статичний експортNode сервер
НалаштуванняОдна командаDockerfile + buildnext build + uploadpm2/node cluster
Підтримка SSRПовна (Edge + Serverless)Повна (один процес)ВідсутняПовна
ВартістьБезкоштовний тир, потім $20+/місEC2 ~$10/міс~$0 (S3/Netlify)Вартість сервера
МасштабуванняАвтоматичнеВручну (Swarm/K8s)Безмежне (CDN)Вручну
Власний middlewareEdge 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 підключені окремо. Локально може працювати, але в більшості контейнерних середовищ, де копіюють тільки результат збірки, - ні.

js
// 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:

dockerfile
ENV PORT=3000 ENV HOSTNAME=0.0.0.0 CMD ["node", "server.js"]

getServerSideProps зі статичним експортом

Статичний експорт і getServerSideProps несумісні - збірка впаде з помилкою. Переходь на generateStaticParams в App Router або на серверне розгортання.

Відсутній NODE_ENV=production в Docker

Dev режим завантажує більше коду, запускає додаткові перевірки і може відкривати налагоджувальну інформацію. Завжди додавай:

dockerfile
ENV NODE_ENV=production

Edge Runtime + Node.js модуль crypto

Якщо перемістити API маршрут на Edge Runtime і він використовує Node.js модуль crypto - отримаєш помилку. Edge запускається на V8 isolates без повного ядра Node.js. Використовуй Web Crypto API:

js
// ❌ Не працює на 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 однією командою

bash
# В корені Next.js проекту npm install -g vercel@latest vercel --prod # Vercel визначає Next.js, збирає його, розділяє статичний/динамічний вихід # і повертає production URL з HTTPS і глобальним CDN

Jodного конфіга не потрібно. Vercel читає існуючий next.config.js і сам вирішує що куди направити.

Середній рівень: Docker Standalone для self-hosted SSR

js
// next.config.js const nextConfig = { output: 'standalone', }; module.exports = nextConfig;
dockerfile
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 --from=deps /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 --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ 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"]
bash
docker build -t my-nextjs-app . docker run -p 3000:3000 my-nextjs-app

Multi-stage build скорочує образ з ~1.2GB до приблизно 150MB. SSR сторінки, API маршрути і статичні ресурси - все працює. Деплоїш цей образ на EC2, Fly.io або будь-який Kubernetes кластер.

Просунутий рівень: webhook валідація на Edge Runtime

Типова помилка - перемістити Stripe webhook handler на Edge Runtime без оновлення crypto коду.

ts
// 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'); }
ts
// ✅ Рішення: 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 без жодних змін.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?