Проблема
Мне, как backend‑разработчику, приходится работать с деплоем своих проектов. Каждый раз одна и та же рутина: настройка сервера, Nginx, SSL, безопасность сервера(fail2ban, отдельный пользователь), CD и многое другое. Это забирает много времени.
Что сделал
После десятков задеплоенных проектов я понял, что все эти действия можно автоматизировать, и решил написать скрипт. Работает так: я запускаю скрипт и ввожу свои данные, которые уникальны у каждого проекта: домен, сервер, Docker, .env. Код этих bash‑скриптов я выложил в открытый репозиторий на GitHub, так что каждый может посмотреть, что выполняется на сервере, куда осуществляется деплой.
Конечно, в процессе работы со скриптами я сталкивался с ошибками, багами, недоработками.
Приведу пример с докером. При деплое на российских серверах у меня падала сборка Docker‑образов из‑за сетевых ограничений, поэтому я начал прописывать зеркала — тогда сборка стала быстрой и надёжной. Также при работе с внешними API запросы к сервисам были нестабильными, поэтому я прописал DNS‑серверы. Теперь всё надёжно и летает.
mkdir -p /etc/docker cat > /etc/docker/daemon.json <<‘JSON’ { “registry-mirrors”: [“https://mirror.gcr.io”, “https://dockerhub.timeweb.cloud”], “userland-proxy”: false, “dns”: [“8.8.8.8”, “1.1.1.1”] } JSON systemctl restart docker || true
С каждым разом я искал возможность автоматизации, потому что где‑то нужен мониторинг системы — Grafana + Prometeus. Их я решил поднимать в отдельном Docker Compose‑файле и запускать вместе с основным. Где‑то нужны защищённые роуты, в качестве VPN выбрал AmneziaWG, он простой в настройке и, как показывает практика, не блокируется. Поднимаем VPN, а в Caddyfile проверяем IP‑адрес запроса, если запрос из нашей сети — пропускаем, если нет — отдаём 404:
@protected {
path /путь /путь/ /путь/*
not remote_ip 10.8.0.0/24
}respond @protected “404 Not Found” 404
Тут же расскажу про Caddy и почему я выбрал его, а не Nginx. Одно из преимуществ Caddy — автоматический SSL: про ручную настройку сертификатов можно практически забыть, и лёгкость конфигурации. В будущем, конечно, планирую перейти на Nginx: всё‑таки он проверен годами, да и при больших нагрузках может быть быстрее. Caddy поднимаю глобально на весь , раньше он запускался в отдельном Docker‑контейнере. Это решение — вынести Caddy глобально — добавило возможность гибко им управлять при добавлении/удалении/обновлении проектов на 1
header {
Strict-Transport-Security "max-age=31536000;"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
-Server
}
и подстановку реального IP и попытки достучаться до приложения, чтобы не отдавать пользователю 502:
reverse_proxy app-проект:порт {
header_up X-Real-IP {http.request.remote.host}
lb_try_duration 30s
lb_try_interval 250ms
}
Почему я сделал сервис, ведь уже есть готовые скрипты?
Эти bash‑команды либо писать вручную на
Про безопасность
Так как сервис работает с чужими серверами, я не хотел делать магию в стиле «просто доверься». Здесь есть несколько важных моментов.
Во‑первых, все запросы к самому сервису идут по HTTPS. Это закрывает передачу данных между браузером и сервисом, но понятно, что одного HTTPS недостаточно, потому что дальше остаются вопросы хранения данных, SSH‑доступ и выполнение команд на сервере.
Во‑вторых, сам деплой выполняется по SSH. То есть сервис не ставит на сервер какой‑то агент, который постоянно работает на сервере и ожидает команд, а подключается по SSH и выполняет нужные шаги во время деплоя. SSH в этом плане удобен тем, что уже решает задачу защищённого удалённого доступа.
.env я стараюсь не хранить в базе на постоянной основе. Он нужен на этапе деплоя, передаётся на сервер и используется уже там. Отдельно слежу, чтобы значения из .env не попадали в логи деплоя, потому что логами делюсь при отладке.
Доступ к GitHub нужен только на время получения кода для деплоя. Здесь тоже важно не записывать токены в логи и не хранить их в открытом виде. Из того, что всё‑таки нужно хранить постоянно: метаданные проекта, домен, репозиторий, настройки деплоя и зашифрованный приватный ключ для CD/откатов. Приватный ключ хранится не в чистом виде, а в зашифрованном виде через AES‑GCM. Это не делает систему неуязвимой, но защищает от самого тупого сценария, если кто‑то получит дамп базы, он не увидит ключи в открытом виде.
Понятно, что здесь ещё есть что улучшать: аудит действий, ротацию ключей, ограничения прав пользователя на сервере, более аккуратную работа с секретами и отдельные роли для команды. Но базовая идея такая: не хранить лишнее, не писать секреты в логи, передавать данные по защищённым каналам и показывать код скриптов, которые реально выполняются на сервере.
Что насчёт Dokku, Heroku, Railway и Render?
Эти инструменты отлично решают проблему деплоя, но всё равно это не то, что я хотел. Dokku хорош тем, что всё остаётся на твоём сервере. Но у меня всё равно оставалось ощущение, что я опять живу в терминале и вручную сопровождаю инфраструктуру. Heroku, Railway, Render и похожие сервисы удобны, но это уже их инфраструктура, ограничения и ценник. А мне хотелось немного другого: оставить проект на своём
Кому интересен код, сервис:
Гитхаб с bash‑скриптами: https://github.com/slime4ik/djaploy‑scripts
Веб‑интерфейс, который вырос: https://djaploy.dev
Автор: Slime4ik
