SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-28

Docker на VPS: що насправді змінюється

Docker на VPS працює так само, але має 4 ризики: нестача RAM, обхід UFW опублікованими портами, зупинка після reboot і переповнений disk.

Що змінюється під час запуску Docker на VPS

Docker на VPS використовує той самий engine і ті самі images, що й Docker на вашому ноутбуці, тому всі вже знайомі команди продовжують працювати. Змінюється середовище, у якому він працює. На ноутбуці зазвичай є вільна пам’ять, firewall, який ніхто не сканує, і достатньо великий диск, стан якого не потрібно постійно перевіряти. Орендований сервер має фіксований ліміт пам’яті, публічну IP-адресу, яку починають сканувати через кілька хвилин після запуску, і root filesystem, який Docker заповнить без попередження.

На невеликому сервері більшість проблем спричиняють чотири відмінності:

  • Пам’ять обмежена, і kernel усуває її нестачу, завершуючи роботу процесу.
  • Опублікований порт обходить UFW (uncomplicated firewall), оскільки Docker створює власні правила firewall.
  • Контейнери не запускаються знову після перезавантаження, якщо це не було налаштовано заздалегідь.
  • Images, containers, volumes і build cache збільшуються, доки диск не заповниться.

У кожному розділі нижче описано проблему, наведено фактичний рядок повідомлення, який ви побачите, і вказано посібник із докладним рішенням. Якщо ви ще не створили compose file, спочатку прочитайте Основи Docker Compose на VPS, а потім поверніться сюди. Ця сторінка передбачає, що ви вже вмієте запускати stack.

Скільки оперативної пам’яті використовує Docker container?

Менше, ніж зазвичай очікують. Container — це процес у cgroup (control group), а не virtual machine, тому в нього немає guest kernel і фіксованого обсягу пам’яті. Витрати визначаються тим, до чого звертається процес усередині. Тому повний стек може працювати в 2 GB, тоді як той самий стек, побудований на virtual machines, не помістився б.

Наведені нижче значення є типовими показниками в режимі простою для стандартних images на Ubuntu 24.04 зі стандартною конфігурацією. Їх зчитано з docker stats через кілька хвилин після запуску. Використовуйте їх як початкові дані для планування, а не як benchmark вашого навантаження. Перш ніж покладатися на будь-яке значення, зокрема на наведені тут, виконайте docker stats --no-stream на власному сервері.

ChartTypical idle container memory, stock images, megabytes
The data behind this chart
[
  {
    "label": "nginx",
    "idle_mb": 8,
    "budget_mb": 64
  },
  {
    "label": "Redis 7",
    "idle_mb": 12,
    "budget_mb": 128
  },
  {
    "label": "Traefik v3",
    "idle_mb": 40,
    "budget_mb": 128
  },
  {
    "label": "PostgreSQL 16",
    "idle_mb": 45,
    "budget_mb": 512
  },
  {
    "label": "Uptime Kuma",
    "idle_mb": 95,
    "budget_mb": 256
  },
  {
    "label": "MariaDB 11",
    "idle_mb": 190,
    "budget_mb": 512
  },
  {
    "label": "Nextcloud (Apache)",
    "idle_mb": 210,
    "budget_mb": 768
  }
]

Два стовпці мають різне призначення. idle_mb — це обсяг пам’яті, який container використовує в режимі простою. budget_mb — це обсяг, який слід закладати під час планування, оскільки реальне використання не обмежується простоєм. PostgreSQL використовує близько 45 MB у режимі простою, але потребує 512 MB після встановлення з’єднань і початку роботи сортування та cache. Для планування використовуйте стовпець із резервом. Для налагодження — стовпець із показником простою.

Зверніть увагу на розподіл цих 7 рядків. nginx використовує 8 MB у режимі простою, а Nextcloud — 210 MB. Reverse proxy перед вашими застосунками майже не споживає пам’яті. Саме database і PHP-застосунок визначають вимоги до сервера.

Щодо docker stats є важливе застереження: показник пам’яті включає page cache, який сформували власні операції читання файлів container, тому після запуску він деякий час зростає, а потім стабілізується. Спостерігайте за ним протягом години, перш ніж робити висновок про витік пам’яті.

Розрахунок ресурсів VPS: що вміщується у 2 GB, 4 GB і 8 GB

Спочатку відніміть частку хоста. Kernel, systemd, journald, sshd і Docker daemon використовують ту саму RAM, що й ваші контейнери, а dockerd разом із containerd займає близько 100 MB. Також потрібна вільна пам’ять для page cache і для пікового навантаження під час збирання image або створення дампа бази даних.

