DocsRuby and Rails apps

Ruby and Rails apps

Install Ruby, deploy Rails, Sinatra or other Rack apps from Git with Bundler and Puma, precompile assets, connect a database and set environment variables.

Ruby applications run on ZoPanel as server apps: ZoPanel installs gems with Bundler, builds the release, starts the app as a service of the hosting account behind nginx and switches to new releases with no downtime. This page covers Ruby on Rails, Rack apps (Sinatra, Hanami…) and plain Ruby servers; ports, blue/green releases and logs work as described in Node.js and Python apps.

Install Ruby on the server

An administrator installs Ruby once:

  1. Open Runtimes.
  2. In the Ruby card ("Ruby with Bundler, for Rails and Sinatra."), click Install.

ZoPanel installs the distribution's Ruby with Bundler and the libraries native gems need:

ruby-full ruby-bundler build-essential libyaml-dev zlib1g-dev libssl-dev
libpq-dev libsqlite3-dev default-libmysqlclient-dev pkg-config

So gems such as pg, mysql2, sqlite3, psych and nokogiri compile without extra steps. The card then shows the installed version.

There is one Ruby per server, the one the operating system ships:

Operating system Ruby Newest Rails it runs
Ubuntu 22.04 3.0 Rails 7.1
Debian 12 3.1 Rails 7.2
Ubuntu 24.04 3.2 Rails 8
Debian 13 3.3 Rails 8

Rails 8.0 requires Ruby 3.2 or newer and Rails 7.2 requires Ruby 3.1 or newer (Rails upgrade guide). A ruby "x.y" line in the Gemfile must match the server's Ruby, otherwise Bundler stops. The Version field and the Gemfile do not select another Ruby. Every build log starts with the Ruby in use, for example "Using the system Ruby 3.2.3", and adds a warning when the Gemfile asks for another version. If your app needs another Ruby version, deploy it with a Dockerfile instead (see Deploy with Docker).

Deploy

  1. In Websites → New website, choose Git deploy, enter the Repository URL and Branch (and Root directory for monorepos), then click Create website. For an existing website, use its Deploy tab.
  2. ZoPanel detects Ruby from the Gemfile and proposes the commands below.
  3. Add your settings in Environment variables and click Save & redeploy.

Detected commands

Every Ruby app gets the same install command. Gems go into the release (vendor/bundle), never system-wide, and development and test groups are skipped:

bundle config set --local path vendor/bundle && bundle config set --local without 'development test' && bundle install --jobs 4
Detected as When Build command Start command
Ruby on Rails config/application.rb exists or the Gemfile lists rails RAILS_ENV=production SECRET_KEY_BASE=precompile bundle exec rails assets:precompile, only when app/assets/config/manifest.js or app/javascript exists RAILS_ENV=production bundle exec rails server -b 127.0.0.1 -p $PORT
Rack (Sinatra, Hanami…) config.ru exists – bundle exec rackup -o 127.0.0.1 -p $PORT -E production
Ruby anything else – bundle exec ruby app.rb -o 127.0.0.1 -p $PORT (check it)

For Rails, Persistent paths are set to storage and log, and the deploy log notes "Set SECRET_KEY_BASE and DATABASE_URL in the environment variables". A web: line in a Procfile always provides the start command instead.

To change a command, turn Auto-detect off in the Build & run card, edit it and click Save & deploy.

Puma

rails server starts Puma, the default server of new Rails apps (gem "puma" in the Gemfile). The -b and -p options bind it to 127.0.0.1:$PORT. Puma's thread count comes from config/puma.rb (RAILS_MAX_THREADS, 3 by default in Rails 8). To start Puma directly, use for example:

bundle exec puma -C config/puma.rb -b tcp://127.0.0.1:$PORT -e production

For Rack apps on Rack 3 (Sinatra 4 and later), the rackup command is a separate gem: add gem "rackup" and gem "puma" to the Gemfile (Sinatra README).

Environment variables

Set these in Environment variables on the Deploy tab. They are available during the build and at runtime; PORT is set automatically.

