VPS начинает тормозить не только потому что стало больше трафика — чаще всего причина в том, что сервер изначально настроен на дефолтах для «среднего случая», а не под конкретную нагрузку. Прежде чем оптимизировать VPS или покупать тариф дороже, стоит пройтись по стеку снизу вверх: ядро, диск, сеть, Nginx, PHP-FPM, база данных, кэш. На каждом слое есть рычаги, которые дают прирост без апгрейда железа.
Как понять, что VPS работает неэффективно

Диагностику начинают с трёх показателей. Load Average (top, htop) — если стабильно выше числа ядер, система перегружена. Swap (free -h) — активное использование swap означает нехватку RAM или неправильные настройки. Базовые симптомы: nginx возвращает 502/504, MySQL выдаёт «too many connections», время ответа сайта резко скачет под нагрузкой.
Для детальной картины — vmstat 1 5 покажет динамику CPU/mem/io, iostat -x 1 покажет утилизацию диска, atop даст историю нагрузки если проблема не постоянная, а пиковая. Задача этого этапа — понять, на каком слое стека узкое место, прежде чем начинать крутить параметры. О том, как настроить постоянный мониторинг, читайте в статье настройка мониторинга сервера.
Слой 1. Ядро Linux — системные параметры (sysctl)
Параметры ядра в /etc/sysctl.conf управляют тем, как система обрабатывает сеть, память и файловые дескрипторы. Дефолтные значения консервативны — для сервера под реальной нагрузкой их нужно скорректировать.
Базовый набор параметров для веб-сервера:
# /etc/sysctl.conf
vm.swappiness = 10
net.core.somaxconn = 65535
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
fs.file-max = 100000
Что делает каждый параметр. vm.swappiness = 10 — ядро будет использовать swap только при реальной нехватке RAM, а не при 40% заполненности (дефолт 60). net.core.somaxconn = 65535 — максимальный размер очереди входящих соединений; дефолт 128 — это очень мало для нагруженного сервера. net.ipv4.tcp_fin_timeout = 15 — время ожидания закрытия TCP-соединения, сокращение уменьшает количество соединений в состоянии TIME_WAIT. tcp_tw_reuse = 1 — разрешает повторное использование сокетов в TIME_WAIT. fs.file-max = 100000 — системный лимит открытых файлов.
Применить без перезагрузки: sysctl -p. Подробнее о параметрах — в документации ядра Linux.
Лимиты файловых дескрипторов (ulimit / limits.conf)
Системный лимит fs.file-max задаёт максимум для всей ОС, но каждый процесс ограничен отдельно через /etc/security/limits.conf. Для nginx и mysql нужно явно установить лимит:
# /etc/security/limits.conf
nginx soft nofile 65536
nginx hard nofile 65536
mysql soft nofile 65536
mysql hard nofile 65536
При большом числе соединений (тысячи одновременных пользователей) каждое соединение — это как минимум один файловый дескриптор. Если лимит исчерпан, nginx начнёт падать с ошибкой too many open files, и это не будет связано ни с CPU, ни с RAM.
Слой 2. Файловая система — диск и I/O
Планировщик I/O влияет на порядок обработки запросов к диску. Для SSD-дисков оптимален mq-deadline, для HDD — bfq. Проверить текущий: cat /sys/block/sda/queue/scheduler. Изменить для SSD: echo mq-deadline > /sys/block/sda/queue/scheduler. Чтобы изменение сохранялось после перезагрузки — нужно добавить правило udev.
Опция noatime в /etc/fstab запрещает обновление времени доступа при каждом чтении файла. На нагруженном сервере это снижает количество записей на диск. Выглядит в fstab так:
UUID=xxx / ext4 defaults,noatime 0 1
Файловая система: ext4 — стандарт и подходит для большинства случаев. XFS быстрее при параллельной записи и работе с большими файлами (медиа, логи, объектное хранилище). Для tmpfs под сессии PHP: монтировать /tmp или отдельную директорию сессий в оперативную память, если размер сессий небольшой и диск является узким местом.
Мониторинг I/O — как найти узкое место
iostat -x 1 показывает метрики диска в реальном времени. Ключевые поля: %util — утилизация диска; выше 80% означает, что диск работает на пределе. await — среднее время ожидания операции; для SSD больше 10ms — это уже признак проблемы. iotop покажет, какой именно процесс генерирует нагрузку на диск.
Слой 3. Сеть — TCP и очереди
В дополнение к параметрам из первого слоя, для высоконагруженных серверов стоит увеличить очереди входящих пакетов: net.core.netdev_max_backlog = 5000 и net.ipv4.tcp_max_syn_backlog = 8192. Это снижает потери соединений при пиках трафика. Подробнее о защите от перегрузки сети — в статье защита VPS от DDoS.
BBR — алгоритм управления перегрузкой от Google, доступен в ядрах 4.9+. Даёт прирост пропускной способности и снижает задержки, особенно при нестабильных соединениях. Включить:
# Проверить версию ядра
uname -r
# Включить BBR
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
sysctl -p
# Проверить что применилось
sysctl net.ipv4.tcp_congestion_control
Слой 4. Nginx — тюнинг веб-сервера
Nginx из коробки работает нормально, но дефолтные значения рассчитаны на умеренную нагрузку. Базовый тюнинг выглядит так:
worker_processes auto;
events {
worker_connections 2048;
use epoll;
multi_accept on;
}
http {
keepalive_timeout 15;
sendfile on;
tcp_nopush on;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
}
worker_processes auto — Nginx сам определит количество воркеров по числу ядер CPU. worker_connections 2048 — максимум соединений на один воркер. use epoll — эффективный механизм мультиплексирования I/O на Linux. multi_accept on — воркер принимает все новые соединения за один проход. open_file_cache кэширует дескрипторы открытых файлов и уменьшает количество системных вызовов. Подробнее о конфигурации — в официальной документации Nginx.
О сравнении Nginx и Apache с точки зрения нагрузки — статья Nginx или Apache — что выбрать.
Nginx + PHP-FPM — правильный fastcgi_pass
Nginx может передавать запросы в PHP-FPM через TCP-порт (127.0.0.1:9000) или Unix-сокет. На одном сервере Unix-сокет быстрее: нет сетевого стека, меньше overhead. Пример:
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 60;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
}
Слой 5. PHP-FPM — менеджер процессов
PHP-FPM управляет пулом рабочих процессов. Выбор режима pm — ключевое решение. dynamic — количество процессов меняется в диапазоне от pm.min_spare_servers до pm.max_children; подходит для большинства случаев. ondemand — процессы создаются по запросу и умирают после простоя; экономит память на небольших VPS. static — фиксированное количество процессов; максимальная производительность при стабильной нагрузке, но держит память даже в простое.
Формула для pm.max_children: разделить доступную RAM (за вычетом MySQL, Redis, системы) на средний размер одного PHP-процесса. Средний размер можно посмотреть через ps aux | grep php-fpm. Обычно 30–80 MB на процесс.
; /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500
; /etc/php/8.2/fpm/php.ini (или conf.d/opcache.ini)
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.validate_timestamps=1
pm.max_requests = 500 — перезапуск процесса после 500 запросов предотвращает накопление утечек памяти в PHP-коде. OPcache кэширует скомпилированный байткод PHP — без него каждый запрос компилирует файлы заново. opcache.memory_consumption=128 для среднего проекта, 256 MB для крупного WordPress-мультисайта или большого фреймворка.
Слой 6. MySQL / MariaDB — база данных
InnoDB buffer pool — самый важный параметр MySQL. Это кэш в памяти для данных и индексов InnoDB. Правило: устанавливать в 60–70% от всей RAM сервера, если MySQL — единственная служба. На shared-сервере пересчитать исходя из доступной доли. Для сервера с 2 GB RAM и только MySQL — около 1,2–1,4 GB.
# /etc/mysql/conf.d/tuning.cnf
[mysqld]
innodb_buffer_pool_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_log_file_size = 128M
max_connections = 150
slow_query_log = 1
long_query_time = 1
slow_query_log_file = /var/log/mysql/slow.log
query_cache_size = 0
query_cache_type = 0
innodb_flush_log_at_trx_commit = 2 — данные пишутся в лог раз в секунду, а не при каждой транзакции. Даёт значительный прирост производительности при некритичных данных. Для финансовых транзакций оставлять значение 1. query_cache_size = 0 — query cache в MySQL 5.7+ и 8.0 официально устарел и удалён: при включении он создаёт конкуренцию за мьютексы и замедляет работу.
О переносе базы без потерь — статья как перенести базу данных MySQL.
Как найти медленные запросы
После включения slow_query_log лог пишется в указанный файл. Анализировать вручную неудобно — использовать mysqldumpslow -t 10 /var/log/mysql/slow.log для топ-10 медленных запросов. Для глубокого анализа — pt-query-digest из Percona Toolkit.
Важный принцип: тюнинг my.cnf без оптимизации самих запросов даёт ограниченный эффект. Если запрос делает full table scan на таблице в 10 миллионов строк — никакой buffer pool это не исправит. Сначала индексы, потом параметры.
Слой 7. Кэш — Redis и Memcached

