Несколько сайтов на одном VPS: виртуальные хосты Nginx

Сайты и веб-серверы
Несколько сайтов на одном VPS: виртуальные хосты Nginx

Разместить несколько сайтов на одном VPS — самый рациональный способ хостить свои проекты: вместо трёх-пяти отдельных тарифов вы платите за один сервер, а Nginx раскладывает запросы по доменам с помощью виртуальных хостов (server-блоков). Блог, лендинг, интернет-магазин и пара клиентских сайтов спокойно уживаются на одной машине, если правильно организовать каталоги, изолировать сайты друг от друга через отдельные PHP-FPM пулы и выдать каждому домену свой SSL-сертификат. В этом руководстве настроим всё это пошагово на Ubuntu 24.04 LTS: рабочие конфиги Nginx, catch-all для запросов по IP и правила, которые уберегут от типичных ошибок.

Сколько сайтов потянет один VPS

Nginx выбирает сайт по заголовку Host из запроса браузера, поэтому количество доменов на один IP технически не ограничено. Реальный лимит — ресурсы: CPU, RAM и диск делятся между всеми сайтами. Грубые ориентиры:

  • Статические сайты и лендинги — почти ничего не потребляют, десятки штук даже на слабом VPS;
  • WordPress / PHP-сайты — считайте по 256–512 МБ RAM на активный сайт (PHP-FPM пул + доля MySQL);
  • Магазины и порталы — от 1–2 ГБ RAM на проект, лучше считать индивидуально.

Например, на тарифе Pro у Cheap-Host (12 vCPU, 16 ГБ RAM, 128 ГБ NVMe за 750 ₽/мес) комфортно живут 10–20 сайтов на WordPress — на NVMe-диске параллельная работа нескольких MySQL-баз не превращается в очередь к диску. Приятный бонус: если проектов станет больше, апгрейд CPU/RAM у Cheap-Host делается без переустановки и потери данных.

Шаг 1. DNS и структура каталогов

Для каждого домена создайте A-запись, указывающую на IP вашего сервера (как это делается — в статье о привязке домена к VPS). Пока DNS обновляется, подготовим файловую структуру. Правило простое: один сайт — один каталог и один системный пользователь.

sudo adduser --disabled-password --gecos "" site1
sudo mkdir -p /var/www/site1.ru/public_html
sudo chown -R site1:site1 /var/www/site1.ru
sudo chmod 755 /var/www/site1.ru

Повторите для каждого сайта (site2, site3...). Такая схема даёт две вещи: порядок (всегда понятно, где что лежит) и изоляцию (файлы сайтов принадлежат разным пользователям). Для проверки положим в каталог тестовую страницу:

echo "<h1>site1.ru работает</h1>" | sudo -u site1 tee /var/www/site1.ru/public_html/index.html

Шаг 2. Создаём виртуальные хосты Nginx

Если Nginx ещё не установлен, начните со статьи об установке Nginx на Ubuntu. В Ubuntu конфиги сайтов принято держать в /etc/nginx/sites-available/ и включать симлинками в sites-enabled/. Создайте файл /etc/nginx/sites-available/site1.ru:

server {
    listen 80;
    listen [::]:80;
    server_name site1.ru www.site1.ru;

    root /var/www/site1.ru/public_html;
    index index.php index.html;

    access_log /var/log/nginx/site1.ru.access.log;
    error_log  /var/log/nginx/site1.ru.error.log;

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

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/site1.sock;
    }

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

Ключевая директива — server_name: именно по ней Nginx понимает, какому сайту адресован запрос. Для второго сайта скопируйте файл и замените site1 на site2 во всех строках — конфиги полностью независимы. Включите сайты и примените изменения:

sudo ln -s /etc/nginx/sites-available/site1.ru /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/site2.ru /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Отдельные access_log и error_log на каждый сайт — не формальность: когда что-то сломается, вы будете смотреть логи конкретного проекта, а не искать его строки в общей куче.

Шаг 3. Catch-all: что отвечать по IP и чужим доменам

Если запрос не совпал ни с одним server_name, Nginx отдаст первый server-блок по алфавиту — и по голому IP внезапно откроется один из ваших сайтов. Это некрасиво и вредно для SEO (поисковик может проиндексировать сайт по IP как дубль). Закроем дыру блоком-заглушкой /etc/nginx/sites-available/default:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}

Код 444 — специальный для Nginx: соединение просто обрывается без ответа. Боты, сканирующие диапазоны IP, не увидят ничего.

Шаг 4. Отдельный PHP-FPM пул для каждого сайта

