Docker на Linux VPS: production-развертывание, сеть, тома, HTTPS, обновления и резервное копирование
Приложение собрано в Docker, локально запускается одной командой и успешно открывается в браузере. Следующий шаг кажется очевидным: заказать Linux VPS, перенести туда проект и выполнить docker compose up -d.
Для тестового стенда этого иногда действительно достаточно. Для постоянной работы — нет.
После первой перезагрузки VPS может оказаться, что контейнер не запустился автоматически. После пересоздания контейнера — что база хранилась внутри него и данные пропали. А опубликованный «временно для проверки» порт PostgreSQL может остаться доступным из Интернета намного дольше, чем планировалось.
Production-развертывание Docker-приложения — это не отдельная магическая технология. Нужно просто заранее решить несколько эксплуатационных вопросов: где лежат постоянные данные, какие сервисы доступны снаружи, как работает HTTPS, что произойдет после reboot, как обновлять контейнеры и из чего восстанавливать приложение после сбоя.
В этой статье разберем именно такую схему для одного Linux VPS. Без Kubernetes и сложной оркестрации — обычное приложение, Docker Compose, reverse proxy, база данных и понятный порядок обслуживания.
Как выглядит production-схема Docker-приложения на VPS
Самый простой Docker-проект часто начинается с одного контейнера:
Интернет → контейнер приложения
Для production этого обычно мало. Если у приложения есть база, HTTPS и постоянные данные, схема становится примерно такой:
Интернет
│
80 / 443
│
▼
Reverse proxy
Nginx / другой
│
внутренняя Docker-сеть
│
▼
Приложение
│
внутренняя Docker-сеть
│
▼
База данных
│
▼
Persistent volume
Reverse proxy, или обратный прокси, принимает запросы пользователей и передает их внутреннему приложению. На практике он удобен для HTTPS, работы с доменами и отделения публичной точки входа от самих контейнеров.
Такое разделение дает простой принцип:
- снаружи открываем только то, что действительно должно быть публичным;
- между контейнерами используем внутреннюю сеть;
- данные базы храним независимо от жизненного цикла контейнера;
- конфигурацию приложения описываем так, чтобы окружение можно было воспроизвести после сбоя.
Docker Compose по умолчанию создает сеть проекта, а сервисы внутри нее могут обращаться друг к другу по именам сервисов. Поэтому приложению обычно не нужен IP контейнера базы: достаточно, например, имени db.
Как подготовить Linux VPS и развернуть приложение
Что проверить до установки Docker
Если VPS только заказан, сначала нужно подготовить сам Linux:
- установить обновления безопасности;
- создать отдельного пользователя;
- настроить SSH;
- проверить firewall;
- убедиться, что хватает RAM и дискового пространства;
- настроить время;
- продумать резервное копирование.
Этот этап подробно разобран в статье «Первичная настройка Ubuntu VPS после заказа: пользователь, SSH, обновления, firewall и резервное копирование».
Для Docker дополнительно полезно проверить архитектуру системы:
dpkg --print-architectureИ версию Ubuntu:
. /etc/os-release && echo "$PRETTY_NAME"Устанавливаем Docker Engine и Compose plugin
Для рабочего VPS лучше использовать официальный apt-репозиторий Docker, а не запускать установку через случайный shell-скрипт.
Сначала добавим необходимые пакеты и каталог для ключа:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyringsЗатем загрузим официальный ключ Docker:
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.ascСоздадим описание репозитория:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOFОбновим индекс пакетов:
sudo apt updateИ установим Docker Engine вместе с Compose plugin:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginПосле установки проверьте службу Docker:
sudo systemctl status dockerИ версию Compose:
sudo docker compose versionКак организовать каталог проекта
Для небольшого приложения удобно держать deployment-конфигурацию в отдельном каталоге, например:
/opt/myapp/
├── compose.yaml
├── .env
├── nginx/
│ └── conf.d/
└── backup/Это только пример. Код приложения может вообще не лежать на VPS, если используется готовый image из registry.
Для production полезно разделять:
- image — код приложения и его зависимости;
- Compose-конфигурацию — как именно приложение запускается;
- постоянные данные — база и пользовательские файлы;
- секреты — пароли, токены и ключи.
Чем меньше ручных изменений сделано внутри уже запущенного контейнера, тем проще потом восстановить сервер.
Минимальный compose.yaml
Возьмем условное веб-приложение и PostgreSQL.
services:
app:
image: registry.example.com/myapp:1.4.2
restart: unless-stopped
environment:
DB_HOST: db
DB_NAME: app
DB_USER: app
DB_PASSWORD: ${DB_PASSWORD}
depends_on:
db:
condition: service_healthy
networks:
- backend
db:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
networks:
backend:
volumes:
postgres_data:Здесь специально нет ports у PostgreSQL. Приложение и база находятся в одной Docker-сети и могут общаться напрямую.
Перед запуском полезно проверить конфигурацию:
sudo docker compose config -qПосле этого можно запустить проект:
sudo docker compose up -dИ проверить состояние сервисов:
sudo docker compose psСеть, порты и HTTPS
Не публикуйте каждый контейнер наружу
Одна из самых распространенных ошибок в первых Docker-проектах выглядит так:
db:
ports:
- "5432:5432"Так делать можно, но только если внешний доступ к базе действительно нужен.
Когда используется ports или -p, Docker сопоставляет контейнерный порт с адресом хоста и создает соответствующие сетевые правила.
Если PostgreSQL нужен только приложению на этом же VPS, внешний порт обычно не требуется:
app → db:5432
Здесь db — имя Compose-сервиса.
То же относится к Redis, RabbitMQ, внутренним API и другим компонентам, которые не должны напрямую принимать соединения из Интернета.
Есть еще один важный нюанс: Docker сам управляет сетевыми правилами для опубликованных портов. Поэтому доступность контейнера нельзя оценивать только по выводу ufw status. После публикации порта проверяйте его фактическую доступность извне.
Где должен находиться HTTPS
Для обычного веб-приложения удобно оставить контейнер приложения во внутренней сети, а наружу вывести reverse proxy:
Интернет
│
443
│
▼
Nginx
│
▼
app:8000
У такого подхода несколько плюсов:
- TLS завершается в одном понятном месте;
- приложению не обязательно самому управлять сертификатами;
- проще подключить несколько доменов;
- внутренний порт приложения не нужно открывать всему Интернету.
Полную настройку Nginx здесь не разбираем — это отдельная тема. Для Docker важнее понять само разделение публичной и внутренней части.
Если Nginx установлен непосредственно на VPS, порт приложения можно привязать только к loopback-интерфейсу:
ports:
- "127.0.0.1:8000:8000"Тогда сервис будет доступен локально на хосте, но не станет слушать этот порт на всех внешних интерфейсах.
Тома, конфигурация и секреты
Почему данные нельзя оставлять только внутри контейнера
Writable layer контейнера не подходит для хранения данных, которые должны пережить удаление самого контейнера. Для этого используются volumes.
Плохая схема:
Контейнер PostgreSQL
└── база внутри writable layer
Нормальная схема:
Контейнер PostgreSQL
│
▼
Docker volume
│
▼
Постоянные данные
Например:
services:
db:
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:Named volume управляется Docker и продолжает существовать отдельно от конкретного контейнера.
Volume или bind mount
Оба варианта полезны, но задачи у них разные.
| Вариант | Когда удобен | Пример |
|---|---|---|
| Named volume | Данные, которыми управляет само приложение или СУБД | PostgreSQL, MySQL, Redis с persistence |
| Bind mount | Файлы, которые администратор хочет явно видеть и редактировать на VPS | Конфигурация Nginx, статические файлы, локальные каталоги |
Пример bind mount:
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:roBind mount сильнее привязывает Compose-проект к конкретной структуре каталогов на VPS. Named volume в этом смысле лучше отделяет данные контейнера от структуры хоста.
.env — это конфигурация, но не сейф
Compose умеет подставлять значения из файла .env.
Например:
DB_PASSWORD=replace-meА в compose.yaml:
environment:
DB_PASSWORD: ${DB_PASSWORD}Это удобно, но файл .env сам по себе не шифрует секреты.
Поэтому:
- не добавляйте рабочий
.envв публичный Git; - ограничивайте права на файл;
- не помещайте пароли и токены в Dockerfile;
- не запекайте рабочие секреты внутрь image;
- для более строгих требований используйте отдельную систему управления секретами.
Как обеспечить постоянную работу и безопасно обновлять приложение
Restart policy
Контейнер, который однажды запустили через docker compose up -d, должен возвращаться в работу после обычного reboot VPS.
Для этого используется restart policy.
Например:
restart: unless-stoppedИли в некоторых сценариях:
restart: alwaysПосле настройки полезно проверить поведение реальным контролируемым reboot в техническое окно, а не считать, что все обязательно запустится автоматически.
Healthcheck
Состояние running говорит только о том, что основной процесс контейнера не завершился. Но приложение при этом может не отвечать на HTTP или не иметь доступа к базе.
Например:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 3Конкретная команда зависит от приложения. Не добавляйте фиктивный endpoint /health, если самого такого маршрута у приложения нет.
Проверить состояние можно командой:
sudo docker compose psЛоги
Для просмотра логов всего Compose-проекта:
sudo docker compose logsПоследние 100 строк:
sudo docker compose logs --tail=100Для наблюдения в реальном времени:
sudo docker compose logs -fНо для постоянной эксплуатации нужно подумать и о ротации. Если логи растут без ограничений, однажды они могут занять весь диск VPS.
Как обновлять контейнеры
Если в compose.yaml используются готовые images, базовая схема обновления выглядит так:
- Сделать актуальную резервную копию критичных данных.
- Проверить release notes новой версии.
- Скачать новые images.
- Пересоздать нужные сервисы.
- Проверить состояние и логи.
- При проблеме выполнить заранее подготовленный откат.
Скачать новые images без запуска контейнеров:
sudo docker compose pullПосле этого:
sudo docker compose up -dПроверяем состояние:
sudo docker compose psИ последние логи:
sudo docker compose logs --tail=100Будет ли обновление без простоя
Не обязательно.
Если приложение работает в одном экземпляре, пересоздание контейнера может дать короткий перерыв.
Docker Compose сам по себе не превращает один контейнер в отказоустойчивый кластер. Для обновлений без разрыва запросов нужна подходящая архитектура: несколько экземпляров приложения, балансировка, совместимые миграции базы и контролируемое переключение трафика.
Для обычного небольшого VPS честнее заранее планировать короткое техническое окно, чем обещать zero downtime там, где архитектура его не обеспечивает.
Что предусмотреть для отката
До обновления нужно знать:
- какой image работал до обновления;
- совместима ли старая версия приложения с уже измененной базой;
- есть ли свежий backup базы;
- сохранилась ли предыдущая Compose-конфигурация;
- можно ли быстро получить старый image.
Особенно внимательно относитесь к миграциям базы. Если новая версия приложения необратимо изменила структуру данных, возврата старого image может оказаться недостаточно.
Резервное копирование и финальная проверка production-ready
Как делать резервные копии Docker-приложения
Что именно резервировать
Один архив каталога проекта редко является полноценным backup.
| Что резервировать | Зачем |
|---|---|
| База данных | Основные изменяемые данные приложения |
| Volumes и пользовательские файлы | Они не входят автоматически в image |
compose.yaml и конфигурация | Нужны для воспроизведения окружения |
| Секреты | Без них восстановленный сервис может не подключиться к зависимостям |
Images обычно можно снова получить из registry. Если image существует только локально на этом VPS, это уже отдельный риск: его нужно либо сохранить, либо разместить в доступном registry.
docker export не копирует volumes
Команда docker export экспортирует файловую систему контейнера, но не содержимое подключенных volumes.
То есть такой вывод:
«Я экспортировал контейнер PostgreSQL — значит база сохранена»
может оказаться ошибочным.
Базу лучше резервировать средствами самой СУБД
Для PostgreSQL обычно используется pg_dump или другой предусмотренный механизм, для MySQL/MariaDB — mysqldump либо выбранная схема физического резервирования.
Для большой production-базы способ backup нужно выбирать с учетом размера, нагрузки и требований к времени восстановления.
Просто архивировать файлы работающей СУБД из volume без понимания ее внутреннего устройства — не универсальный безопасный способ.
Backup volume
Для обычных пользовательских файлов volume можно архивировать через временный контейнер, которому монтируется сам volume и каталог для резервной копии.
Но при работе со stateful-сервисами важно учитывать состояние данных в момент копирования. Если приложение активно пишет в volume, файловый архив может оказаться несогласованным.
Поэтому:
- для пользовательских файлов файлового backup часто достаточно;
- для баз данных лучше использовать штатные механизмы самой СУБД;
- для критичных сервисов нужно отдельно проверять консистентность восстановления.
Проверяем не backup, а восстановление
Сам факт появления файла резервной копии еще не означает, что система восстановления работает.
Проверочный сценарий выглядит примерно так:
Чистый VPS
│
▼
Docker + Compose
│
▼
compose.yaml
│
├── восстановление БД
├── восстановление volumes
└── возврат конфигурации
│
▼
Запуск приложения
│
▼
Функциональная проверка
Что проверить перед тем, как считать сервис production-ready
Контейнеры
- все нужные сервисы запускаются;
- настроена restart policy;
- после reboot приложение возвращается в рабочее состояние;
- healthcheck проходит там, где он действительно нужен.
Сеть
- наружу опубликованы только необходимые порты;
- база и внутренние сервисы не открыты в Интернет без причины;
- правила Docker firewall проверены отдельно;
- внутренние сервисы обращаются друг к другу по Docker network.
Данные
- база хранится в volume или другом постоянном хранилище;
- пользовательские файлы не зависят от writable layer контейнера;
- пересоздание контейнера не уничтожает данные.
HTTPS
- домен ведет на правильный VPS;
- сертификат действителен;
- HTTP при необходимости перенаправляется на HTTPS;
- механизм продления сертификата проверен.
Обновления
- понятна текущая версия image;
- есть порядок
pull → recreate → check; - перед критичными обновлениями создается backup;
- существует сценарий отката.
Резервное копирование
- резервируется база;
- резервируются необходимые volumes;
- сохранены Compose-файлы и конфигурация;
- backup хранится не только на том же VPS;
- восстановление хотя бы один раз проверялось.
Ресурсы
- хватает RAM;
- нет постоянного ухода в swap под обычной нагрузкой;
- остается запас диска;
- контролируется рост логов;
- база и приложение не упираются в CPU.
Если после добавления базы, reverse proxy и фоновых сервисов VPS постоянно работает на пределе, изменением пары параметров Compose проблему уже не решить. Нужно либо увеличить ресурсы, либо сначала определить, какой сервис создает основную нагрузку.
Заключение
Docker хорошо упрощает доставку приложения на Linux VPS, но сам по себе не решает задачи эксплуатации.
Для production нужно отдельно подумать о постоянных данных, сети, HTTPS, автозапуске, обновлениях и резервном копировании. Если эти части описаны в Compose и понятны администратору, пересоздание контейнера перестает быть рискованной операцией.
Хороший признак рабочей Docker-схемы — сервер можно восстановить без ручного редактирования файлов внутри контейнеров. Есть Compose-конфигурация, известные версии images, отдельные данные и проверенный backup.
А ситуация «главное сейчас не трогать этот контейнер, потому что внутри что-то настраивали руками» — уже повод привести deployment в порядок.
Что почитать дальше
Если вы готовите Linux VPS для постоянной работы приложения, пригодятся связанные материалы:
- Первичная настройка Ubuntu VPS после заказа — SSH, обновления, firewall и базовая подготовка нового сервера.
- Как выбрать VPS: полное руководство по ключевым критериям — как оценить CPU, RAM и диск под приложение и базу данных.
- Автоматическое продление SSL-сертификата — как проверить Certbot и обновление сертификатов.
- Как перенести сайт или приложение на Linux VPS без длительного простоя — если Docker-проект переезжает с уже работающего сервера.
Когда стоит обратиться к специалистам
Если приложение состоит из пары контейнеров и хорошо документировано, такую схему вполне можно развернуть самостоятельно. Главное — не оставлять постоянные данные внутри контейнеров и заранее проверить, что произойдет после reboot и обновления.
Если новый сервер еще только выбирается, можно подобрать Linux VPS IPWAY под весь Docker-стек. При расчете стоит учитывать не только основной контейнер, но и базу данных, reverse proxy, фоновые процессы и запас ресурсов под обновления.
Если инфраструктуру вы администрируете самостоятельно, следующий шаг — выбрать подходящую конфигурацию Linux VPS и развернуть на ней Compose-проект. Если есть сомнения по CPU, RAM или диску, сначала лучше уточнить конфигурацию под конкретный набор контейнеров.
Кратко
- Docker Compose удобен для описания сервисов, сетей и volumes одного приложения.
- В Интернет нужно публиковать только те порты, которым действительно нужен внешний доступ.
- Данные базы нельзя хранить только в writable layer контейнера.
- Restart policy помогает контейнерам вернуться в работу после перезапуска Docker или VPS.
- Healthcheck проверяет состояние сервиса, но не заменяет внешний мониторинг.
- Обновление одного контейнера через Compose не гарантирует работу без простоя.
docker exportне включает содержимое подключенных volumes.- Backup считается рабочим только после успешной проверки восстановления.