Coolify на VPS: установка своего PaaS вместо Heroku и Vercel

Coolify на VPS: установка официальным скриптом, домен с TLS для панели, деплой из GitHub, бэкапы в S3 и обновления — свой Heroku и Vercel на сервере.

Статья сверена с Coolify 4.3.23 и актуальна на сентябрь 2026 года. Ветка v4 вышла из беты только в апреле 2026-го, поэтому многие инструкции 2024–2025 годов устарели.

Что такое Coolify и когда он нужен

Coolify — open-source панель под лицензией Apache 2.0, которая превращает сервер в PaaS: по git push собирает образ, запускает контейнер, выпускает сертификат Let's Encrypt и направляет домен на приложение. В комплекте базы данных, бэкапы, превью-деплои и каталог из трёхсот с лишним сервисов, среди них n8n, Uptime Kuma и Forgejo.

Внутри обычный Docker: панель, её PostgreSQL, Redis и сервис живых обновлений работают в контейнерах, а на каждом сервере с приложениями стоит прокси coolify-proxy на Traefik или Caddy. Серверами, включая собственный, Coolify управляет по SSH.

  • Подходит для нескольких приложений и баз на одном-двух серверах, когда нужны деплой по push, превью и общий интерфейс вместо SSH.
  • Проще без Coolify, если приложение одно и меняется редко: хватит Docker Compose и деплоя из Git.
  • Проще без Coolify, если инфраструктура должна жить в git: настройки Coolify хранятся в её базе, а у Kamal 2 или compose-файлов — в репозитории.

Сколько ресурсов нужно

Официальный минимум — 2 ядра, 2 ГБ памяти и 10 ГБ диска. Сама панель лёгкая: в замере на 4.3.23 её контейнеры в простое заняли около 300 МБ, но на сервере с 1 ГБ сборка небольшого Node-приложения забрала почти всю память и больше гигабайта swap.

  • 2 vCPU и 4 ГБ — панель, база и несколько лёгких сервисов, сборки по одной.
  • 4 vCPU и 8 ГБ — несколько приложений на Node.js или Python с PostgreSQL и сборками на месте.
  • Диск от 40 ГБ: образы, кэш сборок и локальные бэкапы растут быстрее данных.

Главный потребитель CPU и памяти — сборки. В одном из issues пересборка Next.js заняла 3,6 ГБ памяти и повесила панель на сервере с 8 ГБ без swap. Лимиты ресурсов приложения на сборку не действуют, только на запущенный контейнер. На небольшом сервере поставьте Number of concurrent builds в Configuration → Advanced сервера равным 1 вместо 2, добавьте swap, а частые сборки вынесите с продакшена (шаг 10).

В пакет ForestsNet Cloud входит только исходящий трафик: docker pull, git clone и загрузка зависимостей при сборке не считаются, а исходящий — это ответы приложений, выгрузка бэкапов и push образов с build-сервера. Другие задачи разработчика — в обзоре VPS.

Что понадобится

  • VPS с Ubuntu 24.04 или 22.04 LTS либо Debian 12 без своего веб-сервера и других Docker-проектов: порты 80 и 443 займёт прокси, а очистка Coolify удаляет чужие образы.
  • Вход по SSH под root по ключу: из статьи «Усиление SSH» подходит PermitRootLogin prohibit-password, но не no.
  • Домен с A-записями на IP сервера: coolify.example.com для панели и *.apps.example.com для приложений, см. статьи о привязке домена и о DNS-записях.
  • Репозиторий на GitHub и S3-совместимое хранилище вне сервера.

Шаг 1. Подготовьте сервер

Обновите систему по чеклисту базовой защиты. При 4 ГБ памяти и меньше добавьте swap: сборку он не ускорит, но поможет пережить её пик.

apt update && apt -y upgrade
fallocate -l 4G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

Шаг 2. Установите Coolify официальным скриптом

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

