Skip to content

Установка на Ubuntu VDS ​

Эта инструкция описывает чистую установку KaevCMS на отдельный VDS с Ubuntu Server 24.04 LTS, nginx, PHP 8.3-FPM и MySQL.

Ubuntu 24.04 выбрана как базовая система KaevCMS, потому что её штатные репозитории содержат PHP 8.3 — ту же минимальную и фиксированную платформу, которая указана в composer.json. Ubuntu 26.04 LTS поставляется с PHP 8.5; её можно проверять отдельно, но до прохождения полного quality-gate она не считается основной проверенной платформой.

Итоговая структура ​

text
/var/www/kaevcms/
├── app/
├── bootstrap/
├── config/
├── public/          ← единственный Document Root nginx
├── storage/
├── vendor/
├── .env
└── artisan

Нельзя направлять nginx на /var/www/kaevcms. Публичным должен быть только /var/www/kaevcms/public.

Перед началом ​

Подготовьте:

  • чистый VDS с Ubuntu Server 24.04 LTS;
  • домен с A-записью на IPv4-адрес VDS — необязательно для локальной или тестовой установки;
  • SSH-доступ с sudo;
  • полный ZIP-релиз KaevCMS;
  • отдельные пароли для MySQL и владельца KaevCMS.

В примерах замените:

text
example.com              на свой домен, если он уже есть
192.168.50.111           на фактический IP сервера
admin@example.com        на свой email для Let's Encrypt
KaevCmsDbPasswordHere    на длинный уникальный пароль MySQL

Для первичной проверки KaevCMS достаточно IP-адреса. Домен и сертификат можно настроить позже.

1. Обновите систему ​

bash
sudo apt update
sudo apt full-upgrade -y
sudo reboot

После перезагрузки снова подключитесь по SSH.

2. Настройте firewall ​

Если SSH работает на стандартном порту 22:

bash
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status verbose

Если SSH перенесён на другой порт, сначала разрешите этот порт и только затем включайте UFW.

Не открывайте порт MySQL 3306 в интернет для локальной базы KaevCMS.

3. Установите nginx, MySQL, PHP и утилиты ​

bash
sudo apt install -y \
    nginx \
    mysql-server \
    unzip \
    curl \
    ca-certificates \
    composer \
    php8.3-cli \
    php8.3-fpm \
    php8.3-mysql \
    php8.3-mbstring \
    php8.3-xml \
    php8.3-curl \
    php8.3-zip \
    php8.3-intl \
    php8.3-gd

Проверьте версии и службы:

bash
php -v
composer --version
sudo systemctl status nginx --no-pager
sudo systemctl status php8.3-fpm --no-pager
sudo systemctl status mysql --no-pager

KaevCMS требует PHP 8.3 или новее. Для этой инструкции команда php -v должна показывать ветку 8.3.x.

4. Создайте базу KaevCMS ​

Откройте MySQL от системного администратора:

bash
sudo mysql

Выполните:

sql
CREATE DATABASE kaevcms_db
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;

CREATE USER 'kaevcms_user'@'localhost'
    IDENTIFIED BY 'KaevCmsDbPasswordHere';

GRANT ALL PRIVILEGES ON kaevcms_db.*
    TO 'kaevcms_user'@'localhost';

FLUSH PRIVILEGES;
EXIT;

Проверьте отдельного пользователя:

bash
mysql -u kaevcms_user -p -h 127.0.0.1 kaevcms_db

После успешного входа выполните EXIT;.

Для Web Installer используйте:

text
Host:      127.0.0.1
Port:      3306
Database:  kaevcms_db
Username:  kaevcms_user
Password:  созданный пароль

5. Загрузите и распакуйте KaevCMS ​

Передайте полный ZIP-релиз на сервер через SFTP/SCP, например в /tmp/KaevCMS-full.zip.

bash
sudo mkdir -p /var/www/kaevcms
sudo unzip /tmp/KaevCMS-full.zip -d /var/www/kaevcms
sudo chown -R "$USER":www-data /var/www/kaevcms
cd /var/www/kaevcms

