VDS · Docker и контейнеры

Ограничения памяти и процессора для контейнеров

Как задать контейнеру потолок по памяти и процессору, почему без этого один сервис роняет все остальные и как понять, что процесс убило ядро.

Обновлено 23 августа 2026 г.

По умолчанию контейнер может занять всю память и все ядра машины. На VDS-1 с двумя гигабайтами это заканчивается предсказуемо: один сервис распухает, ядро начинает убивать процессы, и падает не он, а база данных рядом.

Как задать потолок

services:
  app:
    image: ghcr.io/example/app:1.5.0
    deploy:
      resources:
        limits:
          memory: 768M
          cpus: "1.0"
        reservations:
          memory: 256M

Compose применяет deploy.resources.limits и при обычном docker compose up -d. Эквивалент для docker run:

docker run -d --memory 768m --cpus 1.0 --memory-swap 768m <образ>

--memory-swap, равный --memory, запрещает уход в подкачку: лучше честное падение, чем машина, которая часами еле шевелится.

Сколько выделять

Отталкивайтесь от ступени и оставляйте системе запас.

СтупеньПамятьРазумный суммарный потолок контейнеров
VDS-12 ГБдо 1.5 ГБ
VDS-24 ГБдо 3 ГБ
VDS-38 ГБдо 6.5 ГБ
VDS-416 ГБдо 14 ГБ
VDS-524 ГБдо 21 ГБ
VDS-632 ГБдо 29 ГБ

Остаток нужен системе, файловому кешу и самому Docker. Сумма лимитов больше объёма памяти — это не ошибка настройки, а отложенная авария.

Для процессора: cpus: "1.0" — одно полное ядро. На VDS-1 ядро одно, поэтому ограничивать процессор почти нет смысла, а вот на VDS-4 и выше полезно не дать сборке занять все шесть.

Что реально потребляется

docker stats --no-stream
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.CPUPerc}}"
free -h

Понаблюдайте за сервисом сутки под нагрузкой и ставьте лимит примерно в полтора раза выше пика. Слишком узкий лимит хуже отсутствия: сервис будет перезапускаться в самый нужный момент.

Полноэкранные htop и btop работают только в консоли «Экран» (VNC). По текстовой консоли размер окна не передаётся, там всегда 80×24, и интерфейс разъезжается. Для быстрых замеров в текстовой консоли используйте docker stats --no-stream и free -h.

Как понять, что убило по памяти

docker inspect -f '{{.State.ExitCode}} {{.State.OOMKilled}}' <контейнер>
sudo dmesg -T | grep -i -E "out of memory|killed process"

Код выхода 137 вместе с OOMKilled: true — приговор однозначный. Дальше два пути: поднять лимит, если он был занижен, или разобраться, почему приложение растёт — утечка не лечится увеличением памяти, только откладывается.

Java и другие особые случаи

Виртуальная машина Java до сих пор иногда видит память хоста, а не лимит контейнера, и выставляет размер кучи с запасом, которого нет. Задавайте явно:

environment:
  JAVA_TOOL_OPTIONS: "-Xmx512m -Xms256m"

То же касается PostgreSQL (shared_buffers) и любых сервисов с собственным пулом.

Частые ошибки

  • Лимиты не заданы вообще. Самая частая причина «машина зависла, ничего не отвечает».
  • Лимит есть, а проверки здоровья нет. Контейнер убит и не перезапущен. Добавьте restart: unless-stopped и healthcheck — см. обновление контейнеров.
  • Забыли про диск. Память ограничили, а запись упирается в полку: см. скорость диска.