Docker в продакшене на VPS: что ломается и как это починить
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.
У вас три уровня защиты:
- Rootless-режим запускает весь демон Docker под непривилегированным пользователем. Даже побег из контейнера приводит к непривилегированному пользователю на хосте.
- Профили seccomp ограничивают системные вызовы, доступные контейнерам. Docker поставляется с профилем по умолчанию, который блокирует около 44 опасных системных вызовов, но его можно ужесточить.
- 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. Воспроизведение, повторная публикация или распространение без письменного разрешения запрещены.