Проверьте, что архив не создал лишний уровень каталога:

bash
test -f /var/www/kaevcms/artisan && echo "KaevCMS root OK"

Если artisan находится, например, в /var/www/kaevcms/KaevCMS-0.x.x/artisan, перенесите содержимое этой вложенной папки на один уровень выше.

Удалите загруженный архив после распаковки:

bash
sudo rm -f /tmp/KaevCMS-full.zip

6. Установите production-зависимости ​

Выполняйте Composer от обычного SSH-пользователя, которому принадлежит проект, а не от root:

bash
cd /var/www/kaevcms
composer install --no-dev --optimize-autoloader --no-interaction
composer check-platform-reqs --no-dev

Node.js и npm на VDS не требуются: готовые frontend-assets уже входят в полный релиз.

7. Назначьте права ​

Сначала задайте безопасные базовые права:

bash
sudo find /var/www/kaevcms -type d -exec chmod 755 {} \;
sudo find /var/www/kaevcms -type f -exec chmod 644 {} \;

Разрешите nginx/PHP-FPM запись только в runtime-каталоги:

bash
sudo chgrp -R www-data \
    /var/www/kaevcms/storage \
    /var/www/kaevcms/bootstrap/cache \
    /var/www/kaevcms/public/uploads

sudo chmod -R g+rwX \
    /var/www/kaevcms/storage \
    /var/www/kaevcms/bootstrap/cache \
    /var/www/kaevcms/public/uploads

sudo find /var/www/kaevcms/storage \
    /var/www/kaevcms/bootstrap/cache \
    /var/www/kaevcms/public/uploads \
    -type d -exec chmod 2775 {} \;

Проверьте, что пользователь PHP-FPM действительно может создавать вложенные каталоги кэша и файлы:

bash
cd /var/www/kaevcms
sudo -u www-data php artisan kaevcms:runtime-directories --probe

Общий кэш приложения по умолчанию остаётся файловым, а счётчики ограничений входа и других запросов хранятся в основной базе CMS:

env
CACHE_STORE=file
CACHE_LIMITER=database

Web Installer должен создать .env в корне проекта. Временно разрешите группе www-data запись в корень:

bash
sudo chmod 775 /var/www/kaevcms

Не назначайте 0777 всему проекту.

8. Настройте лимиты PHP ​

Откройте конфигурацию PHP-FPM:

bash
sudo nano /etc/php/8.3/fpm/php.ini

Рекомендуемые стартовые значения:

ini
memory_limit = 512M
upload_max_filesize = 64M
post_max_size = 70M
max_execution_time = 120
max_input_time = 120

Примените изменения:

bash
sudo systemctl restart php8.3-fpm

На VDS обновления применяются через CLI от владельца файлов проекта, поэтому загружать большой update ZIP через PHP не требуется.

9. Настройте nginx ​

Создайте сайт:

bash
sudo nano /etc/nginx/sites-available/kaevcms

Конфигурация:

