Next.js-проект, развёрнутый обычным способом, требует на сервере всю папку node_modules. В ней сотни пакетов, нужных для сборки, но никогда не используемых во время работы: TypeScript, линтеры, тестовые библиотеки и прочее. На маленьком VPS это занимает место на диске, удлиняет деплой и утяжеляет Docker-образы. Режим standalone решает именно эту проблему: во время сборки Next.js отслеживает файлы, которые приложение действительно использует, и собирает их в отдельную папку.
Как работает standalone
При сборке Next.js анализирует, какие модули импортирует каждая страница и каждый API route. Если standalone включён, появляется папка .next/standalone. В ней лежит минимальный server.js и урезанный node_modules только с нужными пакетами. Эта папка не нуждается в команде next start и запускается обычным node server.js. Поэтому на сервер достаточно перенести только её, а не весь проект.
Включаем standalone
Настройка занимает одну строку. Добавьте параметр output в next.config.js или next.config.mjs и пересоберите проект.
// next.config.mjs
const nextConfig = {
output: "standalone",
};
export default nextConfig;
npm ci
npm run build
ls .next/standalone
После сборки в .next/standalone окажутся server.js, package.json, node_modules и вложенная папка .next. Чтобы сравнить объём, выполните du -sh node_modules .next/standalone: разница зависит от проекта, но обычно заметна.
Самый забываемый шаг: public и static
Две вещи в папку standalone автоматически не попадают: папка public и CSS- и JavaScript-чанки из .next/static. Next.js оставляет их намеренно, считая, что отдавать такие файлы напрямую через Nginx или CDN эффективнее. Если их не скопировать, сайт откроется, но без стилей, без картинок и с неработающими кнопками. Именно здесь чаще всего ошибаются в первый раз.
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
Хорошая привычка — добавить эти команды в скрипт сборки, чтобы не забывать о них при следующих деплоях. В package.json это можно сделать так.
"scripts": {
"build": "next build",
"postbuild": "cp -r public .next/standalone/ && cp -r .next/static .next/standalone/.next/"
}
Запуск на сервере
Standalone-сервер берёт порт и адрес из переменных окружения. PORT задаёт порт, HOSTNAME — адрес, на котором слушать. За Nginx безопаснее указать 127.0.0.1, а внутри Docker нужен 0.0.0.0.
cd /var/www/misol-next/.next/standalone
PORT=3000 HOSTNAME=127.0.0.1 NODE_ENV=production node server.js
Для постоянной работы удобен ecosystem-файл PM2: приложение держится в фоне, поднимается после падения и перезапускается при превышении лимита памяти.
// ecosystem.config.js
module.exports = {
apps: [{
name: "misol-next",
cwd: "/var/www/misol-next/.next/standalone",
script: "server.js",
max_memory_restart: "400M",
env: { NODE_ENV: "production", PORT: 3000, HOSTNAME: "127.0.0.1" }
}]
};
Сборка вне сервера
Главное преимущество standalone в том, что собрать проект можно на мощном компьютере или в CI, а на сервер отправить готовую папку. Маленький VPS больше не страдает от нехватки памяти при сборке и только запускает готовое приложение. Важное условие одно: операционная система и архитектура процессора сборочной машины должны совпадать с серверными, потому что в некоторых пакетах, например в sharp для обработки изображений, есть платформозависимые бинарники.
npm run build
tar -czf release.tar.gz -C .next/standalone .
scp release.tar.gz deploy@example.uz:/var/www/misol-next/releases/
Standalone и Docker
В Docker standalone особенно полезен: в финальном образе остаются только нужные для работы файлы. Ниже упрощённый многоэтапный Dockerfile: на первом этапе идёт сборка, на втором копируется только результат.
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
COPY --from=build /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
Практический пример: мебельный магазин в Намангане
Мебельный магазин в Намангане держит сайт-каталог на Next.js на маленьком VPS с 1 ГБ памяти. Раньше сборка шла прямо на сервере, память каждый раз заканчивалась, и сайт на несколько минут переставал отвечать. После перехода на standalone проект собирается на компьютере разработчика, на сервер отправляется готовый архив, а PM2 перезагружает приложение. Вместо всего node_modules на сервере теперь лежат только нужные пакеты, а деплой занимает меньше минуты.
Частые проблемы
- Сайт открывается без стилей: не скопирована
.next/static. - Картинки и favicon отдают 404: не скопирована папка
public. - В Docker сайт недоступен снаружи: не задан
HOSTNAME=0.0.0.0. - Не работают переменные окружения: файлы
.envне попадают в standalone автоматически, задайте их на сервере отдельно.
Если хотите запустить Next.js на сервере с небольшими ресурсами, хороший старт — маленький VPS в кабинете sayt.uz и режим standalone. Если ресурсов станет не хватать, тариф можно будет увеличить позже.