Strapi (headless CMS): установка на VPS
Если вы делаете сайт на React или Vue, мобильное приложение с общим бэкендом контента или просто устали от того, что WordPress тянет за собой шаблоны, плагины и весь фронтенд в одном флаконе — рано или поздно упрётесь в headless CMS. Strapi — один из самых популярных вариантов на Node.js, и его вполне реально поставить на обычный VPS за 20-30 минут, если знать порядок действий. Ниже — рабочая установка без магии: Node.js, PostgreSQL, продакшен-сборка, PM2 и Nginx перед всем этим.
Содержание
Зачем headless CMS, если есть WordPress
Обычная CMS вроде WordPress отдаёт готовую HTML-страницу: шаблон, стили, контент — всё вместе. Headless CMS делает только одну вещь — хранит и отдаёт контент через API (обычно REST или GraphQL), а как этот контент выглядит и на чём рендерится — решает уже фронтенд. Один и тот же контент из Strapi может одновременно кормить сайт на Next.js, мобильное приложение на React Native и виджет на отдельном лендинге — без дублирования данных и без переписывания бэкенда под каждый канал.
Это не значит, что headless CMS всегда лучше — для обычного блога или корпоративного сайта без экзотики WordPress на VPS поставится быстрее и обойдётся дешевле по ресурсам. Strapi имеет смысл, когда у вас несколько фронтендов на один контент, команда фронтенд-разработчиков хочет работать с JSON, а не с PHP-шаблонами, или архитектура проекта изначально build API-first.
Минусы тоже есть: Strapi — это Node.js-приложение, которое нужно собирать, деплоить и обновлять как обычный сервис, а не просто залить файлы по FTP. Для маленького проекта это может быть избыточной сложностью — учитывайте это до того, как выбирать стек.
Подготовка сервера и установка Node.js
Понадобится VPS с Ubuntu 24.04, минимум 1 vCPU / 2 ГБ RAM для тестового проекта (для продакшена с реальной нагрузкой закладывайте от 2 vCPU / 4 ГБ — сборка Strapi при npm run build сама по себе прожорлива по памяти). Заходим по SSH под пользователем с sudo.
Ставим Node.js через официальный репозиторий NodeSource — на момент написания Strapi 5 официально поддерживает Node.js в диапазоне примерно 18–22 LTS, но список точных поддерживаемых версий меняется от релиза к релизу, поэтому перед установкой сверьтесь с requirements в документации Strapi:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v
npm -v
Дополнительно поставим build-essential — некоторые нативные зависимости Strapi (например, обвязка вокруг sqlite или sharp для обработки изображений) собираются из исходников:
sudo apt update
sudo apt install -y build-essential python3
Создаём отдельного системного пользователя под приложение — не запускать Node.js-процессы от root:
sudo adduser --disabled-password --gecos "" strapi
sudo su - strapi
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPSСоздание проекта Strapi
Дальше всё происходит от имени пользователя strapi. Создаём проект через официальный генератор:
npx create-strapi-app@latest my-project
Мастер спросит: использовать ли Strapi Cloud (отвечаем "нет" — ставим локально), какую базу данных выбрать (об этом ниже), нужен ли пример контента для старта. Установка занимает несколько минут — тянутся зависимости, собирается админ-панель.
После установки заходим в директорию проекта и в режиме разработки убеждаемся, что всё поднялось:
cd my-project
npm run develop
По умолчанию Strapi слушает http://127.0.0.1:1337. На этом этапе стоит открыть порт временно через SSH-туннель (ssh -L 1337:127.0.0.1:1337 user@server) и зайти в браузере на /admin, чтобы создать первого администратора и убедиться, что панель открывается. После проверки npm run develop останавливаем — для продакшена нужен другой режим запуска.
PostgreSQL вместо SQLite для продакшена
По умолчанию Strapi в режиме разработки использует SQLite — файл базы прямо в проекте. Для продакшена это плохая идея: SQLite не рассчитан на параллельную запись под нагрузкой и плохо переживает бэкапы "на живую". Ставим полноценный PostgreSQL — если ставите с нуля, процесс подробно разобран в статье про установку PostgreSQL на VPS.
Кратко: создаём базу и пользователя под Strapi:
sudo -u postgres psql
CREATE DATABASE strapi_db;
CREATE USER strapi_user WITH ENCRYPTED PASSWORD 'сложный_пароль';
GRANT ALL PRIVILEGES ON DATABASE strapi_db TO strapi_user;
\q
В проекте Strapi правим .env (если создавали проект с SQLite — переустановите пакет pg и поправьте конфиг вручную; проще сразу на этапе create-strapi-app выбрать PostgreSQL в интерактивном мастере):
npm install pg
# .env
DATABASE_CLIENT=postgres
DATABASE_HOST=127.0.0.1
DATABASE_PORT=5432
DATABASE_NAME=strapi_db
DATABASE_USERNAME=strapi_user
DATABASE_PASSWORD=сложный_пароль
DATABASE_SSL=false
И в config/database.js (или .ts) проверяем, что клиент читается из переменных окружения — в свежих проектах Strapi это уже настроено по умолчанию, менять руками обычно не нужно.
Продакшен-сборка и запуск через PM2
Режим develop не предназначен для постоянной работы — он пересобирает админку на лету и потребляет лишнюю память. Для продакшена собираем статическую админ-панель один раз и запускаем в production-режиме:
NODE_ENV=production npm run build
NODE_ENV=production npm run start
Если сборка и старт проходят без ошибок — переводим процесс под управление PM2, чтобы он автоматически перезапускался при падении и при перезагрузке сервера. Ставим PM2 глобально (от sudo-пользователя, не от strapi):
sudo npm install -g pm2
Возвращаемся к пользователю strapi и запускаем:
cd ~/my-project
NODE_ENV=production pm2 start npm --name strapi -- run start
pm2 save
Автозапуск PM2 при рестарте сервера настраивается командой pm2 startup — она выведет строку с sudo, которую нужно выполнить от имени root:
pm2 startup systemd -u strapi --hp /home/strapi
Проверить, что процесс жив и не падает в рестарт-луп:
pm2 status
pm2 logs strapi --lines 50
Если видите бесконечные перезапуски — почти всегда это неверные переменные окружения (особенно APP_KEYS, API_TOKEN_SALT, ADMIN_JWT_SECRET — Strapi генерирует их автоматически при create-strapi-app, но при переносе проекта на сервер их нужно скопировать в .env вручную, иначе приложение откажется стартовать).
Nginx как reverse proxy и HTTPS
Strapi слушает 127.0.0.1:1337 — наружу этот порт открывать не нужно. Ставим перед ним Nginx как reverse proxy: он терминирует HTTPS, отдаёт запросы на локальный порт и попутно может кэшировать статику. Если Nginx на сервере ещё не настроен как обратный прокси — базовая конфигурация подробно разобрана в статье про Nginx как reverse proxy.
Конфиг для Strapi, /etc/nginx/sites-available/strapi:
server {
listen 80;
server_name cms.example.com;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:1337;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
client_max_body_size увеличен неспроста — через админку Strapi часто загружают медиафайлы, и дефолтный лимит Nginx в 1 МБ обрежет загрузку картинки уже на первом скриншоте для статьи.
Активируем конфиг и получаем сертификат:
sudo ln -s /etc/nginx/sites-available/strapi /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d cms.example.com
Подробности по автопродлению и типичным граблям Let's Encrypt — в отдельной статье про установку Let's Encrypt SSL на VPS.
Отдельно стоит проверить config/server.js в проекте Strapi — параметр url должен указывать на публичный домен (https://cms.example.com), иначе ссылки на загруженные файлы в API будут отдаваться с localhost.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать VPSНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Strapi точно бесплатный?
Community-издание бесплатное и open-source, этого достаточно для большинства проектов. Enterprise-версия с SSO, RBAC и дополнительным аудитом — платная, но для установки на свой VPS она не нужна.
Можно ли обойтись без PostgreSQL и оставить SQLite в продакшене?
Технически да, для очень маленьких проектов с низкой нагрузкой это работает. Но при параллельных запросах на запись (несколько редакторов правят контент одновременно) SQLite начинает давать блокировки, а бэкапы файла "на горячую" менее надёжны, чем pg_dump.
Сколько RAM реально нужно?
Для спокойной работы админки и API на небольшом проекте хватает 2 ГБ, но сама сборка (npm run build) на слабых VPS иногда упирается в память — если сборка падает без внятной ошибки, попробуйте временно добавить своп-файл или собрать проект на более мощной машине и перенести только собранный build-каталог.
Нужен ли отдельный процесс-менеджер, если сервер и так под systemd?
Можно запускать Strapi напрямую через systemd unit вместо PM2 — вопрос вкуса. PM2 удобнее для быстрого мониторинга (pm2 monit, ротация логов из коробки), systemd — если хотите единообразия с остальными сервисами на сервере.
Как обновлять Strapi без даунтайма?
Официальный путь — codemods через npx @strapi/upgrade. Перед любым мажорным апгрейдом обязательно делайте бэкап базы и .env — миграции схемы иногда необратимы без отката из бэкапа.