Variable Value
SECRET_KEY_BASE A long random secret, for example the output of bin/rails secret or openssl rand -hex 64. Rails uses it instead of the one in encrypted credentials.
RAILS_MASTER_KEY Only if the app reads other values from config/credentials.yml.enc.
DATABASE_URL Connection string (see below).
RAILS_LOG_TO_STDOUT 1 for apps older than Rails 7.1, so logs appear in Application output. Rails 7.1 and later log to stdout by default.
SOLID_QUEUE_IN_PUMA 1 to run Solid Queue jobs inside Puma (Rails 8 default config/puma.rb).

The build command sets a placeholder SECRET_KEY_BASE for assets:precompile only, so the precompile does not need your real secret.

Database

SQLite (Rails 8 default). New Rails 8 apps keep their production databases in storage/ (storage/production.sqlite3 and the cache, queue and cable databases). storage is a persistent path, so the data survives every deploy. Nothing else is needed besides migrations.

MariaDB or PostgreSQL. Create a database under Databases and set DATABASE_URL; the host is 127.0.0.1 (Rails database configuration):

# MariaDB with the mysql2 gem
DATABASE_URL=mysql2://alice_app:your-password@127.0.0.1:3306/alice_app
# MariaDB with the trilogy gem (Rails 7.1+)
DATABASE_URL=trilogy://alice_app:your-password@127.0.0.1:3306/alice_app
# PostgreSQL with the pg gem
DATABASE_URL=postgresql://alice_app:your-password@127.0.0.1:5432/alice_app

URL-encode special characters in the password (for example @ as %40).

Migrations

Migrations do not run by themselves. Turn Auto-detect off and append them to the build command:

RAILS_ENV=production SECRET_KEY_BASE=precompile bundle exec rails assets:precompile && RAILS_ENV=production bundle exec rails db:prepare

db:prepare creates missing databases and runs pending migrations. The build runs before the new release goes live, while the old one keeps serving, so write migrations the running release can tolerate.

SSL, static files and uploads

  • nginx terminates SSL and sends X-Forwarded-Proto. Rails 8 sets config.assume_ssl and config.force_ssl in production, and Rails 7.1 sets config.force_ssl: keep Free SSL (Let's Encrypt) on, otherwise secure cookies and redirects break sign-in over plain HTTP.
  • nginx forwards every request to the app, including files in public/. Rails 7.1 and later serve them by default; for Rails 7.0 set RAILS_SERVE_STATIC_FILES=1.
  • Active Storage's local disk writes to storage/, which is persistent. Add any other folder that must survive a release, such as public/uploads, to Persistent paths.

Releases, logs and restarts

  • A new release starts in the idle slot and must answer the Health check path (default /) with a status below 500 within 90 seconds; redirects count as healthy. Rails 7.1 and later include a /up route you can use. Only then does nginx switch over and the old process stop.
  • Releases keeps the last 5 builds for Rollback (the database is not rolled back).
  • Application output shows the last 400 lines the app writes; turn on Live to follow it. The log/ folder is persistent too.
  • The service restarts automatically if Puma exits. Memory and CPU count toward the account's package.

Troubleshooting

Message or symptom What to do
bundle: command not found in the install step Ask the administrator to install Ruby under Runtimes.
"Your Ruby version is …, but your Gemfile specified …" The Gemfile asks for another Ruby than the server's (the build log warns about it before the install). Relax the ruby line (for example ruby "~> 3.2", or remove it), or deploy with a Dockerfile.
A gem fails to compile Read the build log in Tasks; the usual headers are installed with Ruby. Other native libraries must be installed by the administrator.
"the app did not respond on port … within 90s (it must listen on $PORT)" Bind to 127.0.0.1:$PORT (-b 127.0.0.1 -p $PORT).
"the app exited during startup" Read Application output: often SECRET_KEY_BASE or DATABASE_URL is missing, or a migration has not run.
ActiveRecord::PendingMigrationError Add bundle exec rails db:prepare to the build command (see Migrations).
Sign-in loops or cookies are lost The site runs without SSL while Rails forces SSL. Issue the certificate (see SSL certificates).
The repository is detected as Node.js A package.json at the root (jsbundling, cssbundling) takes priority over the Gemfile. Turn Auto-detect off, set Runtime to ruby, Type to Server app (process) and enter the Rails commands above; put npm ci && in front of the build command if assets need Node.js.

← Go apps Java apps →