Elixir-приложение на Phoenix выкладывается в сеть без аренды сервера: вы отдаёте исходники вместе с mix.exs, платформа определяет Elixir, устанавливает зависимости, собирает проект и выдаёт публичную HTTPS-ссылку. Условий три, и они решающие: порт приложение берёт из переменной PORT и слушает 0.0.0.0, SECRET_KEY_BASE лежит в секретах, а не в коде, база подключается по DATABASE_URL. И есть ловушка для Elixir: если приложение идёт скомпилированным релизом, без PHX_SERVER=true оно стартует без ошибок в логах и не слушает ничего. Ниже — путь от папки с кодом до HTTPS-ссылки на Dockbox.
Почему Phoenix-приложение по умолчанию тащат на VPS
Привычный сценарий — длинный чек-лист: арендовать сервер, поставить совпадающие версии Erlang и Elixir, собрать релиз, поднять базу, поставить перед приложением nginx, выпустить сертификат и проследить, чтобы всё вернулось в строй после перезагрузки. Сам Phoenix готов за вечер, а деплой забирает пару выходных.
Версии Erlang и Elixir. Несовпадение версий с сервером — типовая причина падения сборки на чужой машине.
Сборка релиза. mix release собирает релиз под ОС и архитектуру той машины, где он запускается, — то есть на сервере или в контейнере.
Обратный прокси и сертификат. Без nginx приложение не отдаёт HTTPS, а сертификат нужно продлевать каждые три месяца.
Автозапуск. Чтобы всё поднялось после перезагрузки, нужен systemd-юнит.
Оплата независимо от нагрузки. Машина стоит денег все 30 дней, даже если сайт открывают раз в сутки.
Платформа деплоя снимает эту часть: она находит mix.exs, ставит зависимости, собирает проект и отдаёт публичную HTTPS-ссылку. Ни сервера, ни nginx, ни ручного сертификата.
Шаг 1. Отдайте mix.exs и исходники, а не собранный релиз
Сборка идёт на нашей стороне, поэтому в архив кладите исходный код, а в корне должны лежать mix.exs и mix.lock: по ним платформа видит, что перед ней Elixir, и фиксирует версии библиотек. Папки _build и deps в архив не кладите — это результат сборки на вашей машине, они раздувают загрузку. Готовый релиз из _build/prod/rel тоже не нужен, он собирается заново. Ассеты в priv/static не везите: они компилируются при публикации, если в mix.exs есть стандартный алиас assets.deploy.
Локально релиз собирается так, но перед загрузкой это не требуется:
MIX_ENV=prod mix release
Шаг 2. Слушайте порт из PORT, а не 4000
Платформа передаёт порт в переменной PORT, а приложение должно слушать 0.0.0.0, а не localhost. Жёстко прописанный порт 4000 — причина номер один, по которой ссылка не открывается: приложение работает, но не там, куда приходит трафик.
Плохой вариант:
config :my_app, MyAppWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4000]
Правильный — читать значение из окружения в config/runtime.exs. Это принципиально: runtime.exs выполняется при запуске приложения, а prod.exs — во время сборки, когда секретов ещё нет.
config :my_app, MyAppWeb.Endpoint,
http: [
ip: {0, 0, 0, 0},
port: String.to_integer(System.get_env("PORT") || "4000")
]
Шаг 3. SECRET_KEY_BASE и PHX_HOST — в секреты
В production Phoenix отказывается стартовать без SECRET_KEY_BASE — значения, которым подписываются сессии и cookie. Сгенерируйте его на своей машине:
mix phx.gen.secret
Укажите строку в настройках проекта как секрет SECRET_KEY_BASE: платформа хранит его зашифрованным и подставляет переменной окружения, поэтому в репозиторий он не попадает. Если приложение строит абсолютные ссылки или шлёт письма, после первой публикации добавьте PHX_HOST со своим адресом, иначе ссылки продолжат вести на localhost. Тем же секретом подписываются сессии: сменив значение, вы разлогините всех.
Шаг 4. Включите сервер, если приложение идёт релизом
Классическая ловушка Elixir. Когда приложение запускается не через mix phx.server, а как скомпилированный релиз, генераторы Phoenix оставляют в config/runtime.exs условие: веб-сервер поднимается только если задана переменная PHX_SERVER. Без неё приложение стартует без ошибок в логах, ничего не слушает, а ссылка не отвечает — искать причину в коде бесполезно, её там нет. Добавьте в секреты проекта PHX_SERVER=true — и релиз начнёт отвечать: mix phx.server эта переменная не мешает.
Шаг 5. Решите, где живут база и данные
Управляемой базы здесь нет, поэтому путей два. Первый — поднять PostgreSQL рядом как второй сервис в своём docker-compose с именованным томом, иначе данные обнулятся при первом обновлении кода. Второй — взять внешнюю базу и передать строку подключения секретом в DATABASE_URL, которую Phoenix подхватывает без правок в коде:
DATABASE_URL=ecto://app:***@db:5432/app_prod
Миграции Ecto выполняйте при старте приложения: SSH-доступа в контейнер нет и терминала в панели тоже, запустить разовую команду негде. Стандартный модуль Release из mix phx.gen.release содержит функцию migrate/0 — вызовите её до старта приложения. Файлы, которые пишет приложение, держите в постоянной папке /data: всё рядом с кодом пересоздаётся при публикации.
Шаг 6. Загрузите код и получите ссылку
Проект попадает на платформу архивом или импортом репозитория GitHub, в том числе приватного. Ставить Erlang и Elixir у себя и собирать релиз руками не нужно — всё делается на нашей стороне. Логи и статус сборки видны в панели в реальном времени, поэтому ошибка проявляется сразу, а не пустой страницей. После запуска вы получаете публичную HTTPS-ссылку.
Сколько это стоит и как работают контейнеры
Принципиальная разница с VPS: ни администрировать сервер, ни следить за процессом не нужно — контейнеры работают постоянно, без ограничения по часам и без «засыпания». Упавший контейнер платформа перезапускает сама.
Starter — 990 ₽/мес. 1 vCPU, 2 GB RAM. Первый месяц бесплатный. Для одного Phoenix-приложения этого достаточно, лимита по времени работы у тарифа нет.
Pro — 2 490 ₽/мес. Для нескольких проектов — до пяти.
Team — 4 990 ₽/мес — когда команде нужен запас ресурсов и SLA.
Свой Dockerfile поддерживается, но не обязателен: стек платформа определяет по коду. Если он нужен, помните про PHX_SERVER=true.
Частые вопросы
Нужно ли запускать mix release перед загрузкой?
Нет, загружайте исходный код: зависимости устанавливаются и проект собирается на нашей стороне из mix.exs, а mix.lock фиксирует точные версии. Ни Erlang, ни Elixir на вашей машине не нужны. Если у вас есть свой Dockerfile со сборкой релиза, платформа использует его вместо автоматической сборки — и тогда особенно легко забыть про PHX_SERVER.
Phoenix запустился, но сайт не открывается — почему?
Чаще всего приложение слушает не там: порт жёстко прописан как 4000 вместо чтения из PORT, либо endpoint биндится на localhost вместо 0.0.0.0. Вторая частая причина — релиз без PHX_SERVER: приложение работает, а его веб-сервер выключен, и логи остаются чистыми. Третья — настройки читаются в config/prod.exs вместо config/runtime.exs.
Где взять SECRET_KEY_BASE и куда его положить?
Сгенерируйте значение командой mix phx.gen.secret на своей машине. Не вставляйте его в код и не кладите в конфиг, который уедет в репозиторий: передайте его платформе как секрет, и оно подставится в переменную окружения SECRET_KEY_BASE в зашифрованном виде.
Как подключить PostgreSQL и прогнать миграции Ecto?
Возьмите внешнюю базу или поднимите PostgreSQL вторым сервисом в своём docker-compose, а строку подключения передайте секретом в DATABASE_URL. Миграции выполняйте при старте приложения — например, через стандартный модуль Release с функцией migrate/0, который создают генераторы Phoenix. Другого места нет: SSH-доступа в контейнер и терминала в панели нет.
Работает ли LiveView?
Да. Постоянное соединение LiveView идёт через выданную ссылку, дополнительной настройки websocket не требуется. Так же запускается приложение без веб-части — например, очередь Oban или свой процесс с расписанием. Планировщика задач на платформе нет, поэтому расписание держите в приложении.