nginx
server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    root /var/www/kaevcms/public;
    index index.php;

    charset utf-8;
    client_max_body_size 512m;

    access_log /var/log/nginx/kaevcms-access.log;
    error_log  /var/log/nginx/kaevcms-error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /index.php {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location = /install/index.php {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location ^~ /uploads/ {
        try_files $uri =404;
    }

    location ~ \.php$ {
        return 404;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

Активируйте конфигурацию:

bash
sudo ln -s /etc/nginx/sites-available/kaevcms /etc/nginx/sites-enabled/kaevcms
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

Проверьте Document Root:

bash
sudo nginx -T | grep -A4 -B4 '/var/www/kaevcms/public'

Если домена пока нет, укажите в конфигурации nginx фактический IP сервера:

nginx
server_name 192.168.50.111;

10. Настройте HTTPS при наличии домена ​

Если домен и его A-запись уже указывают на VDS, установите Certbot:

bash
sudo apt install -y certbot python3-certbot-nginx

Получите сертификат:

bash
sudo certbot --nginx \
    -d example.com \
    -d www.example.com \
    -m admin@example.com \
    --agree-tos \
    --redirect

Проверьте автоматическое продление:

bash
sudo systemctl status certbot.timer --no-pager
sudo certbot renew --dry-run

Если поддомен www не используется и для него нет DNS-записи, уберите второй -d из команды и www.example.com из server_name.

Если домена или A-записи пока нет, пропустите Certbot. Web Installer разрешает установку по HTTP и только покажет предупреждение. Это удобно для тестового VDS, локального IP или закрытой сети. Перед открытием публичного сайта пользователям настройте домен и HTTPS.

Защитные настройки nginx ​

Эти настройки применяются к самому VDS. Они нужны при новой установке и полезны для уже работающего сайта.

1. Скрыть версию nginx. Откройте конфигурацию:

bash
sudo nano /etc/nginx/nginx.conf

Внутри секции http { ... } найдите настройку server_tokens (если она уже есть — измените её, а не дублируйте) и укажите:

nginx
server_tokens off;

Это скрывает точную версию nginx в заголовке Server и стандартных страницах ошибок, но не заменяет обновления безопасности nginx.

2. Включить HSTS только после проверки HTTPS. Когда сертификат работает, HTTP перенаправляется на HTTPS и продление проверено командой sudo certbot renew --dry-run, откройте конфигурацию сайта:

bash
sudo nano /etc/nginx/sites-available/kaevcms

Добавьте директиву внутрь каждого HTTPS-блока server { ... } сайта (обычно с listen 443 ssl), включая отдельный HTTPS-редирект с www, если он есть:

nginx
add_header Strict-Transport-Security "max-age=604800" always;

604800 — срок HSTS 7 дней. Сначала проверьте работу HTTPS на всём сайте, а затем при необходимости увеличьте срок. Не добавляйте HSTS к HTTP-блоку, сайту только по IP, а также параметры includeSubDomains или preload без отдельной проверки всех поддоменов. Если внутри server есть location со своими add_header, заголовок HSTS может не унаследоваться: добавьте его и в такой location.

3. Проверить и применить:

bash
sudo nginx -t
sudo systemctl reload nginx
curl -I https://example.com/
curl -I https://example.com/login
curl -I https://example.com/nesushchestvuet

Ожидается заголовок Strict-Transport-Security: max-age=604800 на HTTPS-ответах, включая ошибку 404. На страницах входа и для авторизованных пользователей актуальная CMS также возвращает Cache-Control: no-store.

Не дублируйте CSP в nginx

Content-Security-Policy (CSP) и запрет кэширования приватных ответов реализованы в приложении KaevCMS. Указывать вторую CSP в nginx не нужно: браузер применяет обе политики одновременно, что может сломать сайт, личный кабинет или переход к ЮMoney.

Эти изменения приложения вошли в коммит 7fb7bade, сделанный после релиза 0.53.1. Чтобы они работали, установленная сборка должна содержать этот коммит или более позднее исправление.

11. Пройдите Web Installer ​

С доменом и сертификатом откройте:

text
https://example.com/install/

Без домена откройте IP-адрес сервера:

text
http://192.168.50.111/install/

Замените пример на фактический IP. На шаге базы укажите созданные ранее реквизиты и используйте пустую базу kaevcms. Предупреждение об HTTP не блокирует проверку базы или завершение установки.

После завершения установки убедитесь, что Web Installer автоматически удалил публичную папку /install, затем войдите в административную панель. Если итоговая страница сообщает об ошибке удаления, удалите каталог вручную.

12. Закройте временные права после установки ​

После появления .env выполните:

bash
sudo chmod 755 /var/www/kaevcms
sudo chown "$USER":www-data /var/www/kaevcms/.env
sudo chmod 640 /var/www/kaevcms/.env

sudo chown "$USER":www-data /var/www/kaevcms/storage/app/installed.lock
sudo chmod 640 /var/www/kaevcms/storage/app/installed.lock

Проверьте, что закрытые файлы не отдаются через nginx:

bash
curl -I https://example.com/.env
curl -I https://example.com/vendor/autoload.php

Ожидается 404 или 403, но не содержимое файла.

13. Настройте планировщик ​

Создайте системный cron-файл:

bash
sudo nano /etc/cron.d/kaevcms

Содержимое:

cron
* * * * * www-data cd /var/www/kaevcms && /usr/bin/php8.3 artisan schedule:run >> /dev/null 2>&1

Примените права и проверьте:

bash
sudo chmod 644 /etc/cron.d/kaevcms
sudo -u www-data /usr/bin/php8.3 /var/www/kaevcms/artisan schedule:run

Через несколько минут проверьте heartbeat планировщика в системной диагностике KaevCMS.

14. Queue worker для асинхронной почты ​

Этот раздел нужен только после включения асинхронной очереди в KaevCMS.

Создайте systemd-службу:

bash
sudo nano /etc/systemd/system/kaevcms-queue.service

Содержимое:

ini
[Unit]
Description=KaevCMS queue worker
After=network.target mysql.service php8.3-fpm.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/kaevcms
ExecStart=/usr/bin/php8.3 /var/www/kaevcms/artisan queue:work database --queue=mail-probe,mail,default --sleep=1 --tries=3 --timeout=120
Restart=always
RestartSec=5
TimeoutStopSec=360
KillSignal=SIGTERM

[Install]
WantedBy=multi-user.target

Запустите службу:

bash
sudo systemctl daemon-reload
sudo systemctl enable --now kaevcms-queue
sudo systemctl status kaevcms-queue --no-pager

Журнал:

bash
sudo journalctl -u kaevcms-queue -n 100 --no-pager

15. Финальная проверка ​

bash
cd /var/www/kaevcms

sudo -u www-data php artisan about --only=environment
sudo -u www-data php artisan kaevcms:release-version --no-ansi
sudo -u www-data php artisan kaevcms:maintenance-status --no-ansi
sudo -u www-data php artisan kaevcms:encryption-health --no-ansi

sudo nginx -t
sudo systemctl is-active nginx php8.3-fpm mysql
sudo systemctl is-enabled nginx php8.3-fpm mysql

Проверьте в браузере:

  • главную страницу;
  • /admin;
  • вход владельца;
  • загрузку изображения новости;
  • SMTP-тест;
  • подключение LoginServer/GameServer;
  • системную диагностику;

Дальнейшее обновление ​

Порядок установки Update Agent и обновлений через панель или SSH вынесен в отдельную инструкцию: Обновление KaevCMS.

Как изменить адрес сайта после установки ​

Адрес KaevCMS хранится в двух местах:

text
/var/www/kaevcms/.env
/etc/nginx/sites-available/kaevcms

Переход на другой IP или работа по HTTP ​

Откройте .env:

bash
sudo nano /var/www/kaevcms/.env

Укажите фактический адрес и отключите принудительный HTTPS:

env
APP_URL=http://192.168.50.112
APP_FORCE_HTTPS=false
SESSION_SECURE_COOKIE=false

APP_FORCE_HTTPS=true заставляет KaevCMS формировать HTTPS-ссылки. SESSION_SECURE_COOKIE=true запрещает браузеру отправлять сессионную cookie по HTTP и может приводить к ошибке 419 Page Expired при входе.

Затем измените server_name в nginx:

bash
sudo nano /etc/nginx/sites-available/kaevcms
nginx
server_name 192.168.50.112;

Примените изменения:

bash
cd /var/www/kaevcms
php artisan optimize:clear
sudo nginx -t
sudo systemctl reload nginx

После смены IP, домена или протокола удалите старые cookies сайта либо откройте его в приватном окне браузера.

Переход на домен и HTTPS ​

После настройки DNS и сертификата измените .env:

env
APP_URL=https://cms.example.com
APP_FORCE_HTTPS=true
SESSION_SECURE_COOKIE=true

И nginx:

nginx
server_name cms.example.com;

После этого снова выполните php artisan optimize:clear, проверку nginx и его перезагрузку.

Диагностика ​

502 Bad Gateway ​

bash
sudo systemctl status php8.3-fpm --no-pager
ls -la /run/php/
sudo tail -n 100 /var/log/nginx/kaevcms-error.log

Проверьте, что nginx использует существующий сокет:

nginx
fastcgi_pass unix:/run/php/php8.3-fpm.sock;

403 Forbidden ​

bash
namei -l /var/www/kaevcms/public/index.php
sudo -u www-data test -r /var/www/kaevcms/public/index.php && echo readable

Каждый родительский каталог пути должен иметь право прохода x для nginx.

Ошибка записи ​

bash
sudo -u www-data test -w /var/www/kaevcms/storage && echo storage-writable
sudo -u www-data test -w /var/www/kaevcms/bootstrap/cache && echo cache-writable
sudo -u www-data test -w /var/www/kaevcms/public/uploads && echo uploads-writable

Не исправляйте проблему командой chmod -R 777 /var/www/kaevcms.

Если Laravel пишет storage/framework/cache/data/...: No such file or directory, восстановите runtime-каталоги и проверьте запись от пользователя PHP-FPM:

bash
cd /var/www/kaevcms
sudo mkdir -p storage/framework/cache/data storage/framework/sessions storage/framework/views storage/logs bootstrap/cache
sudo chown -R "$USER":www-data storage bootstrap/cache
sudo chmod -R g+rwX storage bootstrap/cache
sudo find storage bootstrap/cache -type d -exec chmod 2775 {} \;
sudo -u www-data php artisan kaevcms:runtime-directories --probe
php artisan optimize:clear
sudo -u www-data php artisan kaevcms:runtime-directories --probe

optimize:clear не заменяет правильные права и владельца. Updater проверяет runtime-каталоги до очистки кэша и повторно создаёт и проверяет их после неё.

419 Page Expired после входа по HTTP ​

Проверьте /var/www/kaevcms/.env:

env
APP_URL=http://192.168.50.111
APP_FORCE_HTTPS=false
SESSION_SECURE_COOKIE=false
SESSION_DOMAIN=

Затем:

bash
cd /var/www/kaevcms
php artisan optimize:clear

Удалите cookies для старого адреса или откройте сайт в приватном окне. Адрес в APP_URL должен совпадать с адресом, который открыт в браузере.

Каталоги загрузок отмечены как недоступные для записи ​

KaevCMS использует:

text
public/uploads/news
public/uploads/pages
public/uploads/settings
public/uploads/account-avatars
public/uploads/game-assets

В актуальном релизе установщик и системная диагностика создают отсутствующие каталоги автоматически. Для ручного восстановления прав выполните:

bash
sudo mkdir -p \
    /var/www/kaevcms/public/uploads/news \
    /var/www/kaevcms/public/uploads/pages \
    /var/www/kaevcms/public/uploads/settings \
    /var/www/kaevcms/public/uploads/account-avatars \
    /var/www/kaevcms/public/uploads/game-assets

sudo chgrp -R www-data /var/www/kaevcms/public/uploads
sudo chmod -R g+rwX /var/www/kaevcms/public/uploads
sudo find /var/www/kaevcms/public/uploads -type d -exec chmod 2775 {} \;

Проверка:

bash
sudo -u www-data test -w /var/www/kaevcms/public/uploads/news && echo news-writable
sudo -u www-data test -w /var/www/kaevcms/public/uploads/pages && echo pages-writable
sudo -u www-data test -w /var/www/kaevcms/public/uploads/settings && echo settings-writable

Не используйте chmod -R 777.

Новость создана, но не отображается на сайте ​

Проверьте в админ-панели, что новость не является черновиком, переключатель публикации включён, а дата публикации не установлена в будущем. Изображение новости не влияет на сам факт публикации.

Логи ​

bash
sudo tail -n 100 /var/log/nginx/kaevcms-error.log
sudo tail -n 100 /var/www/kaevcms/storage/logs/laravel.log
sudo journalctl -u php8.3-fpm -n 100 --no-pager
sudo journalctl -u kaevcms-queue -n 100 --no-pager

Официальные справочные материалы ​

Независимый открытый проект. Lineage II — собственность правообладателей.