Nuxt — фреймворк на основе Vue, который из одного и того же кода позволяет собрать и динамический сайт с рендерингом на сервере, и полностью статический сайт. Этот выбор нужно сделать до деплоя, потому что от него зависит, какой сервер понадобится. Если страницы при каждом запросе берут свежие данные из базы, пользователи входят в аккаунт или нужен серверный API, нужен VPS с Node.js. Если сайт в основном состоит из неизменных страниц, его можно сгенерировать в статический HTML и разместить даже на обычном хостинге.
Результат сборки Nuxt: папка .output
Nuxt 3 и новее используют серверный движок Nitro. После команды npm run build в корне проекта появляется папка .output. В ней есть часть server и часть public с файлами для браузера. Папка самодостаточна: для запуска на сервере не нужны исходники проекта и полный node_modules, потому что Nitro собирает нужные зависимости внутрь.
npm ci
npm run build
node .output/server/index.mjs
По умолчанию сервер стартует на порту 3000. Порт и адрес меняются переменными окружения PORT и HOST или NITRO_PORT и NITRO_HOST. За Nginx безопаснее слушать только адрес 127.0.0.1.
Деплой на VPS в режиме SSR
На сервере должна быть установлена LTS-версия Node.js. Получаем код через Git и собираем проект. На маленьком сервере сборка может упасть из-за нехватки памяти; в этом случае добавьте swap или соберите проект на своём компьютере и отправьте на сервер только папку .output. Во втором случае операционная система и архитектура сборочной машины и сервера должны совпадать.
cd /var/www
git clone https://github.com/misol/misol-nuxt.git
cd misol-nuxt
npm ci
npm run build
Постоянная работа через PM2
Чтобы приложение не останавливалось при закрытии терминала и само запускалось после перезагрузки сервера, используем PM2. В документации Nuxt тоже приведён пример ecosystem-файла для PM2. В варианте ниже включён cluster-режим: PM2 запустит несколько копий по числу ядер сервера.
// ecosystem.config.cjs
module.exports = {
apps: [{
name: "misol-nuxt",
port: "3000",
exec_mode: "cluster",
instances: "max",
script: "./.output/server/index.mjs",
env: {
HOST: "127.0.0.1",
NUXT_PUBLIC_API_BASE: "https://api.example.uz"
}
}]
};
npm install -g pm2
pm2 start ecosystem.config.cjs
pm2 save
pm2 startup
В cluster-режиме не храните сессии и кеш в памяти процесса, иначе в каждой копии они будут разными. Если приложение к этому не готово, установите instances в 1.
Runtime config: настройка без пересборки
Раздел runtimeConfig в nuxt.config.ts можно переопределять переменными окружения во время работы. Для этого имя переменной должно начинаться с NUXT_, а для значений, доступных в браузере, используется NUXT_PUBLIC_. Это очень удобно: при смене адреса API или секретного ключа не нужно пересобирать весь проект, достаточно перезапустить PM2 с новым окружением.
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
apiSecret: "",
public: {
apiBase: ""
}
}
});
NUXT_API_SECRET=... NUXT_PUBLIC_API_BASE=https://api.example.uz node .output/server/index.mjs
Никогда не кладите секреты в public: всё, что там лежит, попадает в код страницы и в браузер.
Nginx перед приложением
Приложение работает на внутреннем порту, а внешние запросы принимает Nginx. Он подключает SSL-сертификат, включает сжатие и передаёт запросы в Nuxt. Сертификат можно бесплатно получить у Let's Encrypt через Certbot.
server {
listen 80;
server_name example.uz;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d example.uz
Статическая генерация: хостинг без Node.js
Если сайту не нужна серверная обработка каждого запроса, команда nuxi generate заранее превратит все страницы в HTML-файлы. Результат записывается в .output/public, и его можно загрузить как обычные файлы на любой веб-сервер, в том числе на хостинг с панелью ISPmanager. Такому сайту не нужен процесс Node.js, поэтому он потребляет мало ресурсов и открывается очень быстро.
npx nuxi generate
ls .output/public
Обычно nuxi generate создаёт отдельную папку для каждого маршрута, но динамические страницы, о которых сборка заранее не знает, не генерируются, и при прямом переходе на них можно получить 404. Такие страницы добавьте в список через nitro.prerender.routes.
Практический пример: гостиница в Бухаре
Небольшая гостиница в Бухаре заказала сайт misol.uz на Nuxt. Описания номеров, галерея и страница контактов меняются редко, поэтому на первом этапе сайт сгенерировали через nuxi generate и разместили на обычном хостинге. Позже, когда понадобились онлайн-бронирование и показ занятости номеров в реальном времени, проект перенесли на VPS в режиме SSR, а PM2 и Nginx настроили по описанной схеме. Сам код почти не изменился — поменялись только команда сборки и способ размещения.
Если не знаете, что выбрать, начните с простого: для статического сайта хватит хостинга sayt.uz, а когда появятся динамические функции, можно перейти на VPS.