Docker в продакшене на VPS: что ломается и как это починить

10 мин чтения·Matthieu·productionsecurityvpsdocker-composedocker|

Docker работает на вашем ноутбуке. На публичном VPS он обходит файрвол, забивает диск логами, запускает всё от root и не имеет стратегии обновления. Вот 8 проблем, которые нужно решить.

Вы установили Docker на VPS. Запустили docker compose up. Приложение работает. Готово?

Не совсем. На ноутбуке никто не сканирует ваши порты, места на диске хватает, а безопасность не важна. На VPS с выходом в интернет дефолтные настройки Docker активно работают против вас.

На этой странице разобраны 8 проблем, с которыми вы столкнётесь, и ссылки на отдельное руководство для каждой. Просмотрите, найдите то, что относится к вашему серверу, и следуйте подробному гайду.

Требования

Это руководство предполагает, что вы уже знаете основы Docker. Если нужно подтянуть знания:

  • Команды и концепции Docker
  • Запуск многосервисных приложений с Compose

Все команды и вывод в этой статье протестированы на Debian 12 и Ubuntu 24.04 с Docker Engine 29.x и Compose v2 (версия 5.x).

Что ломается при запуске Docker на публичном VPS?

Восемь вещей ломаются при переносе Docker из разработки на публичный VPS: файрвол перестаёт работать, контейнеры запускаются от root с полным доступом к хосту, логи забивают диск без ротации, сеть контейнеров конфликтует с сетью хоста, у сервисов нет лимитов ресурсов и health check'ов, у томов нет стратегии бэкапов, порты открыты без TLS, а образы контейнеров устаревают без плана обновлений. Всё это дефолтное поведение Docker. Каждый из этих пунктов создаст проблемы в продакшене.

Краткий обзор перед подробными разборами:

