Чтобы Linux VPS с 512 МБ или 1 ГБ ОЗУ работал без сбоев, сначала измерьте, что на самом деле занимает память, затем добавьте сжатый swap в ОЗУ (zram) поверх небольшого файла подкачки, ограничьте журнал systemd и отключите службы, которые серверу не нужны. В этой инструкции каждый шаг разобран для Debian 12/13 и Ubuntu 22.04/24.04 с пометками об отличиях Rocky Linux и AlmaLinux, а также показано, как читать сообщения OOM killer, если памяти всё же не хватило.
Шаг 1: Сначала измерьте, потом меняйте
Найдите реальных потребителей памяти, чтобы исправлять то, что действительно мешает.
free -h
ps -eo pid,user,rss,comm --sort=-rss | head -n 15 # RSS in KB, biggest first
systemd-cgtop -m -n 1 # memory per service
cat /proc/pressure/memory # memory pressure (PSI)
ps показывает самые крупные процессы, а systemd-cgtop суммирует память по службам — это нагляднее, когда у службы много рабочих процессов. Если в /proc/pressure/memory значение avg60 держится выше ~10, задачи часто ждут память. (В Rocky/AlmaLinux PSI выключен, если ядро не загружено с параметром psi=1.)
RSS учитывает общие библиотеки целиком в каждом процессе, поэтому сумма выглядит больше реальной. smem показывает PSS, где общая память честно делится между процессами:
apt install smem # Rocky / AlmaLinux: from EPEL
smem -tk -s pss -r | head -n 15
Шаг 2: Правильно читайте free -h: смотрите на available, а не на free
total used free shared buff/cache available
Mem: 961Mi 402Mi 74Mi 5.0Mi 484Mi 410Mi
Swap: 1.0Gi 12Mi 1.0Gi
Маленькое значение free — это нормально. Linux использует свободную ОЗУ как страничный кеш (buff/cache), чтобы держать файлы в памяти, и сразу отдает ее, когда она нужна программам. Важно значение available — оценка того, сколько памяти может получить новая нагрузка без использования swap. В примере выше «свободно» всего 74 МБ, но доступно 410 МБ, так что с сервером всё в порядке. Беспокоиться стоит, когда available постоянно ниже ~10% ОЗУ, а использование swap продолжает расти.
Шаг 3: Добавьте zram (сжатый swap в ОЗУ)
zram создает в ОЗУ устройство подкачки, где страницы хранятся в сжатом виде, часто в 2–3 раза компактнее. Подкачка в него гораздо быстрее, чем на диск, поэтому это очень действенный способ «растянуть» небольшой тариф. Цена — немного процессорного времени на сжатие.
Проверьте, есть ли zram в ядре
modprobe zram && echo "zram is available"
swapon --show
Если modprobe сообщает Module zram not found, ядро собрано без него (так бывает с некоторыми минимальными облачными ядрами). Переходите к шагу 4 и используйте только файл подкачки.
Вариант A: zram-tools (Debian, Ubuntu 22.04 и 24.04)
apt install zram-tools
cp /etc/default/zramswap /etc/default/zramswap.bak
cat > /etc/default/zramswap <<'EOF'
ALGO=zstd
PERCENT=50
PRIORITY=100
EOF
systemctl restart zramswap
zramctl
swapon --show
PERCENT=50 задает размер устройства zram в половину ОЗУ (несжатая емкость); реально занятая память равна сжатому объему. PRIORITY=100 заставляет ядро использовать zram раньше любого swap на диске.
Вариант B: systemd zram-generator (Debian 12/13, Ubuntu 24.04, Rocky/AlmaLinux 9)
apt install systemd-zram-generator # Rocky / AlmaLinux 9: dnf install zram-generator
cat > /etc/systemd/zram-generator.conf <<'EOF'
[zram0]
zram-size = min(ram / 2, 2048)
compression-algorithm = zstd
swap-priority = 100
EOF
systemctl daemon-reload
systemctl start [email protected]
zramctl
Если в Rocky/AlmaLinux dnf не находит пакет, сначала подключите EPEL (dnf install epel-release).
zstd не принимается, выберите lz4 — он к тому же быстрее на загруженном тарифе с одним ядром.Шаг 4: Оставьте небольшой файл подкачки как резерв и настройте swappiness
zram находится в ОЗУ, поэтому небольшой файл подкачки на диске всё равно полезен как последний резерв: на этих тарифах достаточно 512 МБ–1 ГБ. Создание файла, запись в fstab и изменение размера по шагам описаны в статье Как добавить swap на Linux VPS. По умолчанию у файла подкачки приоритет ниже, чем у zram, поэтому он используется, только когда zram заполнен.
С zram увеличьте vm.swappiness: выгрузить сжатые страницы в ОЗУ дешевле, чем сбросить файловый кеш и потом снова читать его с диска. vm.page-cluster = 0 запрещает ядру читать из swap сразу несколько страниц — это полезно только для дисков.
cat > /etc/sysctl.d/99-zram.conf <<'EOF'
vm.swappiness = 100
vm.page-cluster = 0
EOF
sysctl --system
sysctl vm.swappiness vm.vfs_cache_pressure
Файлы в /etc/sysctl.d/ читаются в алфавитном порядке, поэтому 99-zram.conf переопределяет vm.swappiness=10 из 99-swap.conf нашей статьи о swap. Без zram оставьте 10. Значение vm.vfs_cache_pressure=50 из той статьи подходит; никогда не ставьте 0 — тогда ядро вообще перестанет освобождать метаданные файловой системы.
Шаг 5: Ограничьте journald
Журнал systemd хранит логи на диске в /var/log/journal или в /run/log/journal, если журнал не постоянный. /run — это tmpfs, то есть эта копия находится в ОЗУ. Ограничьте оба:
journalctl --disk-usage
mkdir -p /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/size.conf <<'EOF'
[Journal]
SystemMaxUse=100M
RuntimeMaxUse=20M
MaxRetentionSec=1month
EOF
systemctl restart systemd-journald
journalctl --vacuum-size=100M
journalctl --disk-usage
SystemMaxUse ограничивает журнал на диске, RuntimeMaxUse — журнал в памяти, а MaxRetentionSec удаляет записи старше месяца. В Ubuntu rsyslog дополнительно пишет те же сообщения в /var/log/syslog; этот файл ротирует logrotate, так что обычно его можно оставить.
Шаг 6: Отключите службы, которые серверу не нужны
Прежде чем что-то останавливать, посмотрите, что запущено, и отключайте только то, что можете опознать:
systemctl list-units --type=service --state=running
systemctl status multipathd --no-pager # what is it and what started it?
systemctl list-dependencies --reverse multipathd.service # does anything need it?
| Служба | Где встречается | Можно отключить, если |
|---|---|---|
snapd | Ubuntu | snap list не показывает нужных вам snap-пакетов (см. ниже) |
multipathd | Ubuntu Server | multipath -ll ничего не выводит, что для VPS нормально |
ModemManager | Некоторые образы Ubuntu | Всегда на VPS: модема там нет |
tuned | Rocky / AlmaLinux | По желанию; экономит немного ОЗУ. Если сомневаетесь, оставьте |
# Ubuntu: multipath storage daemon (not needed without SAN/multipath disks)
multipath -ll # no output = not in use
systemctl disable --now multipathd.service multipathd.socket
# Modem manager - a VPS has no modem
systemctl disable --now ModemManager
# Undo at any time:
systemctl enable --now multipathd.service multipathd.socket
snapd занимает десятки мегабайт и монтирует loop-устройство для каждого snap-пакета. Его удаление стирает и все установленные snap-пакеты вместе с данными, поэтому сначала проверьте snap list. Если, например, certbot установлен как snap, перед удалением snapd поставьте его из apt. Файл с pin-приоритетом не даст apt снова установить snapd как зависимость.
snap list # what is installed as a snap?
apt purge snapd
cat > /etc/apt/preferences.d/no-snapd.pref <<'EOF'
Package: snapd
Pin: release a=*
Pin-Priority: -10
EOF
ssh/sshd, собственные службы systemd (systemd-journald, systemd-logind, systemd-networkd, systemd-resolved), networking или NetworkManager, cron, синхронизацию времени (chrony или systemd-timesyncd) и файрвол (ufw, firewalld, nftables). Также оставьте qemu-guest-agent и cloud-init, если они установлены: платформа может на них опираться, а после загрузки сервера они почти не занимают память. Используйте disable, а не mask, чтобы службу было легко включить обратно.Шаг 7: Перенесите автоматические обновления на тихое время
Не отключайте unattended-upgrades: обновления безопасности важнее нескольких мегабайт ОЗУ. Пик потребления памяти создает сам apt, поэтому просто запланируйте его на спокойное время. По умолчанию apt скачивает обновления в случайный момент в пределах 12-часового окна, а устанавливает примерно между 06:00 и 07:00; эти drop-in-файлы переносят загрузку на 04:00, а установку на 04:30 по времени сервера (проверьте его командой timedatectl):
mkdir -p /etc/systemd/system/apt-daily.timer.d /etc/systemd/system/apt-daily-upgrade.timer.d
cat > /etc/systemd/system/apt-daily.timer.d/override.conf <<'EOF'
[Timer]
OnCalendar=
OnCalendar=*-*-* 04:00
RandomizedDelaySec=15m
EOF
cat > /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf <<'EOF'
[Timer]
OnCalendar=
OnCalendar=*-*-* 04:30
RandomizedDelaySec=15m
EOF
systemctl daemon-reload
systemctl list-timers 'apt-*'
В Rocky/AlmaLinux с dnf-automatic аналогично измените dnf-automatic.timer.
Шаг 8: Выбирайте более легкое ПО
- Оставьте OpenSSH. В простое он занимает всего несколько мегабайт. Замены вроде Dropbear почти ничего не экономят, лишены части защитных функций и реже обновляются в дистрибутивах — риск того не стоит.
- Предпочитайте nginx, а не Apache с mod_php. В модели prefork у Apache каждый дочерний процесс несет полный интерпретатор PHP. nginx с PHP-FPM или хотя бы Apache с
mpm_eventи PHP-FPM расходуют гораздо меньше памяти. - Запускайте PHP-FPM с
pm = ondemand. Воркеры стартуют при поступлении запросов и завершаются после простоя, а не ждут в памяти. - Подгоните базу данных под тариф. Настройки MySQL и MariaDB по умолчанию рассчитаны на больший объем ОЗУ: уменьшите буферный пул и
max_connectionsи выключитеperformance_schema.
; /etc/php/8.2/fpm/pool.d/www.conf (Rocky / AlmaLinux: /etc/php-fpm.d/www.conf)
pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 500
Версия PHP в пути зависит от системы: 8.2 в Debian 12, 8.4 в Debian 13, 8.1 в Ubuntu 22.04 и 8.3 в Ubuntu 24.04. Задайте pm.max_children как объем памяти, который вы готовы отдать PHP, деленный на средний размер воркера, например 200 МБ / 40 МБ = 5. Измерьте размер, затем проверьте конфигурацию и перезагрузите службу:
# Average size of a PHP-FPM process
ps -eo rss,comm | awk '/php-fpm/ {s+=$1; n++} END {if (n) printf "%d processes, average %.0f MB\n", n, s/n/1024}'
php-fpm8.2 -t && systemctl reload php8.2-fpm # Rocky / AlmaLinux: php-fpm -t && systemctl reload php-fpm
Шаг 9: Разберитесь с OOM killer и найдите события OOM
Когда заканчиваются и ОЗУ, и swap, OOM killer ядра завершает один процесс, чтобы спасти систему, — обычно с самым высоким oom_score, то есть, как правило, самый крупный. Служба, которая «сама по себе» перезапускается, или исчезнувшая база данных — часто результат OOM. Поищите в журнале ядра:
journalctl -k | grep -i -E 'out of memory|oom-kill|killed process'
dmesg -T | grep -i -E 'out of memory|killed process'
journalctl -k -b -1 | grep -i 'killed process' # previous boot (persistent journal only)
Типичная строка выглядит так:
Out of memory: Killed process 812 (mariadbd) total-vm:1325400kB, anon-rss:301220kB, file-rss:0kB, shmem-rss:0kB, UID:106
В ней указан процесс и сколько памяти он занимал (anon-rss, в кБ). Если одну и ту же службу завершают снова и снова, уменьшите ее настройки, добавьте swap или zram либо ограничьте ее, как в шаге 10. В Ubuntu также проверьте systemctl status systemd-oomd: если эта служба активна, она может останавливать целые службы при нехватке памяти раньше ядра.
Шаг 10: Ограничьте прожорливую службу через systemd MemoryMax
Лимит памяти для одной службы защищает остальной сервер: если у PHP-FPM утечка памяти, ограничен будет только PHP-FPM, а SSH и база продолжат работать. MemoryHigh — мягкий лимит: выше него ядро замедляет службу и забирает у нее память; MemoryMax — жесткий лимит, при достижении которого OOM killer действует только внутри этой службы.
stat -fc %T /sys/fs/cgroup # cgroup2fs = cgroup v2 (required)
mkdir -p /etc/systemd/system/php8.2-fpm.service.d
cat > /etc/systemd/system/php8.2-fpm.service.d/memory.conf <<'EOF'
[Service]
MemoryHigh=250M
MemoryMax=300M
# A killed worker is replaced by the master instead of stopping the service
OOMPolicy=continue
Restart=on-failure
EOF
systemctl daemon-reload
systemctl restart php8.2-fpm
systemctl show php8.2-fpm -p MemoryHigh -p MemoryMax -p MemoryCurrent
Debian 12/13, Ubuntu 22.04/24.04 и Rocky/AlmaLinux 9 по умолчанию используют cgroup v2. В старых системах с cgroup v1, например Rocky/AlmaLinux 8, используйте MemoryLimit=; MemoryHigh работает только с v2. Не ставьте жесткие лимиты на саму базу данных: внезапное завершение приводит к восстановлению после сбоя. Лучше подберите ее настройки.
Шаг 11: Поймите, когда пора переходить на тариф побольше
Настройка помогает, когда память заканчивается лишь время от времени. Если available постоянно мало, swap всё время занят, а vmstat 1 10 показывает непрерывную подкачку (si/so), нагрузке просто нужно больше ОЗУ. Облачные серверы JUSTG можно перевести на старший тариф со страницы услуги; см. Как увеличить RAM, SSD или добавить IP на VPS.
Частые вопросы
zram лучше файла подкачки?
По скорости — да: он работает со скоростью памяти и не нагружает диск. Но его объем ограничен ОЗУ, поэтому лучшая схема для небольших тарифов — оба сразу: zram с высоким приоритетом для повседневной работы и небольшой файл подкачки как последний резерв.
zram сильно нагружает процессор?
Сжатие требует немного CPU, обычно совсем немного. Если на тарифе с одним ядром сервер постоянно подкачивает в zram, переключитесь на lz4 и уменьшите нагрузку: постоянная подкачка означает, что памяти слишком мало.
Можно ли запустить Docker на тарифе mini с 512 МБ?
Один-два контейнера с небольшими приложениями заработают, но демон Docker и среда выполнения контейнеров сами занимают память. Задайте лимиты памяти для каждого контейнера, используйте zram и swap, а для нескольких контейнеров присмотритесь к тарифу от 1 ГБ.
Если службы продолжают завершаться и вы не можете найти причину, создайте тикет по адресу https://www.justg.com/submitticket.php и приложите IP сервера, вывод free -h и journalctl -k | grep -i oom. В JUSTG работает AI-поддержка 24/7 по тикетам, а вопросы, требующие участия человека, подхватывают инженеры.