Ограничения памяти и процессора для контейнеров
Как задать контейнеру потолок по памяти и процессору, почему без этого один сервис роняет все остальные и как понять, что процесс убило ядро.
Обновлено 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-1 | 2 ГБ | до 1.5 ГБ |
| VDS-2 | 4 ГБ | до 3 ГБ |
| VDS-3 | 8 ГБ | до 6.5 ГБ |
| VDS-4 | 16 ГБ | до 14 ГБ |
| VDS-5 | 24 ГБ | до 21 ГБ |
| VDS-6 | 32 ГБ | до 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— см. обновление контейнеров. - Забыли про диск. Память ограничили, а запись упирается в полку: см. скорость диска.