Сайт на Ruby on Rails выкладывается в сеть без аренды сервера: вы отдаёте исходники с Gemfile и Gemfile.lock, платформа определяет Ruby, ставит гемы, собирает ассеты и запускает приложение. Условий всего три, но они решающие: порт приложение берёт из переменной PORT и слушает 0.0.0.0, а не localhost:3000; ключ, которым Rails расшифровывает собственные credentials, лежит в переменной окружения, а не в файле config/master.key; база данных живёт в постоянном хранилище или приходит извне по DATABASE_URL. Ниже — путь от папки с проектом до рабочей HTTPS-ссылки на Dockbox и места, где Rails-проекты спотыкаются чаще всего.
Почему Rails по умолчанию тащат на VPS
Привычный сценарий — это длинный чек-лист: арендовать сервер, поставить нужную версию Ruby, пройти bundle install, настроить Puma, поставить перед ним nginx, выпустить сертификат, поднять базу, не забыть про assets:precompile и придумать, где безопасно хранить ключ, которым Rails расшифровывает свои же credentials.
Версия Ruby и гемы. Проект пиннит версии в Gemfile.lock, и на чужой машине без этой версии Ruby сборка падает — при том что локально всё работало.
Обратный прокси и сертификат. Без nginx приложение не отдаёт HTTPS, а сертификат нужно продлевать каждые три месяца и легко проспать.
Автозапуск и перезапуск. Чтобы приложение вернулось в строй после падения и перезагрузки, нужен systemd-юнит или супервизор — и его отладка.
Оплата сервера независимо от нагрузки. Машина стоит денег все 30 дней, даже если сайт открывают раз в сутки.
Платформа деплоя снимает эту часть: она видит Gemfile, ставит зависимости, собирает проект и отдаёт публичную HTTPS-ссылку. Ни сервера, ни nginx, ни ручного сертификата.
Шаг 1. Gemfile и Gemfile.lock — в корне проекта
Оба файла должны лежать в корне: по ним платформа понимает, что перед ней Rails-приложение, и по ним же фиксирует точные версии гемов. Gemfile.lock удалять нельзя и не стоит добавлять его в .gitignore — без него версии расходятся, и сборка ломается, хотя локально всё работало. Установленные гемы тащить не нужно: папку vendor/bundle в архив не кладите.
Шаг 2. Слушайте порт из PORT, а не 3000
Приложение должно слушать порт из переменной окружения PORT и биндиться на 0.0.0.0, а не на привычный localhost:3000. Жёстко прописанный порт — причина номер один, по которой ссылка не открывается: приложение работает, но не там, куда приходит трафик.
Плохой вариант:
port 3000
bind "tcp://127.0.0.1:3000"
Штатный config/puma.rb в новых версиях Rails уже читает порт из окружения, поэтому чаще всего правильное действие — ничего не менять и не переопределять:
port ENV.fetch("PORT", 3000)
bind "tcp://0.0.0.0:#{ENV.fetch("PORT", 3000)}"
Шаг 3. Ключ приложения — в переменные окружения
Файл config/master.key и всё, что он расшифровывает, — это секрет: он не должен попасть ни в архив, ни в репозиторий. Скопируйте содержимое master.key в значение RAILS_MASTER_KEY в настройках проекта. Если вы не используете зашифрованные credentials — задайте SECRET_KEY_BASE: без одного из этих двух значений Rails отказывается стартовать в production. Там же выставьте RAILS_ENV=production, чтобы приложение работало в боевом режиме. Платформа хранит эти значения в зашифрованном виде и подставляет их переменными окружения, так что Rails читает их обычным способом.
Шаг 4. Не везите ассеты и vendor/bundle
Ассеты компилируются на нашей стороне при публикации, поэтому собирать их локально и загружать результат незачем. Оставьте за бортом vendor/bundle, node_modules, содержимое tmp и log, а также ранее скомпилированные файлы в public/assets: они раздувают загрузку и могут заслонить свежую сборку устаревшей. В репозитории на GitHub эти папки обычно уже перечислены в .gitignore, так что добавлять ничего не придётся.
Шаг 5. Решите, где живёт база данных
Управляемой базы отдельным сервисом здесь нет, поэтому вариантов два. Первый: поднять базу рядом с приложением как свой сервис в docker-compose и дать ей именованный том — иначе данные обнулятся при первом же обновлении кода. Второй: взять внешнюю базу и указать строку подключения как DATABASE_URL, которую Rails подхватывает без правок config/database.yml.
DATABASE_URL=postgres://app:***@db:5432/app_production
SQLite годится только для небольшого проекта и только если файл базы лежит в постоянной папке /data. Файл рядом с кодом пересоздаётся при каждой публикации, и все записи в нём теряются. У проектов с собственным docker-compose папки /data нет — там постоянное хранилище задаётся именованным томом в вашем файле.
Шаг 6. Загрузите код и прогоните миграции
Проект попадает на платформу архивом или импортом репозитория GitHub, включая приватный. Терминала внутри проекта нет, поэтому миграции должны выполняться сами при старте: положите в корень Procfile — одну строку, которая говорит, как поднимать приложение, — и вызовите в ней rails db:migrate до запуска сервера.
web: bundle exec rails db:migrate && bundle exec puma -C config/puma.rb
Логи сборки и запуска видны в панели в реальном времени, поэтому ошибка проявляется сразу, а не пустой страницей, и после успешного деплоя сайт открывается по своему HTTPS-адресу.
Сколько это стоит и как работают контейнеры
Принципиальная разница с VPS: ни администрировать сервер, ни следить за процессом не нужно — контейнеры работают постоянно, без ограничения по часам и без «засыпания». Упавший контейнер платформа перезапускает сама, и приложение возвращается в строй без вашего участия.
Starter — 990 ₽/мес. 1 vCPU, 2 GB RAM, 20 GB SSD. Первый месяц бесплатный. Для одного Rails-приложения этого достаточно, и лимита по времени работы у тарифа нет.
Pro — 2 490 ₽/мес. 2 vCPU, 4 GB RAM, до пяти проектов.
Team — 4 990 ₽/мес — для команд, которым нужен запас ресурсов и SLA.
Частые вопросы
Куда девать master.key, если его нельзя хранить в репозитории?
Откройте config/master.key, скопируйте единственную строку внутри и укажите её в настройках проекта как значение RAILS_MASTER_KEY. Значение хранится зашифрованным и подставляется переменной окружения, а сам файл остаётся за пределами архива и репозитория. Если вы не используете зашифрованные credentials, задайте SECRET_KEY_BASE — без одного из двух значений Rails в production не запустится.
Нужно ли собирать ассеты и загружать public/assets?
Нет. Ассеты компилируются на нашей стороне при сборке проекта, готовить их локально нечего. Старую скомпилированную папку лучше убрать из архива: она утяжеляет загрузку и может заслонить свежую сборку предыдущей версией. Если у вас нестандартная сборка ассетов, вызовите её из своей команды запуска в Procfile до старта сервера.
Как прогнать миграции после деплоя?
Терминала внутри проекта нет, поэтому миграции выполняются автоматически при старте приложения. Проще всего — Procfile в корне, где перед запуском сервера вызывается rails db:migrate: так таблицы создаются и обновляются на каждом деплое. Второй вариант — запускать миграции из собственного стартового кода приложения.
Можно ли остаться на SQLite вместо PostgreSQL?
Можно, если проект небольшой и файл базы лежит в постоянной папке /data. Файл рядом с кодом пересоздаётся при каждом деплое, и все записи в нём теряются. Учтите, что у проектов с собственным docker-compose папки /data нет — там постоянное хранилище задаётся именованным томом в вашем файле. Для всего, что будет расти, начинайте с PostgreSQL отдельным сервисом или с внешней базы.
Сайт отдаёт 500 сразу после деплоя — почему?
Обычно не хватает ключа приложения (RAILS_MASTER_KEY или SECRET_KEY_BASE) либо не прошли миграции: приложение поднимается и падает на первом же запросе к базе. Настоящая причина видна в логах панели — там реальная ошибка Rails, а не общая страница. Третья частая причина — приложение слушает свой порт вместо того, что приходит в PORT.