Скрипт ставит Docker Engine через get.docker.com (Docker из snap не поддерживается), пишет в /etc/docker/daemon.json ротацию логов и пул адресов, создаёт /data/coolify с секретами в /data/coolify/source/.env, добавляет свой SSH-ключ в authorized_keys root и запускает контейнеры.

Лучше сразу закрепить версию и выключить автообновление (почему — в разделе про обновления):

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | AUTOUPDATE=false bash -s 4.3.23

Сохраните /data/coolify/source/.env вне сервера: APP_KEY из него шифрует ключи и пароли в базе Coolify, и без него бэкап панели бесполезен.

Важно: не запускайте install.sh повторно на работающей установке. Он рекурсивно меняет владельца и права в /data/coolify, включая данные сервисов, — в одном из issues так на 20 часов лёг почтовый сервер. Обновляйтесь через upgrade.sh.

Шаг 3. Создайте администратора и переведите панель на домен

Откройте http://IP:8000 и сразу зарегистрируйтесь: первый пользователь получает root-доступ к серверу, после этого регистрация закрывается. До этого страницу может занять кто угодно.

Включите 2FA в Profile, затем в Settings → Configuration → General впишите в URL адрес https://coolify.example.com. Coolify проверит DNS, прокси выпустит сертификат. Убедитесь, что по HTTPS работают панель, живые логи деплоя и терминал. Кириллические домены вводите в Punycode, вида xn--....

Шаг 4. Закройте порты 8000, 6001 и 6002

Через ufw их не закрыть: Docker публикует порты своими правилами iptables в обход него, подробнее — в статье о файрволе ufw. Официальный способ — привязать порты к 127.0.0.1 в docker-compose.custom.yml, который обновления не трогают:

cat > /data/coolify/source/docker-compose.custom.yml <<'EOF'
services:
  coolify:
    ports: !override
      - "127.0.0.1:${APP_PORT:-8000}:8080"
  soketi:
    ports: !override
      - "127.0.0.1:${SOKETI_PORT:-6001}:6001"
      - "127.0.0.1:6002:6002"
EOF
cd /data/coolify/source
docker compose --env-file .env -f docker-compose.yml -f docker-compose.prod.yml \
  -f docker-compose.custom.yml config -q && echo OK
curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh | bash -s 4.3.23

Последняя команда перезапускает Coolify с новым файлом, подставьте в неё свою версию. С другой машины curl -m 5 http://IP:8000 отвечать больше не должен.

Совет: привязка к 127.0.0.1 оставляет запасной вход, если сломаются домен или прокси: ssh -L 8000:127.0.0.1:8000 root@IP и http://localhost:8000 в браузере.

Шаг 5. Настройте wildcard-домен для приложений

В Servers → localhost → General укажите Wildcard Domain https://apps.example.com: кнопка Generate Domain будет выдавать приложениям адреса https://<id>.apps.example.com, а свой домен вписывается в Domains приложения.

Каждый поддомен получает свой сертификат, а Let's Encrypt выдаёт не больше 50 сертификатов на домен за 7 дней. При десятках приложений и превью выпустите один wildcard-сертификат через DNS-01: в Servers → Proxy → Configuration замените флаги httpchallenge на dnschallenge с токеном DNS-провайдера и добавьте роутер для *.apps.example.com в Dynamic Configurations.

Адреса sslip.io, которые Coolify генерирует без wildcard-домена, годятся только для проверки по HTTP. Для TLS нужен свой домен: на серверах ForestsNet Cloud домен и сертификат должны принадлежать вам.

Шаг 6. Подключите GitHub

Подключайте GitHub после перехода на домен: GitHub App запоминает адрес вебхуков при создании, и приложение, созданное по http://IP:8000, после закрытия порта молча перестанет запускать деплой.

В Sources → + Add выберите GitHub App, Automated Installation и endpoint https://coolify.example.com. Оставьте Preview Deployments, если нужны превью, и дайте приложению доступ только к нужным репозиториям.