ChartRAM budget by plan size, megabytes
The data behind this chart
[
  {
    "plan": "2 GB VPS",
    "total_mb": 2048,
    "host_reserve_mb": 768,
    "container_mb": 1280
  },
  {
    "plan": "4 GB VPS",
    "total_mb": 4096,
    "host_reserve_mb": 1024,
    "container_mb": 3072
  },
  {
    "plan": "8 GB VPS",
    "total_mb": 8192,
    "host_reserve_mb": 1536,
    "container_mb": 6656
  }
]

host_reserve_mb охоплює операційну систему, Docker daemon і запас, який зберігає працездатність сервера під навантаженням. Залишок — це container_mb, і лише цю кількість можна витратити. На більших планах резерв зростає: від 768 MB на найменшому сервері до 1536 MB на найбільшому, оскільки більший сервер запускає більше контейнерів, записує більше журналів і потребує більшого page cache.

План на 2 GB залишає 1280 MB для контейнерів. Витратьте 512 MB на PostgreSQL і 128 MB на Traefik — і половини доступної пам’яті вже немає. Решти вистачить на два невеликі застосунки приблизно по 256 MB кожен. Це реальний і корисний сервер. Але його недостатньо для Nextcloud і кластера пошуку одночасно.

План на 4 GB залишає 3072 MB. Цього достатньо для бази даних, reverse proxy, трьох застосунків і контейнера моніторингу, які працюють одночасно. Це найменший розмір, який варто використовувати для важливих систем, оскільки вільна пам’ять поглинає наслідки невдалого розгортання.

План на 8 GB залишає 6656 MB із 8192 MB, і обмеження зазвичай переміщується з пам’яті на CPU або пропускну здатність диска. Розмір одного класу контейнерів визначається конфігурацією, а не навантаженням: локальний model server резервує KV cache пропорційно до розміру context window, тому збільшення num_ctx в Ollama може додати до бюджету гігабайти ще до надходження першого запиту. Якщо розрахунок показує, що ваш стек не вміщується, оберіть більший план замість спроб оптимізувати конфігурацію в межах недостатніх ресурсів: скільки насправді коштує VPS пояснює, чого варті додаткові гігабайти на місяць.

Два правила допомагають робити точний розрахунок. Встановіть memory limit для кожного сервісу, щоб один процес, який споживає надмірно багато ресурсів, не вивів з ладу весь сервер. Не витрачайте весь доступний бюджет, оскільки docker compose build і pg_dump обом потрібна пам’ять у найгірший момент. У матеріалі Memory limits у Docker Compose наведено синтаксис і типові проблеми.

Чому мій контейнер завершується з кодом 137?

Тому що ядро його завершило. 137 — це 128 плюс 9, а сигнал 9 — це SIGKILL. Контейнеру знадобилося більше пам’яті, ніж йому дозволено, тому OOM killer завершив його.

CONTAINER ID   IMAGE         STATUS
9f2c1a4d7b3e   postgres:16   Exited (137) 4 minutes ago

Підтвердьте причину, а не робіть припущень:

docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory"

"OOMKilled": true означає, що контейнер досяг власного ліміту cgroup, а журнал ядра вказує процес, який воно вибрало:

Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kB

Це сприятливий випадок, оскільки проблема обмежилася одним контейнером. Несприятливий випадок — контейнер взагалі без ліміту. Без ліміту його верхня межа — вся машина. Тому витік пам’яті в одному сервісі виснажує ресурси хоста, після чого ядро вибирає процес для завершення за розміром серед усієї системи. У рядку журналу немає префікса Memory cgroup, і він має вигляд Out of memory: Killed process 2417 (postgres). Найчастіше ядро вибирає вашу базу даних, а контейнер із витоком пам’яті продовжує працювати. Тому ліміт для кожного сервісу важливіший за точне значення будь-якого окремого ліміту.

Swap змінює час виникнення проблеми, але не змінює арифметику. Більшість образів VPS постачаються без swap. Перевірте це за допомогою swapon --show. Якщо swap відсутній, команда нічого не виведе. Файл swap дає ядру змогу переміщувати неактивні сторінки пам’яті, виграючи кілька хвилин для виявлення проблеми.

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h

free -h тепер має показати ненульове загальне значення в рядку Swap. Swap не збільшує обсяг RAM. Сервер під постійним тиском з боку пам’яті може сповільнитися настільки, що ви не зможете підключитися через SSH для виправлення проблеми. Тому використовуйте swap як буфер для аварійних ситуацій і виправте розрахунок ресурсів.

Чому UFW не блокує опублікований Docker-порт?

Тому що цей трафік не потрапляє до ланцюжка, який контролює UFW. Коли ви публікуєте порт за допомогою -p 5432:5432 або запису compose ports:, daemon додає правило DNAT (destination network address translation) до таблиці nat і правило accept до власного ланцюжка DOCKER. Пакет, адресований контейнеру, переспрямовується до нього, а не доставляється на host, тому обробляється в шляху FORWARD і не проходить через правила INPUT, які записує UFW.

