Дисклеймер: Это базовые практики, которые помогут задеплоить проект на VPS и обеспечить его работоспособность. Для поддержки продакшн-версий и аудита безопасности инфраструктуры рекомендуется обращаться к профессиональным DevOps-инженерам.
Версии ПО, описанные в статье, актуальные на февраль 2026 года
- OS Server: Ubuntu 24.04 LTS (Noble Numbat)
- Docker: Engine 27+, Compose V2 (команда
docker compose) - Node.js: 22 LTS (Active LTS на 2026)
- GitHub CLI (gh): v2.50+
- GitHub Actions:
actions/checkout@v4,appleboy/ssh-action@v1.2.0
Термины
- Git — система контроля версий. Позволяет сохранять историю изменений кода и работать над проектом в команде.
- VPS (Virtual Private Server) — арендованный виртуальный сервер, "удаленный компьютер", который работает круглосуточно.
- SSH (Secure Shell) — протокол для безопасного управления сервером через командную строку.
- Docker — технология упаковки приложения в изолированный контейнер со всем необходимым для запуска. Гарантирует, что "работает у меня — заработает и там".
- Docker Compose — инструмент для запуска нескольких связанных контейнеров (например, приложение + база данных) одной командой.
- CI/CD — автоматическая доставка кода: после
git pushсистема сама тестирует и обновляет приложение на сервере. - Local / Dev / Stage / Prod — окружения. Local — компьютер разработчика, Dev — общий сервер для разработки, Stage — тестовый сервер (копия боевого), Prod — боевой сервер с реальными пользователями.
- Nginx — быстрый веб-сервер. В данном гайде используется как Reverse Proxy — принимает запросы из интернета и передает их приложению.
- SSL (Secure Sockets Layer) — технология безопасности, создающая зашифрованное соединение между сервером и браузером (HTTPS). Защищает данные пользователей.
Warp: LLM как DevOps помощник
Warp — это современный терминал, который позволяет доверить работу с командной строкой LLM. Вот что это дает:
- AI Agent Mode: Можно писать на английском или русском что нужно сделать ("SSH to prod и проверь статус докера"), а Warp генерирует команды и показывает diff перед выполнением
- Seamless SSH: После подключения по SSH на удаленный сервер, можно дать возможность LLM выполнить на нём команды
- Command blocks: Весь вывод организован в блоки, которые легко искать и скроллить (удобно при дебаге)
- Notebooks: Комбинирование Markdown-документации с рабочими SSH-командами
Как это работает в вайб-кодинге: Вместо того чтобы помнить все флаги Docker и Nginx, проблема описывается LLM в Warp: "Деплой не прошел, логи говорят permission denied". Warp генерирует команды для диагностики, пользователь смотрит diff и кликает "run".
Установка: brew install warp на macOS, скачать для Linux/Windows.
SSH ключи
Это нужно сделать один раз. После этого будет работать везде.
Генерация SSH ключа
ssh-keygen -t ed25519 -C "your_email@example.com"
# Где your_email@example.com — email, привязанный к GitHub
Система спросит:
- Путь для сохранения → Просто Enter (по умолчанию
~/.ssh/id_ed25519) - Passphrase → Нужно ввести пароль или оставить пусто (рекомендуется пароль)
Создалось два файла:
~/.ssh/id_ed25519— приватный ключ (хранить в надежном месте)~/.ssh/id_ed25519.pub— публичный ключ (тот, что добавляется в GitHub)
Добавление ключа в SSH agent
# Стартуем SSH agent
eval "$(ssh-agent -s)"
# Добавляем ключ
ssh-add ~/.ssh/id_ed25519
# Если задавал пароль, нужно ввести его сейчас
Копирование публичного ключа
# Выведет весь ключ в консоль
cat ~/.ssh/id_ed25519.pub
# На macOS удобнее скопировать в буфер сразу:
cat ~/.ssh/id_ed25519.pub | pbcopy
Должен выглядеть так: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... your_email@example.com
Добавление ключа на GitHub
Веб:
- Зайти на github.com/settings/ssh
- Нажать "New SSH key"
- Title — например "My Laptop" (для удобства, если несколько ключей)
- Key — вставить скопированный ключ
- Key type — выбрать "Authentication"
- "Add SSH key"
Или через CLI:
gh ssh-key add ~/.ssh/id_ed25519.pub --type authentication
🤖 Warp Way: Генерация ключей через Warp:
"Сгенерируй новый ed25519 ssh ключ для my@email.com, добавь в агент и скопируй публичный ключ в буфер обмена" "Добавь ssh ключ в github используя gh cli"
Проверка подключения
ssh -T git@github.com
Если виден вывод типа Hi username! You've successfully authenticated... — готово! ✅
После этого:
- Клонирование репо через SSH:
git clone git@github.com:username/repo.git - Пуш без пароля:
git push - Всё работает везде
GitHub
Создание репозитория с gh CLI
Если GitHub.com уже открыт и работа идет через интерфейс — это нормально, но использование CLI часто экономит время.
Установка gh (GitHub CLI, ориентируемся на v2.50+):
# macOS
brew install gh
# Linux (Debian/Ubuntu)
sudo apt install gh
# или скачи отсюда: https://cli.github.com
🤖 Warp Way:
"Установи github cli"
Единоразовая аутентификация:
gh auth login
# Выбрать:
# - GitHub.com
# - SSH (безопаснее, мы это уже настроили)
# - Yes to add SSH key to agent
Создание репо прямо из терминала:
# Самый быстрый способ:
gh repo create my-project --public --source=. --push
# Где:
# --public — делает репо открытым (или --private)
# --source=. — берет текущую папку как исходник
# --push — сразу пушит в GitHub
# Для выбора через интерактивное меню:
gh repo create
Готово. Репо создано, git инициализирован, remote добавлен. За 10 секунд вместо 2 минут в веб-интерфейсе.
Локальная инициализация: git init, git add
Старт нового проекта:
mkdir my-awesome-app
cd my-awesome-app
# Инициализация Git
git init
# Создание файлов (README, код, etc.)
echo "# My Awesome App" > README.md
# Добавление в staging area
git add README.md
# Или добавление всего сразу
git add .
# Коммит
git commit -m "Initial commit"
Важно: Перед git push необходимо убедиться, что репо создан на GitHub (через gh repo create выше).
# Добавление remote (если gh не сделал это автоматически)
git remote add origin git@github.com:username/my-awesome-app.git
# Пуш
git push -u origin main
🤖 Warp Way:
"Инициализируй git репозиторий, добавь все файлы, закоммить как 'Initial commit' и запушь в origin main"
Warp сгенерирует цепочку команд. Останется только нажать Enter.
Docker: упаковать приложение и забыть
Зачем Docker
Один раз создаётся образ — потом одинаково работает на локальной машине, на стейджинге, в продакшене. Это исключает проблемы с различиями версий Node.js и окружения.
Минимум: Dockerfile + compose.yaml
Важно про безопасность: Пароли нельзя хранить в коде. Следует использовать файл .env.
Dockerfile — рецепт сборки образа приложения:
# Берём готовый образ Node (версия 22 LTS - Active в 2026)
FROM node:22-alpine
# Рабочая директория в контейнере
WORKDIR /app
# Копируем package.json и package-lock.json
COPY package*.json ./
# Устанавливаем зависимости
RUN npm install
# Копируем весь код
COPY . .
# Какой порт слушает приложение
EXPOSE 3000
# Команда для запуска
CMD ["npm", "start"]
compose.yaml (раньше назывался docker-compose.yml, но V2 рекомендует новое имя) — упрощает запуск локально и на сервере:
# В Docker Compose V2 поле version (например, '3.8') больше не обязательно
services:
app:
build: . # Собираем из Dockerfile
ports:
- "3000:3000" # Локальный порт 3000 → контейнер 3000
environment:
NODE_ENV: development
DATABASE_URL: postgres://postgres:${DB_PASSWORD}@db:5432/myapp
depends_on:
- db
volumes:
- .:/app # Горячая перезагрузка (для разработки)
- /app/node_modules
db:
image: postgres:16-alpine # Postgres 16 (Stable)
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Создание файла .env (и добавление его в .gitignore!):
DB_PASSWORD=supersecretpassword123
🤖 Warp Way: Генерация Docker-файлов:
"Создай Dockerfile для приложения Node.js 22 и compose.yaml с базой данных postgres 16"
Запуск:
# Собрать образ и запустить контейнеры
docker compose up -d
# -d = запустить в фоне
# Логи
docker compose logs -f app
# Остановить
docker compose down
🤖 Warp Way:
"Запусти docker compose в фоне" "Следи за логами сервиса app" "Останови все контейнеры docker"
Продвинутый уровень: TypeScript (NestJS / Fastify)
Для TypeScript одного Dockerfile мало — код нужно скомпилировать. Используем Multi-stage build: сборка в одном контейнере, запуск в другом (чистом и легком).
# Stage 1: Сборка
FROM node:22-alpine AS builder
WORKDIR /app
# Копируем конфиги зависимостей
COPY package*.json ./
# Устанавливаем ВСЕ зависимости (включая devDependencies для сборки)
RUN npm ci
# Копируем исходный код
COPY . .
# Собираем TypeScript в JavaScript (папка dist)
RUN npm run build
# Stage 2: Продакшн (лёгкий образ)
FROM node:22-alpine AS runner
WORKDIR /app
COPY package*.json ./
# Устанавливаем ТОЛЬКО продакшн зависимости
RUN npm ci --omit=dev
# Копируем собранное приложение из Stage 1
COPY --from=builder /app/dist ./dist
EXPOSE 3000
# Запускаем (обычно node dist/main.js)
CMD ["node", "dist/main"]
🤖 Warp Way:
"Сгенерируй multi-stage Dockerfile для приложения NestJS используя node:22-alpine с этапами builder и runner"
В чем фишка: Исходники TS и тяжелые dev-пакеты не попадают в итоговый образ. Он весит в 5 раз меньше.
Деплой: из репы на сервер
Подготовка сервера (один раз)
Важно: Сервер должен быть настроен на вход только по SSH ключу. Никаких паролей. Это база безопасности и требование для автоматического деплоя через GitHub Actions.
Ориентируемся на Ubuntu 24.04 LTS. На чистый VPS сначала нужно установить Docker. Одной командой:
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh # Установит актуальный Docker Engine (v27+)
🤖 Warp Way:
"Установи docker и docker compose на ubuntu используя официальный скрипт"
GitHub Actions
Push в репо → автоматический деплой на сервер. Настраивается один раз.
Что нужно:
- SSH ключ сервера (без пароля)
- Адрес сервера
- Путь, где лежит приложение на сервере
.github/workflows/deploy.yml:
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest # В 2026 году это обычно Ubuntu 24.04
steps:
- uses: actions/checkout@v4
- name: Deploy to server
uses: appleboy/ssh-action@v1.2.0 # Используем фиксированную версию для стабильности (не master)
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
# Если папки нет — клонируем, если есть — просто заходим
if [ ! -d "/var/www/myapp" ]; then
git clone git@github.com:username/my-project.git /var/www/myapp
fi
cd /var/www/myapp
git pull origin main
# Создаем .env из секретов (если нужно) или убеждаемся что он есть
docker compose down
docker compose up -d --build
🤖 Warp Way:
"Создай workflow github action для деплоя на сервер через ssh при пуше в ветку main используя appleboy/ssh-action"
Что сделать один раз в GitHub:
- Settings → Secrets and variables → Actions
- Добавить секреты:
SERVER_HOST— IP сервераSERVER_USER— пользователь (обычноrootилиubuntu)SERVER_SSH_KEY— весь приватный SSH ключ (результатcat ~/.ssh/id_rsaна сервере)
Готово. Теперь каждый git push main деплоит приложение автоматически.
SSH + git pull (для собственного сервера)
Проще, чем Actions. Достаточно зайти на сервер и обновить приложение.
На сервере один раз:
# Клонируем репо
git clone git@github.com:username/my-app.git /var/www/myapp
cd /var/www/myapp
# Собираем Docker образ
docker compose build
# Запускаем
docker compose up -d
🤖 Warp Way:
"Клонируй https://github.com/username/my-app.git в /var/www/myapp и запусти docker compose в фоне"
После этого, для обновления:
# С локальной машины:
git push origin main
# На сервере (или через Warp AI):
cd /var/www/myapp
git pull origin main
docker compose down
docker compose up -d --build
Одна команда для быстрого обновления:
ssh user@server.com "cd /var/www/myapp && git pull && docker compose down && docker compose up -d --build"
🤖 Warp Way: Удаленное управление сервером:
"Зайди по SSH на user@server.com, перейди в /var/www/myapp, подтяни изменения из git и перезапусти docker compose с пересборкой"
Warp сам соберет сложную команду в одну строку.
Nginx и SSL сертификаты
Зачем Nginx
Nginx — это обратный прокси (reverse proxy). Приложение работает на порту 3000, а Nginx слушает порты 80 и 443, принимает трафик и пробрасывает его приложению. Плюсы:
- HTTPS/SSL в одном месте
- Возможность держать несколько приложений на одном сервере
- Кэширование, сжатие, раздача статики
Базовая конфигурация Nginx
Важно: Для Ubuntu 24.04 (Nginx 1.24) используется старый синтаксис listen ... http2. Для более свежих версий Nginx (1.25+) нужно писать http2 on;.
/etc/nginx/sites-available/myapp:
server {
listen 80;
server_name example.com www.example.com;
# Перенаправляем HTTP на HTTPS (дальше про это)
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL сертификаты (дальше про как их получить)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Пробрасываем трафик приложению на порту 3000
location / {
proxy_pass http://localhost:3000;
# Важные заголовки
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Активация конфигурации:
# Создаём symlink
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
# Проверяем синтаксис
sudo nginx -t
# Перезагружаем Nginx
sudo systemctl reload nginx
🤖 Warp Way (включить сайт):
"Включи сайт nginx myapp, создав симлинк из sites-available, проверь конфиг и перезагрузи сервис"
🤖 Warp Way (создать конфиг): Автоматическая генерация конфига:
"Сгенерируй конфиг nginx для домена example.com с проксированием на localhost:3000, http2 и заголовками безопасности. Сохрани его в /etc/nginx/sites-available/myapp"
Он выдаст готовый блок кода или команду cat <<EOF ....
Let's Encrypt и Certbot — бесплатные SSL сертификаты
Установка Certbot:
sudo apt update
sudo apt install certbot python3-certbot-nginx # Версия из репозитория Ubuntu 24.04
🤖 Warp Way:
"Установи certbot для nginx"
Получение сертификата:
sudo certbot --nginx -d example.com -d www.example.com
🤖 Warp Way:
"Запроси ssl сертификат для example.com используя certbot"
Certbot:
- Проверяет владение доменом (HTTP challenge на порт 80)
- Получает сертификат от Let's Encrypt
- Автоматически обновляет Nginx конфиг с SSL директивами
- Спрашивает email для напоминаний о продлении
После выполнения:
- Сертификат в
/etc/letsencrypt/live/example.com/ - Nginx конфиг обновлён (HTTP → HTTPS redirect)
- HTTPS работает
Проверка возобновления сертификата:
sudo systemctl list-timers | grep certbot
# Должен быть активный таймер
# Тест возобновления (безопасно)
sudo certbot renew --dry-run
Сертификаты Let's Encrypt действуют 90 дней, но Certbot продлевает их автоматически.
Дополнительно: безопасность
1. Firewall (UFW) Не стоит оставлять лишние порты открытыми. Рекомендуется закрыть всё, кроме SSH и веба:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
🤖 Warp Way:
"Включи фаервол ufw, разрешив только ssh и http/https трафик"
2. Security Headers Добавляем заголовки в конфиг Nginx:
server {
listen 443 ssl http2;
server_name example.com;
# ... SSL и proxy_pass ...
# Безопасность
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
}
🤖 Warp Way:
"Добавь заголовки безопасности (X-Frame-Options, X-Content-Type-Options и т.д.) в файл конфигурации nginx"
Проверка SSL:
# Локально
curl https://example.com
# Или через SSL Labs (https://www.ssllabs.com/ssltest/)
🤖 Warp Way:
"Проверь детали ssl сертификата для example.com используя curl"
Real-world flow: собираем всё вместе
Сценарий: создание Node.js приложения с базой данных.
Инициализация проекта:
mkdir my-app && cd my-app
git init
echo "# My App" > README.md
npm init -y
gh repo create my-app --public --source=. --push
🤖 Warp Way:
"Создай новую папку my-app, инициализируй npm проект, git репозиторий и создай публичный репозиторий github с пушем в origin"
Создание Dockerfile и compose.yaml (как выше с Node + PostgreSQL).
На сервере:
git clone git@github.com:username/my-app.git /var/www/my-app
cd /var/www/my-app
docker compose up -d
🤖 Warp Way:
"Клонируй https://github.com/username/my-app в /var/www/my-app и запусти docker compose в фоне"
Настройка Nginx и SSL (как выше).
Каждый раз при пуше кода:
git push origin main
# На сервере (если используются GitHub Actions, это происходит автоматически):
cd /var/www/my-app && git pull && docker compose down && docker compose up -d --build
🤖 Warp Way:
"Запушь изменения в main и зайди по ssh на сервер, чтобы подтянуть изменения и перезапустить docker compose с пересборкой"
Частые проблемы и решения
Docker контейнер не стартует:
docker compose logs app # Смотреть логи
🤖 Warp: > "Покажи последние логи контейнера app и объясни ошибки"
Nginx не видит приложение:
# Проверка, что приложение слушает на 3000
docker compose ps # Контейнер должен быть UP
curl http://localhost:3000 # С сервера
🤖 Warp: > "Проверь, слушается ли порт 3000 и запущен ли docker контейнер"
SSL сертификат истек:
sudo certbot renew # Обновить сейчас (обычно автоматически)
🤖 Warp: > "Обнови все сертификаты certbot вручную"
Git не пушится (permission denied):
# Проверка SSH ключа
ssh -T git@github.com
# Если ошибка — см. раздел про SSH ключи
🤖 Warp: > "Проверь ssh соединение с github"
Итого
- Warp — терминал, который понимает команды на английском
- gh CLI — создание репо за 10 секунд
- SSH ключи — один раз настроить, потом забыть
- Docker — одна команда
docker compose up, и всё работает везде - GitHub Actions — push в репо, и приложение деплоится автоматически
- Nginx + Let's Encrypt — HTTPS за 5 минут