Шаг 7. Разверните приложение

В проекте нажмите + New → Private Repository (with GitHub App) и выберите сервер, репозиторий, ветку и Build Pack.

Способ сборки

  • Nixpacks сам определяет язык — удобно для старта, но Railway его больше не развивает.
  • Railpack — преемник Nixpacks, в Coolify с 4.1 в статусе beta; перед переходом сравните образы.
  • Dockerfile — предсказуемый выбор для продакшена: тот же образ, что локально и в CI.
  • Docker Compose — для нескольких сервисов, но без rolling updates и build-сервера, а health checks задаются в compose-файле.
  • Static раздаёт готовые файлы через nginx, Docker Image разворачивает образ из registry без сборки.

Порт и адрес

Ports Exposes — порт внутри контейнера, на первый из них прокси шлёт трафик домена; домен вида https://app.example.com:3000 ведёт на порт 3000 и наружу ничего не открывает. Приложение должно слушать 0.0.0.0, иначе будет 502. Port Mappings для веб-приложений не используйте: такой порт открыт в интернет в обход ufw и отключает rolling updates.

Переменные окружения

У переменной два независимых флажка, Build Variable и Runtime Variable, по умолчанию включены оба. Секретам, нужным только в работе, снимите Build Variable: аргументы сборки остаются в метаданных образа. А NEXT_PUBLIC_* и VITE_* вшиваются при сборке, и после их изменения нужен новый деплой, а не перезапуск.

Health check

В Configuration → Healthcheck задайте проверку вроде GET /health. Она выполняется внутри контейнера через curl или wget, которых часто нет в образах на Alpine и distroless. Поля Return Code и Response Text не сравниваются, важен только код выхода.

Без проверки rolling update переключит трафик сразу после старта контейнера, даже если приложение ещё не поднялось. С проверкой старый контейнер работает, пока новый не станет healthy; на завершение по SIGTERM даётся 30 секунд.

Шаг 8. Базы данных и бэкапы

+ New → Database создаёт PostgreSQL, MySQL, MariaDB, MongoDB, Redis, KeyDB, Dragonfly или ClickHouse. Приложение ходит к базе по внутреннему адресу в сети Docker, публичный порт не нужен. Об устройстве СУБД — в статьях про PostgreSQL и Redis.

Расписание и S3

Добавьте бакет в S3 Storages и проверьте подключение, затем в базе откройте Backups → + Add: расписание cron, S3 и сроки хранения, отдельные для локальных копий и S3. Встроенные бэкапы есть у PostgreSQL, MySQL, MariaDB, MongoDB и ClickHouse, но не у Redis, KeyDB и Dragonfly. Остановленную базу Coolify пропускает.

Зелёному статусу не верьте на слово: в issues описаны бакеты, пустые неделями при письмах об успехе, а после обновления до 4.1.2 бэкап самой панели молча перестал запускаться. Оба бага исправлены, но бакет проверяйте сами.

Важно: статус Success (S3 Warning) значит, что локальная копия есть, а в S3 файл не ушёл. Включите уведомление Backup Failure — оно срабатывает и на это — и раз в неделю смотрите на свежие файлы в бакете.

С версии 4.3 бэкапятся и тома — архивами tar.gz, но без кнопки восстановления, а архив тома под записью бывает несогласованным.

Проверьте восстановление

Раз в месяц создайте временную базу той же версии, залейте в неё свежий дамп через Configuration → Import Backup и проверьте на ней приложение. Ручные дампы — в статье о бэкапах баз данных.

Бэкап самой Coolify

Проекты, переменные и ключи живут в coolify-db, её ежедневный бэкап включается в Settings → Backup. В S3 уходит только дамп, а для восстановления на новом сервере нужны ещё APP_KEY из .env и каталог /data/coolify/ssh/keys — их копируйте отдельно, например через restic.

Шаг 9. Включите превью-деплои