Ви можете побачити це на сервері:

sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432

UFW може показувати 5432 DENY IN Anywhere, тоді як у таблиці nat є правило DNAT tcp ... to:172.18.0.2:5432 для того самого порту. З іншої машини nc -vz your.server.ip 5432 усе одно встановлює з’єднання. База даних доступна з публічного інтернету, хоча firewall стверджує протилежне.

Рішення — публікувати менше портів. Контейнери в одному compose-проєкті використовують спільну мережу та звертаються один до одного за іменем сервісу, тому базі даних, яка обслуговує лише розташований поруч застосунок, взагалі не потрібен запис ports:. Якщо потрібен локальний доступ, прив’яжіть опублікований порт до loopback:

services:
  db:
    image: postgres:16
    ports:
      - "127.0.0.1:5432:5432"

Після docker compose up -d зовнішнє nc -vz your.server.ip 5432 завершується помилкою, а psql -h 127.0.0.1 -p 5432 на самому сервері продовжує працювати. У правильно налаштованому невеликому стеку лише reverse proxy публікує порти — 80 і 443. У матеріалі Чому опубліковані Docker-порти обходять UFW описано ланцюжок DOCKER-USER для випадків, коли порт потрібно опублікувати й водночас фільтрувати, а в матеріалі Основи firewall UFW описано базові правила host.

Чому мої контейнери зникли після перезавантаження?

Тому що ніщо не вказало їм запуститися знову. Контейнер створюється з політикою перезапуску no, якщо не вказати іншу. Тому після перезавантаження він залишається зупиненим, а daemon не запускає його автоматично. Перезавантаження не є рідкісними на VPS: оновлення ядра через unattended upgrades, технічне обслуговування провайдера та описана вище послідовність OOM завершуються перезавантаженням.

Мають виконуватися дві умови. Daemon має запускатися під час завантаження системи:

systemctl is-enabled docker

У стандартному встановленні Ubuntu ця команда виводить enabled. Потім для кожного сервісу потрібно задати політику:

services:
  app:
    image: ghcr.io/example/app:1.4
    restart: unless-stopped

unless-stopped запускає контейнер після перезавантаження та не запускає його знову, якщо ви навмисно зупинили контейнер. always також запускає контейнери, які ви зупинили навмисно, щоразу після перезапуску daemon. Під час налагодження це може бути несподіваним. Самого редагування файлу недостатньо, оскільки політика перезапуску задається під час створення контейнера. Виконайте docker compose up -d, щоб створити його повторно, а потім перевірте поточне значення:

docker inspect my-app | grep -A3 RestartPolicy

Після цього навмисно перезавантажте сервер і виконайте docker compose ps у каталозі проєкту. Стек, який переживає заплановане перезавантаження, переживе й незаплановане. Якщо стеку потрібна гарантована послідовність запуску або одноразове завдання під час завантаження системи, краще використати unit systemd: у матеріалі запуск Docker Compose під час завантаження наведено файл unit. Щоб перевірити, чи контейнер, який запустився знову, справді обслуговує запити, додайте перевірки стану Compose.

Чому на моєму VPS закінчилося місце на диску?

Тому що Docker зберігає все, доки ви явно не видалите ці дані. На диску залишаються всі теги образів, які ви коли-небудь завантажували, усі зупинені контейнери, усі анонімні томи, що залишилися після повторного створення, і кожен шар кешу збирання. У root-файловій системі обсягом 40 GB або 80 GB, що є типовим для таких тарифів, це призводить до відмови сервісів за місяці, а не за роки.

Заповнений диск не обов’язково спричиняє аварійне завершення роботи. Протягом однієї години ви можете отримати no space left on device від контейнера, apt, від journald і від docker pull. PostgreSQL припиняє приймати записи. Сервер і далі працює, тому таку проблему важче помітити, ніж цикл перезавантажень.

Перед видаленням перевірте стан:

docker system df
df -h /

docker system df розподіляє загальний обсяг між образами, контейнерами, локальними томами та кешем збирання. Для кожної категорії поруч показано стовпець RECLAIMABLE. На сервері, де образи збираються локально, кеш збирання зазвичай займає найбільше місця.

docker image prune -a
docker builder prune
docker system df

docker image prune -a видаляє всі образи, які не використовує жоден контейнер. docker builder prune очищає кеш збирання. Обидві команди безпечні під час роботи сервісів, оскільки дані, що використовуються, буде пропущено. Небезпечно виконувати docker system prune --volumes, оскільки ця команда видаляє всі томи, на які зараз не посилається жоден контейнер. Стек, який ви зупинили на вихідні, має саме таку структуру, і його том бази даних буде видалено. Перед використанням цього прапорця прочитайте про bind mounts і іменовані томи та спочатку створіть резервну копію.

