Высокопроизводительные и высокодоступные vps/vds с автоматической установкой и полным root-доступом к ОС. Заказанные ресурсы гарантировано закреплены за вами.
Универсальные и масштабируемые среды виртуальных серверов, разработанные с учетом потребностей вашего бизнеса и предлагающие гибкое распределение ресурсов.
Набор сетевых сервисов, обеспечивающих повышенную безопасность, масштабируемость и высокую доступность ресурсов для оптимизации вашей облачной экосистемы.
Обеспечьте непрерывность своей работы с помощью наших надежных решений по восстановлению после сбоев, которые гарантируют быстрое восстановление и минимальное время простоя в случае непредвиденных проблем.
Как развернуть приложение Docker Compose на VPS с Nginx и SSL
Как развернуть приложение Docker Compose на VPS с Nginx и SSL
Валерий Волков
Время прочтения 15 минут
В этом руководстве мы развернём небольшое веб-приложение на VPS с Ubuntu 24.04 с помощью Docker Compose. Приложение будет работать внутри контейнера, а Nginx — принимать внешние HTTP- и HTTPS-запросы и передавать их на локальный порт контейнера.
Для развёртывания потребуется VPS с публичным IP, домен или поддомен, а также открытые TCP-порты 22, 80 и 443. Для небольшого тестового приложения достаточно конфигурации с 2 vCPU, 2–4 ГБ оперативной памяти и диском от 20 ГБ.
Сначала создадим виртуальную машину и подключимся к ней по SSH. Затем установим Docker Engine и Docker Compose Plugin, подготовим файлы приложения, Dockerfile, .dockerignore, переменные окружения и конфигурацию compose.yaml.
После запуска контейнера проверим его состояние и журналы, а затем настроим Nginx как reverse proxy. В этой схеме приложение будет слушать только локальный порт VPS, а наружу будут открыты только Nginx и стандартные веб-порты.
В нашем примере Nginx + certbot будут установлены на хост без докера, для демонстрации возможностей гибридного использования (или установки на разных VPS).
Для стенда будет использоваться поддомен: docker.deploy-test-lab.com
После создания DNS-записи выпустим SSL-сертификат Let’s Encrypt и настроим автоматическое перенаправление с HTTP на HTTPS. Также разберём порядок обновления приложения: получение новой версии файлов, повторную сборку образа и перезапуск контейнеров через Docker Compose.
В результате получим работающее контейнерное приложение, доступное по доменному имени через HTTPS, с автоматическим запуском Docker и контейнеров после перезагрузки VPS.
Архитектура приложения на Docker Compose
Перед развёртыванием разберём, какие компоненты будут работать на VPS и каким образом запросы пользователей попадут в контейнер с приложением.
Какие компоненты понадобятся
В этом руководстве используются следующие компоненты:
Компонент
Назначение
Ubuntu 24.04 LTS
Операционная система виртуальной машины
Docker Engine
Запускает и изолирует контейнеры
Docker Compose
Управляет приложением через файл compose.yaml
Dockerfile
Описывает сборку образа приложения
Nginx
Принимает внешние HTTP- и HTTPS-запросы, терминирует SSL
Certbot
Выпускает и обновляет SSL-сертификат Let’s Encrypt
DNS
Связывает поддомен с публичным IP виртуальной машины
В качестве примера развернём небольшое веб-приложение на Node.js. Оно будет запускаться внутри контейнера и отвечать на локальном порту 3000.
Для тестового проекта достаточно VPS с 2 vCPU, 2–4 ГБ оперативной памяти и диском от 20 ГБ. Серверу также потребуются публичный IP и входящие TCP-порты 22, 80 и 443.
Как взаимодействуют Docker Compose, приложение и Nginx
Docker Engine создаёт изолированную среду для приложения. Внутри контейнера находятся нужная версия Node.js, зависимости и исходные файлы проекта. Благодаря этому приложение не зависит от глобально установленных на VPS библиотек.
Docker Compose читает конфигурацию из файла compose.yaml. В ней указываются параметры сборки, имя сервиса, порты, переменные окружения, сети и политика автоматического перезапуска контейнера.
Приложение будет доступно только через локальный интерфейс VPS: 127.0.0.1:3000
Такой адрес нельзя открыть напрямую из интернета. Все внешние запросы сначала принимает Nginx, после чего, как reverse proxy, передаёт их приложению.
Nginx также отвечает за подключение домена, перенаправление с HTTP на HTTPS и работу с SSL-сертификатом. В результате посетитель взаимодействует только с Nginx, а внутренняя структура Docker-приложения остаётся скрытой.
Подготовка виртуальной машины
Сначала создадим чистую виртуальную машину с Ubuntu 24.04 LTS, подключим к ней публичный IP и проверим сетевые правила. Процесс практически не отличается от подготовки VPS для обычного приложения, однако сам проект позднее будет запускаться в контейнере.
Создание VM в облачной панели
В панели управления облаком создайте новую виртуальную машину. Для тестового приложения можно использовать следующую конфигурацию:
имя VM — docker-compose-guide;
образ — Ubuntu 24.04 LTS;
2 vCPU;
4 ГБ оперативной памяти;
системный диск 20 ГБ;
приватная сеть проекта;
авторизация по SSH key pair.
При создании нового SSH key pair скачайте приватный ключ и сохраните его на своём компьютере. Он потребуется для подключения к серверу.
После подтверждения конфигурации дождитесь, пока виртуальная машина перейдёт в статус Active.
Подключение публичного IP и настройка firewall
Изначально виртуальная машина получает приватный IP внутри облачной сети. Для подключения по SSH, работы домена и открытия приложения из интернета к VM нужно привязать Floating IP.
В этом руководстве будет повторно использован публичный адрес: 203.0.113.10
В своей конфигурации необходимо использовать Floating IP, который назначен вашей виртуальной машине.
В security group разрешите входящие TCP-подключения:
Порт
Назначение
22
Подключение к VPS по SSH
80
HTTP и проверка домена Let’s Encrypt
443
Защищённые HTTPS-подключения
Порт приложения 3000 открывать для интернета не нужно. Позднее Docker опубликует его только на локальном интерфейсе 127.0.0.1, а внешние запросы будет принимать Nginx.
Подключение к VPS по SSH
На актуальных версиях Windows для подключения можно использовать PowerShell, командную строку или Windows Terminal. Перейдите в каталог с приватным SSH-ключом и выполните: ssh -i .\docker-compose-guide.pem ubuntu@<FLOATING_IP>
Вместо <FLOATING_IP> укажите публичный адрес своей виртуальной машины. В нашем случае команда будет выглядеть так: ssh -i .\docker-compose-guide.pem ubuntu@203.0.113.10
Название файла ключа также может отличаться. Вместо docker-compose-guide.pem укажите имя или полный путь к собственному приватному ключу.
При первом подключении SSH предложит подтвердить fingerprint сервера. Введите: yes
Если этот Floating IP ранее использовался другой виртуальной машиной, SSH может сообщить об изменении ключа удалённого узла. Старую запись можно удалить командой: ssh-keygen -R 203.0.113.10
После этого повторите подключение и подтвердите новый fingerprint. Удалять сохранённый ключ следует только в том случае, если изменение ожидаемо — например, после удаления старой VM и назначения того же IP новому серверу.
Обновление Ubuntu
После подключения обновите индекс пакетов и установленные компоненты:
sudo apt update
sudo apt upgrade -y
Затем установите базовые утилиты, которые понадобятся для добавления репозитория Docker и дальнейшей настройки сервера: sudo apt install -y ca-certificates curl gnupg unzip ufw
Проверьте версию операционной системы: cat /etc/os-release
В выводе должна быть указана Ubuntu 24.04 LTS. После обновления виртуальная машина готова к установке Docker Engine и Docker Compose.
Установка Docker и Docker Compose
Подключение официального репозитория Docker
Docker можно установить из стандартных репозиториев Ubuntu, но для актуальной версии Docker Engine и Compose Plugin используем официальный APT-репозиторий Docker.
Сначала создайте каталог для ключей репозиториев: sudo install -m 0755 -d /etc/apt/keyrings
Она используется без дефиса между словами docker и compose. Отдельная команда docker-compose относится к устаревшему standalone-варианту, тогда как для новых установок рекомендуется Compose Plugin.
После установки Docker обычно запускается автоматически. Проверьте его состояние: sudo systemctl status docker --no-pager
Проверка установленных версий
Проверьте версию Docker Engine: docker --version
Затем проверьте Docker Compose: docker compose version
Убедитесь, что Docker отвечает на команды: sudo docker run --rm hello-world
Команда загрузит небольшой тестовый образ, запустит контейнер и удалит его после завершения.
Настройка автозапуска Docker
В Ubuntu сервис Docker обычно включается в автозагрузку при установке. Дополнительно закрепите его автоматический запуск: sudo systemctl enable --now docker
Проверьте статусы:
systemctl is-active docker
systemctl is-enabled docker
Ожидаемый результат:
active
enabled
На Ubuntu и Debian сервис Docker при штатной установке запускается вместе с системой, однако явная проверка systemctl is-enabled позволяет убедиться в правильной настройке VPS.
По умолчанию обычный пользователь не всегда имеет доступ к Docker socket, поэтому в дальнейших командах можно использовать sudo docker. Для тестового стенда также можно добавить пользователя ubuntu в группу docker: sudo usermod -aG docker ubuntu
Чтобы членство в группе применилось, завершите SSH-сеанс и подключитесь повторно: exit
После нового входа проверьте: docker ps
Если команда выполнилась без ошибки доступа, дальнейшие операции можно запускать без sudo.
Подготовка приложения
В качестве примера создадим небольшое Node.js-приложение. Оно будет возвращать веб-страницу с информацией о том, что контейнер успешно запущен через Docker Compose.
<p>Приложение успешно запущено в Docker-контейнере.</p>
<p>Трафик передаётся через <code>Nginx</code> по защищённому соединению.</p>
</main>
</body>
</html>
`);
});
app.get('/health', (request, response) => {
response.json({
status: 'ok',
service: appName
});
});
app.listen(port, '0.0.0.0', () => {
console.log(`${appName} is listening on port ${port}`);
});
Приложение прослушивает адрес 0.0.0.0 внутри контейнера. Сам порт позднее будет опубликован только на локальном интерфейсе VPS через compose.yaml.
Маршрут /health понадобится для быстрой проверки состояния приложения: http://127.0.0.1:3000/health
Создание Dockerfile
Создайте файл Dockerfile: nano Dockerfile
Добавьте:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["npm", "start"]
В качестве базового образа используется официальный образ Node.js. Docker рекомендует использовать доверенные официальные образы и исключать из контекста сборки ненужные файлы.
Файл .dockerignore исключает ненужные данные из контекста сборки. Это уменьшает объём передаваемых Docker файлов и предотвращает случайное попадание локальных зависимостей, истории Git и переменных окружения в образ.
Настройка переменных окружения
Создайте файл .env: nano .env
Добавьте:
APP_NAME=Deploy Test Lab
APP_PORT=3000
Переменная APP_NAME будет передана внутрь контейнера, а APP_PORT понадобится в конфигурации Docker Compose.
Закройте доступ к файлу для других пользователей VPS: chmod 600 .env
Не добавляйте .env в Docker-образ и публичный репозиторий. Для рабочего проекта в этом файле могут храниться пароли, токены и другие параметры окружения.
Проверьте структуру каталога: ls -la
На этом этапе в нём должны находиться:
.dockerignore
.env
Dockerfile
app.js
package.json
Файлы приложения подготовлены. Следующим этапом станет создание compose.yaml, настройка локального порта и запуск контейнера.
Создание конфигурации Docker Compose
Описание сервисов в compose.yaml
В корне проекта создайте файл compose.yaml:
cd /opt/docker-compose-app
nano compose.yaml
Добавьте следующую конфигурацию:
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: docker-compose-guide
restart: unless-stopped
env_file:
- .env
environment:
PORT: 3000
ports:
- "127.0.0.1:${APP_PORT}:3000"
networks:
- app-network
networks:
app-network:
driver: bridge
Верхнеуровневый раздел services содержит описание контейнеров приложения. В нашем случае используется один сервис app, который собирается из локального Dockerfile. Docker Compose поддерживает описание сервисов, сетей, портов и других параметров в одном конфигурационном файле.
Параметр:
build:
context: .
dockerfile: Dockerfile
указывает Docker использовать текущий каталог как контекст сборки и найти в нём файл Dockerfile.
Файл .env подключается через:
env_file:
- .env
Переменная APP_NAME из него попадёт внутрь контейнера. Дополнительная переменная PORT задаёт порт, который слушает приложение внутри контейнера.
127.0.0.1 ограничивает доступ локальным интерфейсом VPS;
${APP_PORT} подставляется из файла .env;
3000 — порт приложения внутри контейнера.
В результате приложение будет доступно на сервере по адресу: http://127.0.0.1:3000
но подключиться к этому порту напрямую из интернета будет нельзя. Позднее Nginx станет передавать на него внешние запросы.
Для сервиса также задана политика перезапуска: restart: unless-stopped
Она перезапускает контейнер после сбоя и запуска Docker, но не поднимает его снова, если контейнер был остановлен вручную. Docker Compose поддерживает политики no, always, on-failure и unless-stopped.
Отдельная bridge-сеть:
networks:
- app-network
Cоздаёт изолированную сеть проекта. Сейчас в ней работает один контейнер, но позднее в конфигурацию можно добавить базу данных, Redis или другой внутренний сервис.
Команда обрабатывает compose.yaml, подставляет переменные из .env и выводит нормализованную конфигурацию. Если в YAML есть ошибка в отступах, неизвестный параметр или отсутствует обязательное значение, Docker Compose сообщит об этом.
Для более короткой проверки без вывода всей конфигурации можно выполнить: docker compose config --quiet
Если ошибок нет, команда завершится без сообщений. Затем выведите итоговую конфигурацию сервиса: docker compose config
Запуск приложения через Docker Compose
Сборка и запуск контейнеров
Находясь в каталоге проекта, запустите сборку образа и контейнер в фоновом режиме: docker compose up -d --build
Параметр --build принудительно запускает сборку образа перед стартом, а -d оставляет контейнер работать в фоновом режиме. Команда docker compose up создаёт и запускает сервисы, а при изменении конфигурации или образа пересоздаёт связанные контейнеры.
Во время первой сборки Docker:
Загрузит базовый образ node:22-alpine;
Скопирует package.json;
Установит зависимости;
Добавит файлы приложения;
Создаст контейнер;
Подключит его к сети app-network.
После завершения должен появиться статус запуска сервиса без ошибок.
Проверка состояния контейнеров
Посмотрите состояние проекта: docker compose ps
Ожидаемый вывод будет похож на следующий:
NAME
IMAGE
COMMAND
SERVICE
STATUS
PORTS
docker-compose-guide
docker-compose-app-app
"npm start"
app
Up 10 seconds
127.0.0.1:3000->3000/tcp
Важно, чтобы:
Контейнер имел статус Up;
Сервис назывался app;
Порт был опубликован как 127.0.0.1:3000->3000/tcp.
При необходимости можно вывести список всех работающих контейнеров: docker ps
Команда docker compose ps показывает контейнеры, относящиеся к текущему Compose-проекту.
Просмотр журналов приложения
Посмотрите последние строки журналов: docker compose logs --tail=50 app
В выводе должно появиться сообщение: Deploy Test Lab is listening on port 3000
Для просмотра журналов в реальном времени используйте: docker compose logs -f app
Параметр -f продолжает выводить новые строки по мере их появления. Чтобы завершить просмотр и не останавливать контейнер, нажмите Ctrl+C.
Если контейнер постоянно перезапускается или получает статус Exited, журналы обычно показывают причину: ошибку JavaScript, отсутствие зависимости, неправильную переменную окружения или занятой порт.
Проверка приложения по локальному порту
Проверьте главную страницу непосредственно на VPS: curl -I http://127.0.0.1:3000
Ожидаемый ответ: HTTP/1.1 200 OK
Затем проверьте маршрут состояния: curl http://127.0.0.1:3000/health
Приложение должно вернуть JSON: {"status":"ok","service":"Deploy Test Lab"}
Дополнительно проверьте, что порт слушает только локальный интерфейс: sudo ss -lntp | grep :3000
В выводе должен отображаться адрес 127.0.0.1:3000 а не 0.0.0.0:3000.
Это подтверждает, что контейнер не доступен напрямую из интернета. Следующим этапом настроим Nginx как reverse proxy и направим внешний трафик на локальный порт приложения.
Включите автоматический запуск и сразу запустите сервис: sudo systemctl enable --now nginx
Проверьте его состояние:
systemctl is-active nginx
systemctl is-enabled nginx
Ожидаемый результат:
active
enabled
Nginx будет работать на хостовой системе, принимать внешние запросы и передавать их приложению, опубликованному контейнером на локальном адресе 127.0.0.1:3000. Nginx официально поддерживает работу в качестве HTTP reverse proxy.
Создание server block
Создайте отдельный конфигурационный файл: sudo nano /etc/nginx/sites-available/docker-compose-app
Основную передачу запросов выполняет директива: proxy_pass http://127.0.0.1:3000;
Она направляет запросы из блока location / на локальный порт приложения. Поскольку Docker Compose опубликовал порт только на 127.0.0.1, контейнер недоступен напрямую из интернета. Подключение к нему выполняет только Nginx. Поведение proxy_pass и передача запросов проксируемому серверу описаны в официальной документации модуля Nginx.
Заголовок Host сохраняет доменное имя, по которому обратился пользователь: proxy_set_header Host $host;
Остальные заголовки передают приложению информацию об исходном подключении:
Это позволяет приложению получить IP пользователя и понять, использовался ли HTTP или HTTPS.
Перед проверкой Nginx убедитесь, что контейнер отвечает локально: curl http://127.0.0.1:3000/health
Ожидаемый ответ: {"status":"ok","service":"Deploy Test Lab"}.
Проверка конфигурации Nginx
Проверьте синтаксис: sudo nginx -t
При корректной конфигурации появится результат:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Примените изменения без остановки веб-сервера: sudo systemctl reload nginx
Затем проверьте server block локально:
curl -I \
-H "Host: docker.deploy-test-lab.com" \
http://127.0.0.1
В ответе должен появиться код: HTTP/1.1 200 OK
Также можно проверить маршрут состояния через Nginx:
curl \
-H "Host: docker.deploy-test-lab.com" \
http://127.0.0.1/health
Если Nginx возвращает JSON приложения, reverse proxy работает корректно.
Подключение домена и SSL
Создание DNS-записи
В DNS-зоне домена создайте запись типа A со следующими параметрами:
Параметр
Значение
Тип
A
Имя
docker
IPv4-адрес
203.0.113.10
TTL
Auto или значение по умолчанию
Адрес 203.0.113.10 используется в нашем тестовом стенде. В своей конфигурации укажите Floating IP, назначенный вашей виртуальной машине.
Если DNS обслуживается через Cloudflare, для первоначальной настройки можно использовать режим DNS only, чтобы домен напрямую указывал на VPS.
После сохранения проверьте DNS с локального компьютера: nslookup docker.deploy-test-lab.com
В ответе должен отображаться IP VPS: 203.0.113.10
Также проверьте приложение по HTTP: http://docker.deploy-test-lab.com
До выпуска сертификата оно должно открываться без HTTPS.
Можно выполнить проверку через терминал: curl -I http://docker.deploy-test-lab.com
Не переходите к Certbot, пока домен не начнёт возвращать правильный IP и приложение не станет доступно по HTTP.
Выпуск сертификата Let’s Encrypt
Установите Certbot через Snap: sudo snap install --classic certbot
Создайте ссылку на исполняемый файл: sudo ln -s /snap/bin/certbot /usr/local/bin/certbot
Если ссылка уже существует, повторно создавать её не требуется.
Запросите сертификат для поддомена:
sudo certbot --nginx \
-d docker.deploy-test-lab.com
Во время запуска Certbot предложит:
Указать email;
Принять условия использования;
Выбрать участие в рассылке;
Подтвердить изменение конфигурации Nginx.
Плагин Nginx может автоматически получить сертификат, добавить HTTPS-настройки и применить их к существующему HTTP-сайту.
Для проверки домена через HTTP-01 сервер должен быть доступен извне по порту 80. Let’s Encrypt рекомендует не закрывать его и перенаправлять обычные HTTP-запросы на HTTPS.
После успешного выпуска сертификата повторно проверьте Nginx: sudo nginx -t
Затем выполните тест автоматического обновления: sudo certbot renew --dry-run
Если проверка завершается успешно, сертификат сможет продлеваться автоматически.
Настройка перенаправления с HTTP на HTTPS
После работы Certbot в конфигурации Nginx появится отдельный блок для HTTPS и правило перенаправления с порта 80.
Это означает, что незашифрованные запросы автоматически направляются на защищённую версию сайта.
Не удаляйте входящее правило для TCP-порта 80. Оно понадобится для HTTP-переадресации и может использоваться при последующих проверках владения доменом.
Проверка защищённого соединения
Откройте приложение в браузере: https://docker.deploy-test-lab.com
Должна загрузиться страница тестового приложения с сообщением:
Приложение успешно запущено в Docker-контейнере.
Проверьте HTTPS через терминал: curl -I https://docker.deploy-test-lab.com
Сервер должен вернуть успешный ответ: HTTP/2 200
Проверьте маршрут состояния: curl https://docker.deploy-test-lab.com/health
Ожидаемый результат: {"status":"ok","service":"Deploy Test Lab"}
На этом этапе внешний трафик проходит по HTTPS через Nginx, а само приложение продолжает работать внутри Docker-контейнера на локальном порту VPS.
Обновление приложения
Получение новой версии файлов
Способ обновления зависит от того, как исходный код попадает на VPS. Если проект хранится в Git-репозитории, перейдите в каталог приложения и получите последнюю версию:
cd /opt/docker-compose-app
git pull
Перед обновлением убедитесь, что в каталоге нет несохранённых локальных изменений: git status
Если проект передаётся архивом или отдельными файлами, сначала замените исходный код, package.json, Dockerfile и другие изменившиеся элементы, но не удаляйте .env и постоянные данные приложения.
После обновления проверьте конфигурацию Compose: docker compose config --quiet
Отсутствие вывода означает, что файл compose.yaml успешно обработан.
Повторная сборка контейнеров
Если изменился исходный код, зависимости или Dockerfile, пересоберите образ и запустите обновлённый контейнер: docker compose up -d --build
Compose сравнит текущую конфигурацию с уже запущенным проектом и при необходимости пересоздаст контейнер. Для нашего приложения этого достаточно: образ собирается из локального каталога, а сервис запускается в фоновом режиме.
После завершения проверьте состояние: docker compose ps
И последние строки журнала: docker compose logs --tail=50 app
Если в compose.yaml используются готовые образы из реестра, перед запуском новой версии можно выполнить:
docker compose pull
docker compose up -d
Команда docker compose pull загружает актуальные образы, указанные для сервисов.
Перезапуск без удаления постоянных данных
Для обычного обновления не требуется выполнять: docker compose down -v
Параметр -v удаляет именованные и анонимные тома проекта, поэтому вместе с ними могут исчезнуть базы данных, загруженные файлы и другие постоянные данные.
Безопасный вариант обновления: docker compose up -d --build
Если нужно только перезапустить уже созданный контейнер без пересборки: docker compose restart app
Однако docker compose restart не применяет изменения, внесённые в compose.yaml. После изменения конфигурации следует использовать docker compose up -d, чтобы Compose пересоздал сервис с новыми параметрами.
В текущем демонстрационном приложении тома не используются, но этот принцип особенно важен для проектов с PostgreSQL, MySQL, Redis или если сервис использует пользовательские загрузки.
Очистка неиспользуемых образов
После нескольких обновлений на VPS могут остаться старые слои и образы. Посмотреть их можно командой: docker image ls
Docker запросит подтверждение. Для выполнения без дополнительного вопроса используется: docker image prune -f
Команда удаляет неиспользуемые dangling images, но не затрагивает образы, необходимые запущенным контейнерам.
Более агрессивная очистка: docker image prune -a
удаляет все образы, которые не связаны с существующими контейнерами. На рабочем сервере её следует применять осторожно, поскольку при следующем запуске часть образов придётся загружать или собирать заново.
Проверка после развёртывания
Проверка контейнеров и сервиса Docker
Перейдите в каталог проекта: cd /opt/docker-compose-app
Проверьте состояние Docker:
systemctl is-active docker
systemctl is-enabled docker
Ожидаемый результат:
active
enabled
Затем проверьте контейнер: docker compose ps
Сервис app должен иметь статус Up, а порт — быть опубликован только на локальном интерфейсе: 127.0.0.1:3000->3000/tcp
Дополнительно можно проверить ресурсы контейнера: docker stats --no-stream
Команда покажет использование процессора, оперативной памяти и сети на момент проверки.
Проверка Nginx и HTTPS
Проверьте состояние Nginx:
systemctl is-active nginx
systemctl is-enabled nginx
Затем проверьте конфигурацию: sudo nginx -t
Убедитесь, что HTTP перенаправляется на HTTPS: curl -I http://docker.deploy-test-lab.com
В ответе должен присутствовать заголовок: Location: https://docker.deploy-test-lab.com/
Маршрут состояния приложения также должен быть доступен через Nginx: curl https://docker.deploy-test-lab.com/health
Ожидаемый ответ: {"status":"ok","service":"Deploy Test Lab"}.
Проверка журналов приложения
Посмотрите последние сообщения контейнера: docker compose logs --tail=50 app
В журнале не должно быть циклических перезапусков, необработанных исключений или сообщений о занятом порте.
Для просмотра новых событий в реальном времени используйте: docker compose logs -f app
Остановить просмотр можно сочетанием Ctrl+C. Контейнер при этом продолжит работать. Docker Compose предоставляет отдельные команды для просмотра журналов и состояния сервисов проекта.
При проблемах с reverse proxy дополнительно проверьте журналы Nginx: sudo tail -n 50 /var/log/nginx/docker-compose-app_error.log
Проверка автозапуска после reboot
Убедитесь, что сервисы включены в автозагрузку:
systemctl is-enabled docker
systemctl is-enabled nginx
Для контейнера в compose.yaml должна использоваться политика: restart: unless-stopped
Она позволяет Docker автоматически запускать контейнер после перезапуска демона или VPS, если контейнер ранее не был остановлен вручную.
Перезагрузите сервер: sudo reboot
SSH-соединение будет закрыто. Через минуту подключитесь повторно: ssh -i .\docker-compose-guide.pem ubuntu@203.0.113.10
После входа выполните:
cd /opt/docker-compose-app
printf "docker: "
systemctl is-active docker
printf "nginx: "
systemctl is-active nginx
docker compose ps
В выводе должны отображаться статусы active, а контейнер приложения должен снова иметь состояние Up.
После этого повторно проверьте маршрут состояния: curl https://docker.deploy-test-lab.com/health
Если сервер возвращает JSON со статусом ok, Docker, контейнер, Nginx и HTTPS успешно восстановили работу после reboot.
Итоговая проверка приложения
Откройте в браузере: https://docker.deploy-test-lab.com
На странице должны отображаться название Deploy Test Lab и сообщение о том, что приложение запущено в Docker-контейнере, а трафик передаётся через Nginx.
На этом развёртывание можно считать завершённым. Контейнер запускается через Docker Compose, внутренний порт недоступен напрямую из интернета, внешний трафик обслуживает Nginx, а соединение защищено сертификатом Let’s Encrypt.
Заключение
В результате на VPS с Ubuntu 24.04 LTS развёрнуто веб-приложение, упакованное в Docker-контейнер и управляемое через Docker Compose. Конфигурация проекта хранится в compose.yaml, поэтому приложение можно запускать, останавливать, пересобирать и обновлять стандартными командами Compose.
Контейнер принимает запросы только через локальный адрес 127.0.0.1:3000 и не доступен напрямую из интернета. Внешний трафик обслуживает Nginx, который работает как reverse proxy, принимает запросы по доменному имени и передаёт их приложению. HTTPS обеспечивается сертификатом Let’s Encrypt, а HTTP-запросы автоматически перенаправляются на защищённую версию сайта.
Политика restart: unless-stopped и автозапуск Docker позволяют восстановить работу контейнера после перезагрузки VPS. Для выпуска новой версии достаточно обновить файлы проекта и повторно выполнить docker compose up -d --build.
Такая схема подходит для небольших веб-приложений, API, административных панелей и внутренних сервисов. При усложнении проекта в compose.yaml можно добавить базу данных, Redis, очереди задач и другие контейнеры, сохранив единый способ управления всем стеком.
FAQ
Чем Docker Compose отличается от обычного запуска Docker-контейнера?
При обычном запуске параметры контейнера передаются длинной командой docker run. Docker Compose хранит сервисы, порты, сети, переменные окружения и политики перезапуска в файле compose.yaml.
Благодаря этому конфигурацию проще повторно использовать, обновлять и переносить на другой VPS.
Можно ли разместить несколько контейнеров в одном compose.yaml?
Да. В разделе services можно описать приложение, базу данных, Redis, очередь задач и другие компоненты.
Сервисы внутри одной Compose-сети могут обращаться друг к другу по именам, указанным в конфигурации.
Нужно ли устанавливать Nginx в отдельный контейнер?
Нет. В этом руководстве Nginx установлен непосредственно на VPS. Такой вариант упрощает выпуск сертификатов Certbot и позволяет использовать один reverse proxy для нескольких контейнерных приложений.
При необходимости и для переносимости Nginx тоже можно запустить в Docker Compose, но тогда конфигурация сертификатов, портов и томов станет сложнее.
Как обновить приложение после изменения кода?
Перейдите в каталог проекта и выполните: docker compose up -d --build
Docker пересоберёт образ и пересоздаст контейнер с новой версией приложения. После обновления следует проверить docker compose ps и журналы сервиса.
Удаляет ли docker compose down данные приложения?
Команда docker compose down удаляет контейнеры и сеть проекта, но не удаляет именованные тома без дополнительного параметра.
Команда: docker compose down -v
удаляет и тома. Для проекта с базой данных или пользовательскими файлами это может привести к потере постоянных данных.
Что произойдёт после перезагрузки VPS?
Сервис Docker и Nginx запустятся через systemd. Контейнер приложения также будет поднят автоматически благодаря политике: restart: unless-stopped
Исключением будет контейнер, который был остановлен вручную до перезагрузки.
Подходит ли Docker Compose для production?
Docker Compose можно использовать для развёртывания приложений на одном сервере, в том числе в production. Для рабочего проекта дополнительно следует настроить резервное копирование, мониторинг, ограничения ресурсов, безопасное хранение секретов и регулярное обновление образов.
Отказоустойчивое веб-приложение можно построить из двух одинаковых application-серверов, размещенных в одной приватной сети, и Load Balancer, который принимает внешние...
Terraform позволяет описывать облачную инфраструктуру OpenStack в виде кода и управлять ее жизненным циклом через единый набор команд. Вместо ручного создания ресурсов в панели можно...
Uptime Kuma — это self-hosted система мониторинга, которую можно развернуть на собственном VPS и использовать для проверки сайтов, API и сетевых сервисов. Она поддерживает HTTP(S), TCP,...
Используем cookies
Мы используем файлы cookie чтобы вам было комфортнее работать на нашем сайте. Нажимая «Далее», вы соглашаетесь на использование файлов cookie в соответствии с Политикой конфиденциальности и Соглашением о Cookies
Functional Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.