Превью включаются в Configuration → Advanced: на каждый pull request Coolify разворачивает копию и удаляет её после закрытия PR. Адрес задаёт Preview URL Template, по умолчанию {{pr_id}}.{{domain}}: PR №42 приложения app.example.com получит 42.app.example.com, и нужна запись *.app.example.com. Удобнее держать все превью под одним доменом — шаблон {{pr_id}}.preview.example.com и wildcard-сертификат из шага 5.

  • Каждое превью — ещё одна сборка и ещё один контейнер, закладывайте ресурсы.
  • Дайте превью свои переменные и отдельную базу: код из PR выполняется на вашем сервере.
  • Не включайте Allow Public PR Deployments, иначе превью запустит PR из любого форка.

Шаг 10. Вынесите сборки с продакшен-сервера

Пора, если деплой тормозит продакшен или заканчивается OOM.

GitHub Actions. Workflow собирает образ и кладёт его в GHCR, а Coolify разворачивает приложение типа Docker Image по вызову API. Включите API Access в Settings → Configuration → Advanced, создайте в Keys & Tokens токен с правом deploy и вызывайте вебхук последним шагом, после тестов. Собирать можно и на своём self-hosted runner.

curl --fail -X POST "https://coolify.example.com/api/v1/deploy?uuid=<uuid>&force=false" \
  -H "Authorization: Bearer $COOLIFY_TOKEN"

Build-сервер. Чистый сервер добавляется в Servers с отметкой Use it as a build server? и не может запускать приложения. На нём нужен docker login в registry, а в приложении — поле Docker Image и Use a Build Server?; для приватного registry залогиньтесь и на продакшене. Архитектура серверов должна совпадать, Compose-приложения режим не поддерживают. Если build-сервер недоступен, Coolify соберёт образ на продакшене, в логе будет «No suitable build server found».

Несколько серверов. У каждого сервера свой прокси, и DNS приложения должен вести на его сервер. Одно приложение можно развернуть на нескольких серверах через registry и внешний балансировщик, но не с постоянными томами и не в Compose. Docker Swarm в Coolify устарел и уйдёт в v5.

Обновления и безопасность

Обновления

Автообновление после установки включено и срабатывает каждую ночь в 00:00, когда новые образы тянут все инстансы разом: в одном из issues обновление 37 ночей подряд упиралось в лимит registry, а в логе при этом значился успех. Выключите автообновление в Settings → Configuration → Updates и обновляйтесь вручную: release notes, бэкап инстанса, ни одного идущего деплоя, затем кнопка в панели или скрипт с явной версией:

curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh | bash -s 4.3.23

Без версии скрипт запишет в .env значение latest, и проверка обновлений сломается. Откат — тем же скриптом со старой версией, если новая не меняла схему базы, иначе — бэкап инстанса.

Что ломалось в 2026 году:

  • 4.2–4.3: API деплоя принимает только POST, старые вебхуки с GET получают 405; роль Member стала только для чтения; у compose-сервисов с точками и дефисами в имени сменились имена роутеров Traefik.
  • Патч-версии 4.3.19 и 4.3.22: Sentinel стал обязательным, из интерфейса и API убрали host-path у томов.
  • Docker 29 при старом Traefik: сайты отвечают «No available server», в логах прокси client version 1.24 is too old. Прокси Coolify сама не обновляет — поменяйте образ на Traefik 3.6.1 или новее в Servers → Proxy до обновления Docker.

Безопасность

Coolify хранит SSH-ключи с root-доступом ко всем подключённым серверам, и взлом панели — это взлом всей инфраструктуры. За 2025–2026 годы для неё опубликовали 70 уязвимостей, 21 критическую, в основном инъекции команд, дававшие пользователю панели root на серверах. В январе 2026 года Censys насчитал около 52 тысяч панелей в открытом интернете. Почти всё закрыли ещё до 4.0.0, но раскрывали уязвимости через 2–5 месяцев после исправления, так что отставать от релизов опасно.

