Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Розгортання додатків Next.js». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Розгортання Next.js додатку** зводиться до трьох варіантів: Vercel (керована платформа, нульова конфігурація), Docker з `output: 'standalone'` (власний хостинг, повна підтримка SSR) або статичний експорт (без сервера, будь-який CDN). ```bash vercel --prod # ✅ Production: https://your-app.vercel.app (45s) ``` **Головне:** для будь-якого розгортання поза Vercel спершу додай `output: 'standalone'` у `next.config.js`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Розгортання 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 режим ### Таблиця порівняння | Характеристика | 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` підключені окремо. Локально може працювати, але в більшості контейнерних середовищ, де копіюють тільки результат збірки, - ні. ```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 без жодних змін.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.