Redis — кэш с функциями: поддерживает структуры данных, опциональную персистентность (RDB/AOF), pub/sub, очереди. В веб-стеке используется для: кэширования страниц и объектов (WordPress Object Cache, Django Cache Framework), хранения сессий (быстрее, чем файлы на диске), очередей задач (Celery, Laravel Queue).
Базовые параметры в /etc/redis/redis.conf:
maxmemory 256mb
maxmemory-policy allkeys-lru
allkeys-lru — при достижении лимита Redis вытесняет наименее используемые ключи. Это поведение для кэша; для хранилища данных выбирать другую политику.
Memcached проще: только key-value кэш, без персистентности, без структур данных. Потребляет меньше памяти на одинаковых объёмах данных. Выбор: Redis если нужны структуры данных, persistence или очереди. Memcached — если только простое кэширование и важна максимальная простота конфига.
Swap — последний рубеж
Swap не заменяет оперативную память и не должен использоваться постоянно — это буфер для пиков, который предотвращает гибель процессов через OOM killer. Правило размера: для VPS с 1–4 GB RAM — swap равен RAM. Для 8+ GB — достаточно 4 GB.
# Создать swap-файл 2 GB
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# Сделать постоянным
echo '/swapfile none swap sw 0 0' >> /etc/fstab
# Проверить
swapon --show
На серверах с очень ограниченной RAM (<1 GB) — рассмотреть zswap: это сжатый кэш swap-страниц прямо в RAM, значительно быстрее дискового swap при правильной нагрузке.
Мониторинг после оптимизации
Тюнинг без измерений — это гадание. После внесения изменений нужно убедиться, что они дали эффект, а не создали новое узкое место. Метрики для контроля: LA, свободная RAM, использование swap, количество активных соединений Nginx (stub_status), Threads_running в MySQL (SHOW STATUS LIKE 'Threads_running'), listen.queue в PHP-FPM (pm.status_path).
Инструменты: Netdata устанавливается одной командой и сразу показывает все метрики с историей. Prometheus + Grafana — если мониторинг уже есть в инфраструктуре. UptimeRobot — для внешней проверки доступности, чтобы видеть проблемы глазами пользователя.
Когда оптимизация не помогает — пора апгрейдить
Точечный тюнинг даёт 30–50% прироста производительности. Это существенно, но не бесконечно. Есть пороги, за которыми только смена тарифа или переход на выделенный сервер:
- LA стабильно выше значения
число ядер × 2даже в ночное время - Swap заполнен на 80%+ постоянно (не при пиках)
- InnoDB buffer pool hit rate ниже 95% (проверить:
SHOW STATUS LIKE 'Innodb_buffer_pool%') - Диск утилизирован на 90%+ по
iostat - Добавление RAM или CPU физически невозможно на текущем тарифе
Перед апгрейдом — сделать бэкап VPS: при переезде на новый тариф данные нужно переносить. О том, как выбрать следующий тариф — статья как выбрать VPS.
Если ресурсов VPS уже не хватает — сравните тарифы провайдеров на free-hosting.ru. Фильтр по RAM, CPU и типу дисков поможет найти подходящий вариант без переплаты. Выбрать VPS-провайдера →
Ну и в заключение — шпаргалка по слоям
| Слой | Ключевой параметр | Эффект |
|---|---|---|
| Ядро (sysctl) | vm.swappiness, somaxconn, tcp_fin_timeout | Меньше swap, больше соединений, быстрее освобождение TCP |
| Диск | I/O scheduler, noatime | Меньше latency на SSD, меньше лишних записей |
| Сеть | BBR, netdev_max_backlog | Лучший throughput, меньше дропов при пиках |
| Nginx | worker_connections, open_file_cache, gzip | Больше одновременных соединений, меньше syscall |
| PHP-FPM | pm.max_children, OPcache | Меньше процессов-зомби, быстрый байткод-кэш |
| MySQL | innodb_buffer_pool_size, slow_query_log | Данные из RAM, видимость медленных запросов |
| Redis | maxmemory, allkeys-lru | Меньше запросов к PHP и MySQL, быстрые сессии |
| Swap | Размер, swappiness | Защита от OOM killer при пиках |