MAATRIX / Блог / Сколько доменов повесить на один сервер: где кончается nginx, а где ваше терпение

Сколько доменов повесить на один сервер: где кончается nginx, а где ваше терпение

MAATRIX

Рано или поздно у любого, кто держит сайты на своём сервере, встаёт вопрос: можно ли добавить ещё один домен, или пора заводить второй сервер. Интуиция подсказывает, что где-то есть потолок — nginx же не резиновый. И интуиция отчасти права, только предел почти никогда не там, где его ищут: не в самом nginx, а в памяти под процессы, в механике TLS и в том, что происходит с конфигурацией и логами, когда доменов становится не десять, а пятьсот. Разберём, что реально ограничивает число виртуальных хостов на одном сервере — и где технический лимит заканчивается, а начинается вопрос управляемости.

Сколько виртуальных хостов nginx выдерживает технически

nginx не имеет встроенного жёсткого лимита на число server-блоков. Это конфигурационный файл (или набор файлов), который парсится при старте и перезагрузке — технически ничто не мешает описать тысячу server { ... } блоков в одном nginx.conf или в паре тысяч файлов внутри sites-enabled/.

Практическая структура обычно выглядит так:

/etc/nginx/
├── nginx.conf
├── sites-available/
│   ├── site1.ru.conf
│   ├── site2.ru.conf
│   └── ...
└── sites-enabled/ -> симлинки на sites-available

Каждый файл подключается через include:

