Полное руководство: деплой веб-приложения с Docker, Nginx и SSL на продакшен-сервер
Пошаговая инструкция по развёртыванию приложения на VPS. Каждый шаг содержит команду, объяснение и ожидаемый результат. Подходит как для Next.js, NestJS, так и для любого другого стека.
Содержание
- Требования к серверу
- Архитектура проекта
- Подготовка сервера
- Установка Docker и Nginx
- Docker Compose: конфигурация сервисов
- Настройка Nginx как reverse proxy
- SSL-сертификаты Let's Encrypt
- PostgreSQL, Redis и MinIO в Docker
- Переменные окружения и секреты
- Настройка файрвола
- Мониторинг (Prometheus + Grafana)
- Автоматические бэкапы
- CI/CD через GitHub Actions
- Устранение неполадок
- Чеклист продакшена
1. Требования к серверу
Перед началом убедитесь, что у вас есть VPS с достаточными ресурсами.
| Параметр | Минимум | Рекомендуется |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 4 GB | 8 GB |
| Диск | 40 GB SSD | 80+ GB SSD |
| ОС | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
| Сеть | 100 Mbps | 1 Gbps |
Где арендовать сервер:
- Hetzner — Европа, от $4/мес
- DigitalOcean — от $6/мес
- Timeweb Cloud — от ~2000 тг/мес
- PS.kz — Казахстан, локальные серверы
Также вам понадобится:
- Домен (например
your-domain.com) с доступом к DNS-настройкам - SSH-клиент (встроен в macOS/Linux, на Windows — PuTTY или Windows Terminal)
2. Архитектура проекта
Прежде чем начинать деплой, важно понимать как компоненты взаимодействуют:
Пользователь (браузер)
|
v
+-------------------+
| Nginx (:80/443) | <-- Принимает все запросы, раздаёт по адресам
+----+----------+---+
| |
v v
+----------+ +----------+
| Web | | API |
| (Next.js)| | (NestJS) | <-- Приложения в Docker-контейнерах
| :3000 | | :3001 |
+----------+ +--+-+-+---+
| | |
+---------+ | +--------+
v v v
+----------+ +----------+ +---------+
|PostgreSQL| | Redis | | MinIO |
| (БД) | | (кэш) | | (файлы) |
| :5432 | | :6379 | | :9000 |
+----------+ +----------+ +---------+
Роль каждого компонента:
- Nginx — принимает запросы из интернета, терминирует SSL и проксирует к контейнерам
- Web — фронтенд (SSR/SSG), то что видит пользователь
- API — серверная логика (авторизация, данные, файлы)
- PostgreSQL — основная база данных
- Redis — кэширование и очереди
- MinIO — S3-совместимое хранилище файлов
3. Подготовка сервера
3.1. Подключение к серверу
3.2. Обновление системы
Скачивает и устанавливает все обновления безопасности. Занимает 1-3 минуты.
3.3. Установка базовых утилит
Что мы установили:
curl,wget— скачивание файловgit— управление кодомhtop— мониторинг нагрузкиufw— файрволfail2ban— защита от перебора паролей
3.4. Создание deploy-пользователя
Работать под
rootопасно — одна ошибка может сломать весь сервер. Создаём отдельного пользователя.
3.5. Настройка SSH-ключа
3.6. Защита SSH
Откройте конфигурацию SSH:
Измените следующие строки:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
Перезапустите SSH:
Важно: перед закрытием текущей сессии откройте новый терминал и проверьте, что можете подключиться:
ssh deploy@YOUR_SERVER_IP
3.7. Переключение на deploy-пользователя
С этого момента все команды выполняем от имени deploy:
4. Установка Docker и Nginx
4.1. Docker
Проверка:
Ожидаемый результат:
Docker version 27.x.x, build ...
Docker Compose version v2.x.x
4.2. Nginx
Проверка:
Должна быть строка Active: active (running).
4.3. Certbot (для SSL)
5. Docker Compose: конфигурация сервисов
Создайте файл docker-compose.prod.yml в корне вашего проекта. Это главный файл, описывающий все сервисы.
Обратите внимание: все порты инфраструктуры привязаны к
127.0.0.1— они не доступны из интернета напрямую, только через Nginx.
6. Настройка Nginx как reverse proxy
Nginx принимает запросы из интернета и направляет их к Docker-контейнерам. Без Nginx сайт не будет доступен по домену.
6.1. Конфигурация для основного сайта
Важно: создаём конфиги только с HTTP (порт 80). SSL-блоки добавит certbot автоматически.
6.2. Конфигурация для API
6.3. Активация конфигов
6.4. Проверка и перезагрузка
Ожидаемый результат:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
7. SSL-сертификаты Let's Encrypt
SSL шифрует соединение между пользователем и сервером. Без него браузер показывает предупреждение «Небезопасный сайт». Let's Encrypt выдаёт бесплатные сертификаты.
7.1. Настройка DNS
Перед получением сертификатов убедитесь, что DNS-записи настроены:
| Тип | Имя | Значение | TTL |
|---|---|---|---|
| A | @ | YOUR_SERVER_IP | 3600 |
| A | www | YOUR_SERVER_IP | 3600 |
| A | api | YOUR_SERVER_IP | 3600 |
Проверка:
7.2. Получение сертификатов
Certbot спросит email и предложит перенаправление HTTP на HTTPS — выберите «Redirect».
7.3. Проверка автопродления
Ожидаемый результат: Congratulations, all simulated renewals succeeded
Сертификаты автоматически продляются каждые 60 дней. Ничего дополнительно делать не нужно.
8. PostgreSQL, Redis и MinIO в Docker
8.1. Запуск инфраструктуры
Сначала запускаем базовые сервисы, от которых зависит приложение:
Подождите 15 секунд для инициализации БД:
8.2. Проверка
Все 3 сервиса должны быть в статусе Up (healthy).
8.3. Настройка MinIO
Установите MinIO Client:
Подключитесь и создайте бакет:
8.4. Сборка и запуск приложения
8.5. Инициализация базы данных
Ожидаемый результат: Your database is now in sync with your Prisma schema.
9. Переменные окружения и секреты
9.1. Генерация секретов
Каждый пароль и секрет должен быть уникальным. Никогда не используйте пароли из примеров!
9.2. Файл .env
Создайте файл .env рядом с docker-compose.prod.yml:
Безопасность: никогда не коммитьте файл
.envв Git. Добавьте его в.gitignore.
10. Настройка файрвола
Файрвол блокирует все порты кроме необходимых. Без него любой может подключиться к базе данных напрямую!
Проверка:
Ожидаемый результат:
Status: active
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
Порты 3000, 3001, 5432, 6379, 9000 НЕ открыты наружу — они доступны только через Nginx.
Настройка fail2ban
После 3 неудачных попыток входа IP-адрес блокируется на 1 час.
11. Мониторинг (Prometheus + Grafana)
Мониторинг опционален, но настоятельно рекомендуется для продакшена.
11.1. Добавление сервисов мониторинга в Docker Compose
Добавьте следующие сервисы в ваш docker-compose.prod.yml:
11.2. Запуск мониторинга
11.3. Что мониторить
| Метрика | Описание | Порог алерта |
|---|---|---|
| CPU сервера | Нагрузка на процессор | > 80% |
| RAM | Использование памяти | > 85% |
| Диск | Заполненность диска | > 90% |
| PostgreSQL | Активные подключения | > 80 |
| Redis | Использование памяти | > 200 MB |
| API | Время ответа, ошибки 5xx | > 2s / > 1% |
12. Автоматические бэкапы
Правило: если вы не проверяли восстановление — бэкапа не существует.
12.1. Скрипт бэкапа
12.2. Автоматический запуск (cron)
Добавьте строку:
0 3 * * * /home/deploy/backup.sh >> /home/deploy/backups/backup.log 2>&1
Бэкап будет выполняться каждый день в 3:00.
12.3. Восстановление из бэкапа
13. CI/CD через GitHub Actions
13.1. Подготовка SSH-ключа
На сервере:
Скопируйте приватный ключ.
13.2. Секреты в GitHub
Перейдите: Settings > Secrets and variables > Actions > New repository secret
| Имя секрета | Значение |
|---|---|
DEPLOY_HOST | IP-адрес сервера |
DEPLOY_USER | deploy |
DEPLOY_SSH_KEY | Приватный ключ |
13.3. GitHub Actions Workflow
Создайте файл .github/workflows/deploy.yml:
Теперь при пуше в
mainприложение автоматически обновится на сервере.
14. Устранение неполадок
502 Bad Gateway
Nginx работает, но приложение не запущено или упало.
Connection Refused
Nginx не работает.
Нехватка RAM при сборке
Docker-сборка требует ~2 GB RAM. Добавьте swap:
Контейнер постоянно перезапускается
Типичные причины:
- Неправильные переменные в
.env - БД недоступна
- Порт уже занят
Нехватка дискового пространства
Полезные команды для диагностики
15. Чеклист продакшена
Безопасность
- SSH: отключён root-логин, только ключи
- UFW: открыты только порты 22, 80, 443
- fail2ban настроен и запущен
- SSL-сертификаты установлены и автопродляются
- JWT-секреты уникальны
- Пароль БД сгенерирован
- Пароль MinIO сгенерирован
-
.envфайлы не в Git
Инфраструктура
- PostgreSQL работает (healthcheck зелёный)
- Redis работает (healthcheck зелёный)
- MinIO работает, бакет создан
- Nginx настроен как reverse proxy
- Порты инфраструктуры привязаны к 127.0.0.1
Приложение
- Сайт открывается по HTTPS
- API отвечает на health-эндпоинт
- Авторизация работает
- Загрузка файлов работает
- CORS настроен на правильный домен
Обслуживание
- Cron-бэкап настроен (ежедневно)
- Тестовое восстановление проведено
- CI/CD workflow работает
- Мониторинг настроен (опционально)
- Алерты на критические метрики
Заключение
Вы развернули полноценное продакшен-окружение:
- Сервер — защищённый, с отдельным deploy-пользователем
- Docker Compose — все сервисы изолированы и воспроизводимы
- Nginx — reverse proxy с кэшированием статики
- SSL — бесплатные сертификаты Let's Encrypt с автопродлением
- Файрвол — только необходимые порты открыты
- Бэкапы — автоматические, ежедневные, с ротацией
- CI/CD — автоматический деплой при пуше в main
- Мониторинг — метрики и алерты через Grafana
Эта архитектура выдерживает до 10 000 RPS при правильной настройке кэширования и горизонтальном масштабировании контейнеров. Удачного деплоя!