Журнали контейнерів ростуть непомітніше. Драйвер json-file за замовчуванням не має обмеження розміру, тому один контейнер із великою кількістю повідомлень може записати гігабайти даних у /var/lib/docker/containers. Установіть обмеження для кожного контейнера в /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Застосуйте зміни за допомогою sudo systemctl restart docker. Ця команда перезапускає контейнери, тому виберіть відповідний час. Обмеження застосовується до контейнерів, створених після внесення змін. Тому повторно створіть запущені контейнери за допомогою docker compose up -d --force-recreate і перевірте результат:

docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containers

У виведенні inspect має бути встановлено max-size. Якщо значення порожнє, цей контейнер було створено до внесення змін, і він досі записує журнали без обмеження.

Звички, які підтримують невеликий Docker-сервер у справному стані

Для цього не потрібні dashboard або інструмент, який доведеться опановувати.

  • Запускайте docker system df і df -h / першого числа кожного місяця. Дві команди, тридцять секунд — і ви побачите тенденцію задовго до збою сервісу.
  • Встановіть memory limit для кожного сервісу, зокрема для тих, які здаються вам невеликими. Обмеження перетворює збій усього хоста на перезапуск одного контейнера.
  • Моніторте сервер з іншого місця, щоб дізнатися про нестачу memory або disk space до того, як відреагує kernel. Uptime Kuma працює в контейнері та споживає в режимі простою приблизно 95 MB.
  • Створюйте резервні копії volumes, а не контейнерів. Контейнер можна видалити, а volume — ні. резервні копії restic на VPS охоплюють розклад і перевірку відновлення.
  • Фіксуйте image tags у compose-файлі та оновлюйте їх у вибраний день. З latest версія, яку ви отримаєте з наступного docker compose pull, буде саме тією, що вийшла того ранку.

Невеликий VPS із Docker залишатиметься справним роками, якщо чотири показники перебувають у межах норми: memory budget, список опублікованих портів, restart policy для кожного сервісу та вільне місце на диску. Усе інше — це той самий Docker, який ви вже запускаєте вдома.

FAQ

Скільки RAM потрібно для запуску Docker на VPS?

Сам Docker споживає небагато ресурсів. Разом daemon і containerd використовують близько 100 MB, а решта потреб припадає на контейнери. Спочатку зарезервуйте частину ресурсів для хоста: 768 MB на сервері з 2048 MB для операційної системи, daemon і запасу ресурсів. Це залишає 1280 MB для контейнерів. База даних на 512 MB, reverse proxy на 128 MB і два невеликі застосунки вмістяться в цей обсяг. Вимірюйте споживання власного стека за допомогою docker stats --no-stream, а не покладайтеся на опубліковані значення.

Чи можна запускати Docker на VPS з 1 GB RAM?

Так, якщо це один або два легкі контейнери. Перед запуском створіть swap file. Приблизно половина сервера з 1 GB RAM буде зайнята після запуску операційної системи та Docker daemon. Залишку вистачить для невеликого застосунку і reverse proxy, але не для бази даних під реальним навантаженням. Створення образів на такому сервері завершиться помилкою або призведе до зупинки іншого процесу. Створюйте образи в іншому місці та завантажуйте готовий образ.

Чи захищає UFW контейнер Docker?

Ні, якщо для контейнера опубліковано порт. Docker створює власні правила DNAT і пересилання. Тому пакет, надісланий на опублікований порт контейнера, переспрямовується до контейнера, а не передається хосту. Правила INPUT, якими керує UFW, його не бачать. ufw deny 5432 може бути активним, поки цей порт доступний з інтернету. Опублікуйте порт лише на loopback за допомогою 127.0.0.1:5432:5432, не публікуйте внутрішні сервіси або фільтруйте трафік у ланцюжку DOCKER-USER.

Чи перезапустяться мої контейнери після перезавантаження VPS?

Лише якщо під час їх створення було задано restart policy. Додайте restart: unless-stopped для кожного сервісу, виконайте docker compose up -d, щоб контейнери було створено повторно з цією політикою, і переконайтеся, що systemctl is-enabled docker виводить enabled. Потім навмисно перезавантажте сервер і перевірте docker compose ps. Restart policy, яку ви ніколи не тестували, не є надійною політикою перезапуску.

Як часто потрібно очищати Docker images?

Для більшості невеликих серверів достатньо виконувати очищення щомісяця або щоразу, коли docker system df повідомляє про доступний для звільнення простір, який вам потрібен. docker image prune -a і docker builder prune безпечно виконувати під час роботи сервісів, оскільки образи та кеш, які використовуються, буде пропущено. Не використовуйте docker system prune --volumes, якщо точно не знаєте, які volumes не мають посилань. Ця команда видаляє дані будь-якого стека, який на цей момент зупинено.