Важно: в 4.3.23 остаётся CVE-2026-86117: вход через OAuth пускает в любой аккаунт с совпадающим email, мимо пароля и 2FA. На конец сентября 2026 года исправление есть только в ветке main — пока оно не выйдет в релизе, держите OAuth-провайдеры выключенными.

Минимум:

  • порты 8000, 6001 и 6002 закрыты, панель только по HTTPS, у всех аккаунтов 2FA;
  • в команде только те, кому вы доверили бы root; API выключен или ограничен токенами с минимальными правами и Allowed IPs;
  • у баз нет публичных портов;
  • исправления безопасности ставятся за пару дней, релизы и advisories — в подписке;
  • SSH закрыт по чеклисту и fail2ban, а новый порт SSH прописан и в настройках сервера в Coolify.

Сервисы из каталога тоже нужно обновлять: у автора статьи, переведённой на Хабре, Umami десять дней майнил Monero через уязвимость Next.js, хотя исправление вышло за неделю до взлома.

Dokploy как альтернатива

Dokploy — ближайший аналог: панель поверх Docker и Traefik с базами, бэкапами в S3 и превью. Требует от 2 ГБ памяти и 30 ГБ диска, занимает порты 80, 443 и 3000:

curl -sSL https://dokploy.com/install.sh | sh
  • Swarm в основе: установщик всегда инициализирует Docker Swarm, приложения работают как сервисы с репликами.
  • Compose в центре: основной сценарий — compose-проекты и стеки.
  • Версия 0.x: актуальна 0.30.8, за 2025–2026 годы — 60 security advisories, 34 критических, так что ниже 0.29.13 не оставайтесь. SSO и аудит — по коммерческой лицензии.
  • Порт 3000 после настройки домена закрывают командой docker service update --publish-rm "published=3000,target=3000,mode=host" dokploy, а порты Swarm 2377, 7946 и 4789 — файрволом.

Dokploy удобнее, когда нужны реплики на нескольких нодах и Compose как основной формат, Coolify — когда важнее стабильная ветка 4.x, чистая Apache 2.0 и rolling updates без Swarm.

Диагностика

  • Панель не открывается по домену — не обновился DNS, закрыт порт 80 или нет сертификата. Зайдите через SSH-туннель и смотрите Servers → Proxy → Logs.
  • 502 Bad Gateway — приложение слушает 127.0.0.1 или порт не совпадает с Ports Exposes.
  • 503 «No available server» — контейнер unhealthy (в образе нет curl или wget, проверка смотрит не на тот порт) или старый Traefik после обновления Docker.
  • 504 на больших загрузках и долгих запросах — у Traefik таймаут чтения 60 секунд, его поднимают в конфигурации прокси.
  • Сертификат TRAEFIK DEFAULT CERT — Let's Encrypt не прошёл проверку: закрыт порт 80, домен проксирует Cloudflare, AAAA-запись ведёт в нерабочий IPv6 или сработал лимит (ошибка 429 в логах прокси).
  • Бесконечный редирект за Cloudflare — режим SSL Flexible вместо Full (strict), см. статью «Cloudflare перед VPS».
  • Сервер замирает при деплое — сборка съела память и CPU, лимиты на неё не действуют: одна сборка за раз, swap или build-сервер. Разбор нагрузки — в статье «Высокая нагрузка на сервер».
  • Push не запускает деплой — GitHub App шлёт вебхуки на старый адрес. Проверьте Recent Deliveries на GitHub и смените Webhook URL на https://coolify.example.com/webhooks/source/github/events.
  • Деплой падает с «no space left on device» — диск заняли образы и кэш сборок. Включите Servers → Docker Cleanup (по умолчанию раз в сутки или при заполнении диска на 80%), но не Delete Unused Volumes: в неиспользуемых томах бывают данные. Остальное — в статье «Закончилось место на диске».

Источники