http {
    include /etc/nginx/sites-enabled/*.conf;
}

При старте nginx читает все файлы, строит внутреннюю карту server_name → конфигурация и держит её в памяти управляющего процесса. Сама эта карта весит немного даже при тысячах записей — набор строк и указателей, а не полноценная база. Проблема с масштабом начинается не на этапе парсинга, а дальше — там, где виртуальные хосты реально начинают потреблять ресурсы под трафиком.

Стоит сразу развести два разных вопроса, которые часто путают: «сколько доменов может обслуживать один процесс nginx» и «сколько одновременных соединений выдержит сервер». Это независимые величины — число виртуальных хостов почти не влияет на пропускную способность по соединениям, если сами хосты не нагружены трафиком. Про соединения отдельно и подробно — в материале о том, сколько соединений реально держит nginx до отказа.

Память: воркер-процессы и с чем реально растёт потребление

nginx работает по модели master + worker: master читает конфигурацию и управляет воркерами, воркеры обрабатывают запросы. Число воркеров задаётся директивой:

worker_processes auto;

auto обычно означает по одному воркеру на ядро CPU. Важный нюанс: число виртуальных хостов само по себе почти не влияет на базовое потребление памяти воркером — конфигурация с описанием server-блоков хранится один раз в разделяемой памяти, а не копируется под каждый воркер отдельно. Разница в потреблении между сервером с 10 доменами и сервером с 500 доменами при прочих равных заметно меньше, чем можно ожидать — до тех пор, пока не вступают в игру три вещи.

Первая — SSL-сессии и связанные с ними структуры (о них — в следующем разделе). Вторая — буферы на каждое активное соединение: client_body_buffer_size, proxy_buffer_size и подобные директивы резервируют память не на домен, а на каждое одновременное соединение, так что суммарный трафик по всем доменам определяет память, а не число доменов как таковое. Третья — open file cache и кеш DNS-резолвинга для upstream, если у каждого домена свой бэкенд: чем больше разных upstream-адресов, тем больше записей держит резолвер.

Итог: рост числа доменов сам по себе — дешёвая операция для памяти. Дорогим он становится, когда вместе с доменами растёт суммарный трафик, число активных TLS-сессий и число разных бэкендов — тогда стоит отдельно разбирать, почему конкретно раздулось потребление памяти именно у ваших воркеров.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

SSL-сертификаты: TLS handshake, SNI и настоящий CPU-налог

Вот где количество доменов действительно начинает стоить ресурсов напрямую — а не через объём трафика, а через сам факт «у меня N доменов с HTTPS».

Каждое новое TLS-соединение — это handshake: обмен ключами, проверка сертификата, установка шифрованного канала. Это не бесплатно по CPU, особенно без аппаратного ускорения криптографии. Если у вас один домен с постоянным потоком клиентов — большая часть handshake'ов возобновляемые (TLS session resumption), и накладные расходы стабилизируются. Если у вас сотни низкотрафиковых доменов, где новый посетитель — редкость, доля «полных», самых дорогих по CPU handshake'ов на общем фоне выше: сессии реже переиспользуются, потому что кеш сессии успевает протухнуть между визитами.

Второй момент — SNI (Server Name Indication). Именно SNI позволяет nginx на одном IP и порту 443 подбирать нужный сертификат для нужного домена ещё до расшифровки остального handshake'а — клиент присылает имя домена открытым текстом на этапе ClientHello, и nginx выбирает соответствующий ssl_certificate. Без SNI пришлось бы держать отдельный IP на каждый HTTPS-домен — с ним один IP обслуживает сколько угодно доменов с разными сертификатами. Практически все современные клиенты SNI поддерживают, так что для обычного веб-трафика это не ограничение. Но само сопоставление домен → сертификат при большом числе server-блоков — дополнительный указатель, который nginx резолвит на каждый handshake, и при тысячах виртуальных хостов с отдельными сертификатами это даёт заметный, хоть и не катастрофический, оверхед.

Организационная часть здесь тяжелее технической. Каждый сертификат Let's Encrypt (или любого другого CA с коротким сроком жизни) нужно продлевать — обычно раз в 60-90 дней. При десяти доменах это фоновая задача cron. При нескольких сотнях начинают всплывать частные случаи: у Let's Encrypt есть лимиты на число запросов в единицу времени на домен и на аккаунт — при агрессивной пересборке большого числа сертификатов можно в них упереться, это разобрано в материале Let's Encrypt: превышен лимит запросов — причины и решение. Ещё одна классическая грабля — сертификат обновился на диске, но nginx не перечитал его, потому что процесс не получил сигнал reload; разбор этого сценария — в статье сертификат обновлялся, а nginx не перечитывал его. При одном домене такую ошибку вы заметите сразу — браузер покажет предупреждение. При трёхстах доменах она может провисеть неделями незамеченной без отдельного мониторинга сроков действия.

Конфигурация становится неуправляемой раньше, чем железо

Формально nginx одинаково легко читает и 10, и 1000 файлов конфигурации. Но человек, который эти файлы поддерживает, — не читает их одинаково легко. Это организационный предел, который наступает раньше технического. Практические симптомы, что конфигурация вышла из-под контроля:

  • Найти, какой домен привязан к какому бэкенду, можно только через grep, а не по внятной структуре каталогов.
  • Копипаста конфига под новый домен несёт с собой забытые правки — не поменяли upstream, оставили чужой server_name в комментарии.
  • nginx -t перед каждым reload — уже не формальность, а единственное, что спасает от опечатки в одном из трёхсот файлов, роняющей весь сервер разом.
  • Один и тот же логический сервис описан по-разному в разных файлах, потому что их писали в разное время.

Практика, снимающая часть боли заранее, — сниппеты через include для повторяющихся блоков:

# /etc/nginx/snippets/ssl-common.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

Подключается в каждый server-блок строкой include snippets/ssl-common.conf; — правите один сниппет, а не двести файлов по отдельности. ssl_session_cache напрямую касается предыдущего раздела: общий кеш TLS-сессий между воркерами снижает долю дорогих полных handshake'ов при большом числе доменов, разумный размер стоит закладывать заранее.

Второй инструмент дисциплины — обязательная проверка перед каждым релоадом:

nginx -t && systemctl reload nginx

Именно &&, а не последовательные команды — чтобы битый конфиг физически не мог долететь до reload. Частые причины, по которым nginx вообще не стартует после правки конфига, и как их быстро находить, разобраны в статье nginx не стартует после правки конфига: причины и решение.

Логи: файлы растут пропорционально числу доменов и трафика на каждом

Если у каждого домена свой access- и error-лог (разумная практика при десятках и сотнях сайтов — иначе не разберёшь, какой запрос к какому домену относится), число файлов растёт линейно вместе с числом доменов:

server {
    server_name example.ru;
    access_log /var/log/nginx/example.ru.access.log;
    error_log  /var/log/nginx/example.ru.error.log;
}

При 5 доменах это 10 файлов, о которых не нужно думать специально. При 300 доменах — 600 файлов, и без явной ротации диск заполнится молча. Базовая ротация через logrotate закрывает большую часть проблемы:

# /etc/logrotate.d/nginx-sites
/var/log/nginx/*.access.log /var/log/nginx/*.error.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

Обратите внимание на postrotate с сигналом USR1 — без него nginx продолжит писать в уже переименованный файл по старому файловому дескриптору, и на диске накопится куча пустых «новых» логов, пока старый молча растёт дальше. Это одна из самых частых причин, по которым ротация вроде бы настроена, а место всё равно кончается.

При действительно больших числах доменов (сотни и больше) имеет смысл на определённом этапе отказаться от отдельных файлов на каждый сайт в пользу единого структурированного лога с полем домена внутри записи и отправкой в централизованную систему сбора — Graylog, Grafana Loki или аналог. Это отдельная архитектурная развилка, но на масштабе в несколько сотен виртуальных хостов вопрос «куда девать логи» становится таким же практическим, как вопрос памяти под TLS-сессии.

Организационный предел: мониторинг, продление сертификатов, изоляция инцидентов

Есть предел другого рода, который часто наступает раньше железного: способность команды (или одного человека) реально следить за состоянием каждого из N доменов.

  • Истечение сертификатов остаётся незамеченным. При 5 доменах вы физически видите проблему глазами раз в пару месяцев. При 200 доменах без отдельного мониторинга один забытый сертификат вылезет только тогда, когда об этом напишет клиент — а то и уйдёт молча. Решение — не «помнить руками», а отдельный сервис, явно проверяющий срок жизни по каждому домену и алертящий заранее; подход разобран в статье мониторинг сертификатов и доменов.
  • Инцидент на одном домене может задеть остальные. Если все сайты делят один пул воркеров и одну память, аномальный трафик на один низкоприоритетный домен (флуд, скан ботом, внезапный вирусный трафик на давно забытый сайт) может выесть ресурсы у соседей. Технически nginx это не мешает работать — изоляция здесь вопрос архитектуры: лимиты через limit_req/limit_conn на уровне отдельных server-блоков, а при более высоких требованиях — разнесение критичных доменов на отдельные серверы или контейнеры.
  • Конфигурация теряет владельца. Через полгода-год без документации никто не помнит, зачем нужен домен test-legacy-2024.example.ru и можно ли его удалить. Минимальная гигиена — комментарий в начале файла конфигурации с датой создания, назначением и ответственным.

Практический ориентир, а не жёсткое число: пока доменов на сервере до нескольких десятков и все они у вас (или у небольшой команды) в голове — держать их вместе на одном сервере нормально и экономически оправданно. Когда счёт идёт на сотни, или среди доменов есть хотя бы один с высокими требованиями к доступности и изоляции (платёжная страница, клиентский продакшен), стоит явно решить держать его на отдельном сервере — не потому, что nginx не справится технически, а потому, что цена совместного инцидента становится выше экономии на железе.

Практический план: как понять, что вашему серверу пора делиться

Вместо абстрактного «а сколько доменов нормально», честнее пройти по конкретным проверкам на своём сервере:

  1. Память воркеров под нагрузкой:
ps aux | grep 'nginx: worker' | awk '{sum+=$6} END {print sum/1024, "MB"}'

Сравните с общим объёмом RAM и потреблением остальных сервисов — nginx редко бывает узким местом по памяти сам по себе.

  1. Время проверки конфигурации:
time nginx -t

Если оно заметно растёт со временем — конфигурация разрослась, пора приводить к общим сниппетам.

  1. Доля новых (не resumed) TLS-хендшейков, если логируется $ssl_session_reused. Большая доля новых сессий на низкотрафиковых доменах — сигнал пересмотреть размер ssl_session_cache и ssl_session_timeout.
  1. Объём логов и работоспособность ротации:
du -sh /var/log/nginx/

Убедитесь, что старые файлы реально не растут после ротации (см. проверку сигнала USR1 выше).

  1. Явный ответ на вопрос про мониторинг сертификатов: есть ли процесс, который заранее скажет, что конкретный домен теряет сертификат через 10 дней — или вы узнаете об этом от клиента.

Если по итогам CPU и память в порядке, конфигурация читаема, логи не копятся бесконтрольно и мониторинг сертификатов настроен явно — можно спокойно добавлять новые домены хоть до нескольких сотен на один сервер. Если упираетесь именно в память под воркеры и TLS-сессии при разумном трафике — это уже вопрос запаса ресурсов на самом сервере, а не самого nginx.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

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

Жёсткого числа нет — nginx технически не ограничивает число server-блоков. Практический ориентир: несколько десятков низкотрафиковых доменов почти всегда без проблем укладываются даже на скромный VPS, счёт на сотни требует уже осознанного контроля памяти, конфигурации и сертификатов.

Стоит ли использовать один сертификат на несколько доменов вместо отдельных?

Можно выпустить сертификат SAN на список доменов — это снижает число объектов для продления, но увеличивает радиус поражения при истечении: если он не продлился, падают все домены в нём разом. Выбор зависит от того, что важнее — простота продления или изоляция сбоя.

Нужно ли каждому домену свой upstream и отдельный процесс приложения?

Не обязательно — статические и лёгкие сайты могут делить один бэкенд-пул. Но чем важнее домен для бизнеса, тем больше смысла в отдельном upstream и, возможно, контейнере — чтобы падение одного приложения не утащило за собой соседей.

Как понять, что пора переезжать на отдельный сервер для конкретного домена?

Когда трафик или требования к доступности этого домена начинают заметно влиять на остальные — или когда цена инцидента на нём (платёжка, продакшен клиента) явно выше стоимости отдельного сервера под него.

SNI работает во всех браузерах, или для старых клиентов нужен отдельный IP?

Подавляющее большинство современных клиентов SNI поддерживают уже много лет. Отдельный IP на домен сегодня нужен по другим причинам — например, полная сетевая изоляция, а не проблемы совместимости с SNI.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →