Развёртывание любого проекта: полная поваренная книга
Этот документ — исчерпывающий рецептурник: у вас есть исходники проекта — здесь есть путь, как развернуть их на сервере mitdev, только по документации. Каждый рецепт самодостаточен: команды копируются как есть.
Новичок? Порядок чтения: Азбука (термины, SSH, домены) → Большое руководство (первый сервер с нуля) → эта страница (рецепт под ваш конкретный стек).
Содержание:
- Выбор способа запуска (дерево решений)
- Общая подготовка (одинакова для всех)
- Рецепты по типу проекта — Node.js API (Express/Nest/Fastify) · Next.js · Статический сайт (React/Vue/HTML) · Python (FastAPI/Django/Flask) · Go / Rust / бинарник · PHP (Laravel/WordPress) · Любой проект с Docker Compose · Kubernetes · Фоновые воркеры и Telegram-боты
- Подключение к службам: строки подключения
- Несколько проектов на одном сервере
- Обновление, логи, откат
- Чек-лист «проект не открывается»
1. Выбор способа запуска
| Ситуация | Способ | Рецепт |
|---|---|---|
В проекте есть docker-compose.yml |
Docker Compose — универсально, ничего не важно про язык | 3.7 |
| Node.js без Docker | PM2 | 3.1, 3.2 |
| Только собранная статика (dist/build) | nginx напрямую, процессов не нужно | 3.3 |
| Python/Go/Rust/PHP без Docker | systemd-юнит или Docker | 3.4–3.6 |
| Нужна отказоустойчивость приложения | Kubernetes (HA-кластер из 3 серверов) | 3.8 |
| Нет Dockerfile и не Node | проще всего добавить Dockerfile (10 строк) и идти путём 3.7 | 3.7 |
Практическое правило: есть compose — используйте compose; Node без контейнеров — PM2; всё остальное стремитесь упаковать в Docker.
2. Общая подготовка
Одинакова для любого рецепта.
2.1. Сервер. Чистый сервер → curl -fsSL https://mitdev.top/get | sudo bash.
Компоненты: по умолчанию (+ Docker, если пойдёте путём compose). Проверка:
mitdev doctor — всё зелёное.
2.2. Домен. В DNS-панели: A-запись myapp.example.com → IP сервера.
Проверка: dig +short myapp.example.com возвращает IP. Без домена можно
жить (доступ по http://IP:порт), но SSL требует домен.
2.3. Доступ к репозиторию. Публичный — просто URL. Приватный:
- GitHub: Settings → Developer settings → Fine-grained token (read-only
content) → URL вида
https://<token>@github.com/you/repo.git; - GitLab: deploy token →
https://<user>:<token>@gitlab.com/you/repo.git; - либо SSH-ключ deploy-пользователя:
sudo -u deploy ssh-keygen -t ed25519, публичный ключ (sudo cat /home/deploy/.ssh/id_ed25519.pub) добавьте в Deploy keys репозитория, URL —[email protected]:you/repo.git.
2.4. Развёртывание каркаса. Всегда начинайте с:
sudo mitdev app
Он спросит имя, git-URL, домен, порт, nginx-конфиг — и сам сделает: клон в
/home/deploy/apps/<имя>, .env из .env.example, зависимости, compose
(если есть), nginx, SSL. Дальнейшие рецепты дополняют его там, где нужен
процесс-менеджер.
Локальный путь как источник. В поле источника можно указать не только
git-URL, но и путь к каталогу на сервере (абсолютный /srv/src/app или
относительный от текущего каталога). mitdev распознаёт локальный путь
автоматически и просит подтверждение. Файлы копируются в ~/apps/<name>
через rsync; исходный путь запоминается. При sudo mitdev deploy <name>
вместо git pull выполняется повторная синхронизация из этого пути.
Серверные .env, node_modules/.venv и служебный .mitdev-run.sh при
синхронизации сохраняются.
2.5. Заполните .env. Настоящие значения (строки подключения — в
разделе 4):
sudo nano /home/deploy/apps/<имя>/.env
3. Рецепты
3.1. Node.js API
Express, NestJS, Fastify, Koa — любой сервер на Node.
sudo mitdev app # имя: api, порт: тот, что слушает ваш код (например 3000)
sudo nano /home/deploy/apps/api/.env
# сборка, если есть (Nest/TypeScript):
sudo -u deploy -H bash -lc 'cd ~/apps/api && npm run build'
# запуск под PM2 (переживает перезагрузки, рестартует при падении):
sudo -u deploy -H bash -lc 'cd ~/apps/api && pm2 start npm --name api -- start && pm2 save'
# либо конкретный файл: pm2 start dist/main.js --name api
# несколько CPU-ядер: pm2 start dist/main.js --name api -i max (cluster-режим)
# проверка:
sudo -u deploy -H pm2 ls
curl -s http://127.0.0.1:3000/ | head -3
curl -s https://myapp.example.com | head -3
Если в репозитории есть ecosystem.config.js — вместо pm2 start npm
используйте pm2 start ecosystem.config.js.
3.2. Next.js
SSR/ISR (обычный Next):
sudo mitdev app # порт 3000
sudo -u deploy -H bash -lc 'cd ~/apps/web && npm run build'
sudo -u deploy -H bash -lc 'cd ~/apps/web && pm2 start npm --name web -- start && pm2 save'
Статический экспорт (output: "export"): это статика — идите в 3.3
с каталогом out/.
Обновление любого Next: sudo mitdev deploy web (сам сделает pull → install
→ build → рестарт PM2-процесса web).
3.3. Статический сайт
React/Vue/Svelte-сборка, чистый HTML — процесс не нужен, отдаёт nginx.
sudo mitdev app # домен укажите, порт любой (не используется), nginx: свой конфиг
На вопрос о своём nginx-конфиге дайте файл (или положите в репозиторий как
.mitdev/nginx.conf):
server {
listen 80;
server_name __DOMAIN__;
root /home/deploy/apps/__APP_NAME__/dist; # ваш каталог сборки: dist/build/out
index index.html;
location / { try_files $uri $uri/ /index.html; } # SPA-роутинг
location ~* \.(js|css|png|jpg|svg|woff2)$ { expires 30d; add_header Cache-Control public; }
}
Соберите проект и перезагрузите nginx:
sudo -u deploy -H bash -lc 'cd ~/apps/site && npm run build'
sudo mitdev nginx reload
3.4. Python
FastAPI/Flask (uvicorn/gunicorn) или Django. mitdev не ставит Python-стек модулем, поэтому два пути.
Путь А (рекомендуется) — Docker. Добавьте в проект Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
и docker-compose.yml:
services:
app:
build: .
restart: unless-stopped
env_file: .env
ports: ["8000:8000"]
Дальше — обычный sudo mitdev app (порт 8000): compose поднимется сам.
Путь Б — systemd без Docker:
sudo apt install -y python3-venv
sudo -u deploy -H bash -lc 'cd ~/apps/api && python3 -m venv .venv && .venv/bin/pip install -r requirements.txt'
sudo tee /etc/systemd/system/myapi.service <<'EOF'
[Unit]
Description=myapi
After=network.target
[Service]
User=deploy
WorkingDirectory=/home/deploy/apps/api
EnvironmentFile=/home/deploy/apps/api/.env
ExecStart=/home/deploy/apps/api/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now myapi
Django: ExecStart=….venv/bin/gunicorn config.wsgi -b 127.0.0.1:8000;
статику отдайте nginx'ом (alias на staticfiles/ в своём конфиге), миграции —
.venv/bin/python manage.py migrate перед стартом.
3.5. Go / Rust / бинарник
sudo mitdev app # клон + nginx; порт — какой слушает бинарник
# сборка:
sudo -u deploy -H bash -lc 'cd ~/apps/svc && go build -o svc ./cmd/svc' # или cargo build --release
sudo tee /etc/systemd/system/svc.service <<'EOF'
[Unit]
Description=svc
After=network.target
[Service]
User=deploy
WorkingDirectory=/home/deploy/apps/svc
EnvironmentFile=/home/deploy/apps/svc/.env
ExecStart=/home/deploy/apps/svc/svc
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now svc
3.6. PHP
Laravel/WordPress — самый надёжный путь на mitdev-сервере — Docker
(php-fpm не ставится модулем). Для Laravel добавьте compose с officially
поддерживаемым образом (или Laravel Sail: php artisan sail:install);
для WordPress — стандартный compose из документации WordPress (wordpress +
mysql). Дальше — sudo mitdev app, порт — опубликованный в compose.
Nginx mitdev проксирует на контейнер, SSL — как обычно.
3.7. Docker Compose
Универсальный путь для ЛЮБОГО проекта. Требование одно: в корне репозитория
docker-compose.yml (или compose.yml), где веб-сервис публикует порт:
services:
app:
build: .
restart: unless-stopped # обязательно: авто-старт после перезагрузки
env_file: .env
ports: ["3000:3000"] # левый порт = порт для mitdev app
sudo mitdev app # порт: 3000 (левый из ports)
sudo nano /home/deploy/apps/<имя>/.env
cd /home/deploy/apps/<имя> && sudo docker compose up -d # если правили .env после
Управление: mitdev docker ps, mitdev docker logs <контейнер>,
cd ~/apps/<имя> && docker compose restart.
База в compose или на хосте? Если проект несёт свою БД в compose —
оставьте как есть (данные — в named volume). Если хотите центральную/
кластерную БД mitdev — уберите сервис БД из compose и дайте приложению
строку подключения из раздела 4; контейнеру для
доступа к БД хоста используйте host.docker.internal c
extra_hosts: ["host.docker.internal:host-gateway"].
3.8. Kubernetes
Когда нужен HA (переживать смерть сервера) или десятки реплик.
# образ должен лежать в registry (GitHub Actions → ghcr.io — стандартный путь)
sudo mitdev k8s deploy myapp ghcr.io/you/myapp:latest 3000 myapp.example.com
Всё остальное (Deployment с probes, анти-аффинити по серверам, Service,
публикация, SSL) mitdev делает сам. Кластер из трёх серверов: режимы
init/join в инсталляторе + mitdev k8s token — см.
COMMANDS.md. Секреты: положите .env в
кластер один раз — kubectl create secret generic myapp-env --from-env-file=.env, и добавьте в свой манифест envFrom: secretRef.
3.9. Воркеры и боты
Процессы без входящего HTTP (очереди, кроны, Telegram-боты): домен и nginx
не нужны — на вопросы mitdev app о домене ответьте «пусто».
sudo mitdev app # домен: пусто
# Node-воркер:
sudo -u deploy -H bash -lc 'cd ~/apps/bot && pm2 start src/bot.js --name bot && pm2 save'
# не-Node — systemd-юнит из рецепта 3.5, либо compose без ports.
Расписание (аналог cron): pm2 start job.js --name job --cron "0 3 * * *" --no-autorestart, либо системный /etc/cron.d/.
4. Строки подключения
Все пароли служб созданы при установке и лежат в /root/.mitdev-credentials
(sudo cat /root/.mitdev-credentials). Подставляйте в .env проекта:
| Служба | Строка подключения / переменные |
|---|---|
| PostgreSQL (этот же сервер) | postgresql://<user>:<пароль postgresql>@127.0.0.1:5432/<база> |
| PostgreSQL-кластер через прокси | postgresql://<user>:<пароль>@127.0.0.1:5432/<база>?sslmode=require (после mitdev pg proxy A B C на этом сервере) |
| PostgreSQL-кластер через VIP | postgresql://<user>:<пароль>@<VIP>:5432/<база>?sslmode=require |
| Redis | redis://:<пароль redis>@127.0.0.1:6379/0 |
| MongoDB | mongodb://127.0.0.1:27017/<база> (auth включаете сами — см. SECURITY.md) |
| RabbitMQ | amqp://admin:<пароль rabbitmq>@127.0.0.1:5672/ (консоль: :15672) |
| MinIO (S3) | endpoint http://127.0.0.1:9000, AWS_ACCESS_KEY_ID=<root_user>, AWS_SECRET_ACCESS_KEY=<root_password> |
Приложение на другом сервере, а БД здесь? На каждом DB-узле:
sudo mitdev pg allow <IP-приложения> <база> <user> — и в строке вместо
127.0.0.1 адрес DB-сервера (или VIP/прокси). Для Redis/Mongo/RabbitMQ
наружу по умолчанию закрыто — открывайте осознанно
(sudo mitdev firewall allow … + bind в конфиге службы) или туннелируйте.
5. Несколько проектов
Сколько угодно приложений на одном сервере — уникальные имя и порт:
sudo mitdev app # api, api.example.com, порт 3000
sudo mitdev app # admin, admin.example.com, порт 3001
sudo mitdev app # bot, без домена, порт не важен
Каждому — свой nginx-сайт и свой SSL-сертификат (автоматически). Поддомены:
просто ещё A-записи на тот же IP. Один домен, разные пути (/api → :3000,
/ → статика) — случай для своего nginx-конфига (рецепт 3.3
6. Обновление, логи, откат
sudo mitdev deploy <имя> # git pull → зависимости → build → рестарт (PM2/compose)
Для systemd-рецептов после deploy добавьте рестарт юнита:
sudo systemctl restart <юнит>.
Логи:
sudo -u deploy -H pm2 logs <имя> --lines 100 # PM2
mitdev docker logs <контейнер> # Docker
sudo journalctl -u <юнит> -n 100 -f # systemd
mitdev k8s logs <имя> # Kubernetes
tail -f /var/log/nginx/<имя>.access.log # HTTP-трафик
Откат на прошлый коммит:
sudo -u deploy -H git -C /home/deploy/apps/<имя> log --oneline -5
sudo -u deploy -H git -C /home/deploy/apps/<имя> reset --hard <коммит>
sudo mitdev deploy <имя> # пересборка/рестарт уже от старого кода
Бекапы перед рискованными обновлениями: sudo mitdev backup.
7. Чек-лист
Проект не открывается — идите сверху вниз, где сломалось — там и чините:
# 1. Процесс жив и слушает свой порт?
sudo -u deploy -H pm2 ls # или docker compose ps / systemctl status
sudo ss -tlnp | grep <порт>
# 2. Отвечает локально?
curl -sv http://127.0.0.1:<порт>/ | head # ошибка здесь = смотрите логи приложения (.env? миграции?)
# 3. Nginx знает про домен и жив?
sudo nginx -t && ls /etc/nginx/sites-enabled/
curl -sv -H "Host: myapp.example.com" http://127.0.0.1/ | head
# 4. DNS указывает сюда?
dig +short myapp.example.com # = IP сервера?
# 5. Снаружи пускает файрвол?
mitdev firewall status # 80,443 открыты?
# 6. SSL?
curl -sv https://myapp.example.com 2>&1 | grep -E 'SSL|HTTP'
sudo certbot certificates
Типовые причины по частоте: незаполненный .env (№1 с большим отрывом);
порт в mitdev app не совпадает с портом приложения; не выполнены миграции
БД; DNS ещё не обновился (до часа); приложение слушает 127.0.0.1 внутри
Docker (нужно 0.0.0.0). Остальное — TROUBLESHOOTING.md.