MAATRIX / Блог / Strapi (headless CMS): установка на VPS

Strapi (headless CMS): установка на VPS

Strapi (headless CMS): установка на VPS

MAATRIX

Если вы делаете сайт на 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 — миграции схемы иногда необратимы без отката из бэкапа.