По умолчанию все PHP-сайты работают через один пул www под пользователем www-data. Для нескольких сайтов это плохо: взломанный через дырявый плагин сайт сможет читать и менять файлы соседей, а один «прожорливый» проект — съесть все PHP-воркеры. Создайте пул на каждый сайт: файл /etc/php/8.3/fpm/pool.d/site1.conf:

[site1]
user = site1
group = site1
listen = /run/php/site1.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 8
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
php_admin_value[open_basedir] = /var/www/site1.ru:/tmp

Пул работает под пользователем сайта, слушает собственный сокет (тот самый /run/php/site1.sock из конфига Nginx), а open_basedir запрещает PHP-скриптам выходить за пределы каталога сайта. Значения pm.max_children подбирайте под вашу RAM — методика расчёта есть в статье об оптимизации PHP-FPM. После создания пулов перезапустите службу:

sudo systemctl restart php8.3-fpm

Шаг 5. SSL-сертификат для каждого домена

Каждому сайту — свой бесплатный сертификат Let's Encrypt. Благодаря механизму SNI все они спокойно работают на одном IP-адресе:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d site1.ru -d www.site1.ru
sudo certbot --nginx -d site2.ru -d www.site2.ru

Certbot сам допишет в server-блоки директивы listen 443 ssl, пути к сертификатам и редирект с HTTP на HTTPS, а также настроит автопродление. Подробности и разбор нюансов — в отдельной статье про Let's Encrypt и certbot.

Обслуживание: как жить с несколькими сайтами дальше

Несколько правил, которые экономят часы при эксплуатации:

  • Один сайт — одна база и один пользователь MySQL. Не давайте сайтам общего пользователя БД: CREATE USER 'site1'@'localhost' ...; GRANT ALL ON site1_db.* TO 'site1'@'localhost';
  • Бэкапы по сайтам. Архивируйте каталог и дамп базы каждого проекта отдельно — восстанавливать один сайт проще, чем весь сервер.
  • Следите за ресурсами. htop покажет, чей PHP-FPM пул ест CPU: процессы подписаны именем пула.
  • Тестируйте конфиг перед перезагрузкой. Всегда nginx -t перед reload: ошибка в конфиге одного сайта уронит все.

Типичная структура: шпаргалка

ЧтоГде лежит
Файлы сайта/var/www/site1.ru/public_html/
Конфиг Nginx/etc/nginx/sites-available/site1.ru
Включённые сайты/etc/nginx/sites-enabled/ (симлинки)
PHP-FPM пул/etc/php/8.3/fpm/pool.d/site1.conf
Логи сайта/var/log/nginx/site1.ru.*.log
Сертификаты/etc/letsencrypt/live/site1.ru/

FAQ

Сколько сайтов можно разместить на одном VPS?

Ограничение — не количество доменов, а нагрузка. На VPS с 12 vCPU и 16 ГБ RAM (тариф Pro у Cheap-Host, 750 ₽/мес) спокойно живут 10–20 сайтов на WordPress или 30–50 статических. Сам Nginx обслуживает сотни server-блоков без заметных накладных расходов.

Нужен ли отдельный IP для каждого сайта?

Нет. Nginx различает сайты по заголовку Host, а SSL для разных доменов на одном IP работает через SNI, который поддерживают все современные браузеры. Одного выделенного IPv4 хватает на любое число доменов.

Зачем каждому сайту отдельный PHP-FPM пул?

Безопасность (взломанный сайт не дотянется до файлов соседей), стабильность (лимиты воркеров не дают одному сайту съесть всю память) и удобство (свои логи и настройки PHP у каждого проекта).

Почему по IP-адресу открывается один из моих сайтов?

Без default_server Nginx отдаёт на несовпавшие запросы первый попавшийся server-блок. Добавьте catch-all блок с return 444 — и запросы по IP будут обрываться.

Как понять, что VPS перестал справляться?

Load average стабильно выше числа ядер, RAM занята на 80–90%, время ответа растёт. У Cheap-Host в этой ситуации можно апгрейдить CPU, RAM и диск без переустановки и потери данных.

Вывод

Виртуальные хосты Nginx превращают один VPS в полноценную хостинг-площадку: отдельные каталоги, свои PHP-FPM пулы, независимые логи и сертификаты — каждый сайт живёт своей жизнью, а платите вы за один сервер. Настройка первого сайта занимает полчаса, каждый следующий добавляется за пять минут копированием конфигов. Если для этой задачи нужен сервер — у Cheap-Host тариф Pro за 750 ₽/мес: 12 vCPU, 16 ГБ RAM, 128 ГБ NVMe, KVM, безлимитный трафик и деплой за 45–60 секунд. Все тарифы — на cheap-host.onl.

Читайте также