# Проблема Диагностическая команда Риск
1 Обход файрвола sudo iptables -L DOCKER-USER -n Критический
2 Root-контейнеры docker info --format '{{.SecurityOptions}}' Критический
3 Диск забит логами du -sh /var/lib/docker/containers/*/*-json.log Высокий
4 Сетевые конфликты docker network ls Средний
5 Нет лимитов ресурсов docker stats --no-stream Высокий
6 Нет бэкапов томов docker volume ls Высокий
7 Нет reverse proxy / TLS ss -tlnp | grep -E ':80|:443' Критический
8 Устаревшие образы docker compose images Средний

Запустите эти команды на своём сервере прямо сейчас. Если какой-то вывод вас удивит, читайте соответствующий раздел ниже.

Docker обходит ваш файрвол?

Да. Docker напрямую манипулирует iptables, вставляя правила в таблицы nat и filter, которые обрабатываются до того, как UFW или firewalld увидят трафик. Когда вы публикуете порт через -p 8080:80, этот порт открыт всему интернету, даже если правила UFW говорят deny incoming.

Проверьте, затрагивает ли это вас:

sudo iptables -L DOCKER-USER -n -v

Если вывод показывает пустую цепочку или только безусловное правило RETURN без фильтрации, каждый опубликованный порт открыт настежь.

Корневая причина: пакеты, предназначенные для контейнеров Docker, проходят через цепочку FORWARD и цепочку DOCKER. UFW работает в цепочке INPUT. Эти две цепочки никогда не пересекаются.

Самый простой немедленный фикс — привязать опубликованные порты только к 127.0.0.1:

ports:
  - "127.0.0.1:8080:80"

Это делает порт доступным только с самого хоста. Используйте в связке с reverse proxy для обработки внешнего трафика.

Полный разбор фикса цепочки DOCKER-USER, интеграции с UFW и конфигурации nftables.

Опасно ли запускать Docker от root на VPS?

По умолчанию демон Docker работает от root, и контейнеры запускаются от root внутри своих пространств имён. Если атакующий сбежит из контейнера, он окажется на хосте с правами root. На публичном VPS это прямой путь к полной компрометации сервера.

Проверьте текущую конфигурацию:

docker info --format '{{.SecurityOptions}}'

Ищите name=rootless в выводе. Если его нет, ваш демон работает от root.

Также проверьте, запускаются ли контейнеры от root внутри:

docker ps -q | xargs -I {} docker exec {} id

Если у большинства контейнеров вы видите uid=0(root), они работают от root внутри контейнера. Образ, в котором Dockerfile задаёт USER appuser, покажет не-root uid.

У вас три уровня защиты:

  1. Rootless-режим запускает весь демон Docker под непривилегированным пользователем. Даже побег из контейнера приводит к непривилегированному пользователю на хосте.
  2. Профили seccomp ограничивают системные вызовы, доступные контейнерам. Docker поставляется с профилем по умолчанию, который блокирует около 44 опасных системных вызовов, но его можно ужесточить.
  3. AppArmor / SELinux добавляет мандатный контроль доступа поверх всего остального.

Полная настройка rootless Docker, пользовательских профилей seccomp и no-new-privileges.

Почему контейнеры Docker забивают диск?

Драйвер логов Docker по умолчанию — json-file без ограничения размера и без ротации. Каждая строка, которую приложение пишет в stdout, сохраняется в /var/lib/docker/containers/<id>/<id>-json.log. Активное веб-приложение может сгенерировать гигабайты логов за несколько дней.

Проверьте, сколько места занимают логи прямо сейчас:

sudo du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -rh | head -5

На VPS, работающем несколько недель без ротации логов, вы можете увидеть такой вывод:

4.2G    /var/lib/docker/containers/a1b2c3.../a1b2c3...-json.log
1.8G    /var/lib/docker/containers/d4e5f6.../d4e5f6...-json.log
256M    /var/lib/docker/containers/g7h8i9.../g7h8i9...-json.log

Логи — не единственный потребитель диска. Проверьте общее использование диска Docker:

docker system df

Типичный вывод на сервере с 5 сервисами:

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          12        5         4.2GB     2.8GB (66%)
Containers      8         5         52MB      12MB (23%)
Local Volumes   6         4         1.1GB     245MB (22%)
Build Cache     18        0         890MB     890MB (100%)

Этот build cache на 100% можно освободить. Те 7 неиспользуемых образов занимают 2,8 ГБ. На 40 ГБ диске VPS это быстро накапливается.

Решение состоит из двух частей: настроить ротацию логов в /etc/docker/daemon.json и организовать периодическую очистку неиспользуемых образов и build cache.

Полная настройка ротации логов, мониторинг диска и автоматическая очистка:

Как работает сеть Docker на одном VPS?

Docker создаёт собственную bridge-сеть (172.17.0.0/16 по умолчанию) и управляет IP-адресами контейнеров внутренне. На VPS это может конфликтовать с сетью хоста, подсетями VPN или другими сервисами.

Посмотрите, какие сети создал Docker:

docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a1b2c3d4e5f6   bridge    bridge    local
f6e5d4c3b2a1   host      host      local
9i8h7g6f5e4d   none      null      local

Каждый docker compose up создаёт дополнительную сеть. После нескольких проектов у вас может быть дюжина сетей с пересекающимися подсетями.

Ключевые решения для сети VPS:

  • Режим bridge (по умолчанию): контейнеры получают внутренние IP, порты публикуются через iptables. Работает для большинства конфигураций. Добавляет NAT-слой.
  • Режим host: контейнеры напрямую разделяют сетевой стек хоста. Нет NAT-оверхеда, но нет и изоляции портов. Полезен для чувствительных к производительности сервисов.
  • Пользовательские bridge-сети: контейнеры в одной пользовательской сети могут обращаться друг к другу по имени контейнера. Именно это Compose создаёт автоматически.

Важный момент на VPS: определяйте подсети явно в docker-compose.yml, а не позволяйте Docker выбирать. Это предотвращает конфликты с внутренними сетями хостинг-провайдера.

Подробный разбор bridge vs host, пользовательских подсетей и межконтейнерного DNS:

Что происходит без лимитов ресурсов и health check'ов?

Без лимитов памяти один контейнер с утечкой памяти сожрёт всю доступную RAM, вызовет Linux OOM killer и уронит не связанные контейнеры или даже SSH-демон. Без health check'ов Docker не может узнать, что контейнер сломан. Он остаётся в состоянии «running», отдавая ошибки.

Проверьте, установлены ли лимиты у ваших контейнеров:

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

Если столбец MEM % показывает значения, но вы никогда не устанавливали лимиты, эти проценты считаются от общего объёма RAM хоста. Контейнер, показывающий 45%, использует 45% всей памяти VPS, и ничто не мешает ему взять больше.

Минимальная конфигурация для продакшена в docker-compose.yml:

services:
  app:
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "1.0"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
    restart: unless-stopped

Политика restart: unless-stopped автоматически перезапускает упавшие контейнеры, но уважает ручные остановки. healthcheck позволяет Docker помечать нездоровые контейнеры и перезапускать их по счётчику попыток.

Полное руководство по лимитам ресурсов Compose, паттернам healthcheck и политикам перезапуска:

Как делать бэкапы Docker-томов?

Docker-тома хранят данные вне контейнеров. Если том удалён или повреждён, ваша база данных, загруженные файлы или конфигурация пропадают. У Docker нет встроенного механизма бэкапов для томов.

Посмотрите список томов и их размеры:

docker system df -v | grep -A 100 "Local Volumes space usage"

Или проверьте точки монтирования отдельных томов:

docker volume ls -q | xargs -I {} docker volume inspect {} --format '{{.Name}}: {{.Mountpoint}}'

Точки монтирования по умолчанию находятся в /var/lib/docker/volumes/. Их можно бэкапить стандартными инструментами Linux, но сначала нужно остановить или приостановить контейнер, чтобы избежать несогласованных снапшотов. Для баз данных копия файловой системы работающей базы — не надёжный бэкап. Сначала нужен дамп.

Стратегии бэкапов, процедуры восстановления и автоматическое планирование:

Как открыть Docker-сервисы с TLS?

Публикация портов напрямую через -p 443:443 на каждом контейнере не масштабируется дальше одного сервиса. Нужен reverse proxy, который терминирует TLS, маршрутизирует трафик к нужному контейнеру и занимается обновлением сертификатов.

Проверьте, что сейчас слушает на вашем VPS:

ss -tlnp | grep -E ':80|:443'

Если вы видите, что контейнеры приложений напрямую привязаны к портам 80 или 443, они открыты без прокси-слоя. Это значит, что каждому сервису нужно своё управление сертификатами, и вы не можете разместить несколько доменов на одном VPS.

Три варианта reverse proxy доминируют в экосистеме Docker:

Прокси Автообнаружение Автоматический TLS Стиль конфигурации
Traefik Да (Docker-лейблы) Let's Encrypt встроен Лейблы + YAML
Caddy Через плагин Автоматически по умолчанию Caddyfile
Nginx Proxy Manager Docker-сокет UI Let's Encrypt Веб-интерфейс

Traefik — самый распространённый выбор для Docker-native развёртываний, потому что он читает лейблы контейнеров и автоматически генерирует правила маршрутизации. Не нужно обновлять конфигурационные файлы при добавлении сервиса.

Сравнение, руководства по установке и конфигурация TLS для каждого варианта:

Какая стратегия обновления контейнеров Docker на VPS?

Docker-образы не обновляются сами. Если вы запускаете postgres:16 и выходит патч безопасности в postgres:16.4, ваш контейнер продолжает работать со старым образом, пока вы явно не подтянете новый и не пересоздадите контейнер.

Проверьте, какие образы используют ваши сервисы:

docker compose images

Сравните дайджесты образов с последними доступными версиями:

docker compose pull --dry-run 2>&1

Если какой-то образ показан как «pulled» в выводе dry-run, ваша работающая версия устарела.

Ключевые решения:

  • Фиксируйте теги образов: никогда не используйте :latest в продакшене. Указывайте конкретные теги версий, например postgres:16.4, или даже SHA-дайджесты.
  • Запланированные pulls: запускайте docker compose pull && docker compose up -d по расписанию, но сначала тестируйте обновления на стейджинге.
  • Перезапуск без даунтайма: используйте health check'и и docker compose up -d --no-deps <service> для обновления по одному сервису за раз.
  • Сканирование образов: инструменты вроде Trivy сканируют образы на известные CVE перед деплоем.

Стратегии обновления, паттерны пиннинга образов и деплой без даунтайма:

Нужен ли Kubernetes для Docker в продакшене?

Нет. Docker Compose — готовый к продакшену инструмент деплоя для нагрузок на одном сервере. Kubernetes решает оркестрацию на нескольких нодах, автоматическое масштабирование и самовосстановление в кластере. Если ваше приложение работает на одном VPS, Compose даёт всё необходимое без операционных расходов Kubernetes.

Где Compose работает:

  • Один VPS с 3-15 контейнерами
  • Статичные или предсказуемые паттерны трафика
  • Небольшая команда без выделенных платформенных инженеров
  • Сервисы, которые терпят секунды даунтайма при обновлениях

Где вы перерастаете Compose:

  • Нужно несколько серверов для высокой доступности
  • Автомасштабирование при пиках трафика
  • Сложные service mesh'и с сотнями микросервисов
  • Требования нулевого даунтайма с blue-green деплоями между нодами

Лёгкий промежуточный вариант — K3s, урезанный Kubernetes, который работает на одном VPS с ~512 МБ оверхеда RAM. Но для большинства пет-проектов, SaaS MVP и небольших продакшен-приложений Compose — правильный инструмент.

Сколько RAM нужно VPS для Docker в продакшене?

VPS с Docker в продакшене требуется минимум 4 ГБ RAM для 3-5 контейнеров. Сам демон Docker использует примерно 100-200 МБ. Каждый контейнер добавляет свой объём потребления. Без установленных лимитов памяти один проблемный контейнер может сожрать всё.

Размер VPS Контейнеры Подходит для
4 ГБ RAM 3-5 лёгких Блог, API, база данных, Redis
8 ГБ RAM 5-10 смешанных SaaS MVP, несколько сервисов, стек мониторинга
16 ГБ RAM 10-20 Несколько проектов, CI-раннеры, тяжёлые базы данных
32 ГБ RAM 20+ Инференс ИИ, большие базы данных, билд-серверы

Хранилище важно не менее RAM. Docker-образы, тома, логи и build cache накапливаются. Планируйте минимум 40 ГБ NVMe-хранилища и настройте мониторинг, описанный в , с первого дня.

CPU редко является узким местом для контейнеризированных веб-сервисов. 4 vCPU справляются с большинством нагрузок. Исключения: транскодирование видео, инференс ИИ и билд-серверы.

Для VPS с NVMe-хранилищем, рассчитанным на продакшен-нагрузки Docker, смотрите тарифы VPS Virtua Cloud.

Чеклист для продакшена

Перед запуском Docker на VPS в продакшене проверьте каждый пункт:

  • Правила файрвола блокируют все опубликованные Docker порты, кроме тех, что идут через reverse proxy
  • Контейнеры запускаются от непривилегированных пользователей, где это возможно
  • Демон Docker работает в rootless-режиме или контейнеры используют seccomp + no-new-privileges
  • Ротация логов настроена в /etc/docker/daemon.json
  • docker system prune запускается по расписанию (еженедельный cron)
  • У каждого сервиса есть лимиты памяти и CPU в docker-compose.yml
  • У каждого сервиса есть health check
  • Политика перезапуска установлена (unless-stopped или on-failure)
  • Тома с важными данными имеют автоматические бэкапы
  • Reverse proxy обеспечивает терминацию TLS и обновление сертификатов
  • Теги образов зафиксированы на конкретных версиях, не :latest
  • У вас есть протестированная процедура обновления для подтягивания новых образов
  • journalctl -u docker и docker logs <container> работают и вы знаете, куда смотреть

Если не можете отметить каждый пункт, проработайте статьи, на которые ведут ссылки в каждом разделе выше.


Авторское право 2026 Virtua.Cloud. Все права защищены. Данный контент является оригинальным произведением команды Virtua.Cloud. Воспроизведение, повторная публикация или распространение без письменного разрешения запрещены.

Готовы попробовать?

Запустите Docker в продакшн на надёжном VPS.

Смотреть тарифы VPS