Chajio Cloud
TutorialApp Hosting

How to Deploy a Ruby on Rails App (Rails 8)

Rails runs well on Chajio Cloud: your gems are installed, your assets precompiled and Puma started on two copies, next to a managed PostgreSQL database. A fresh Rails 8 app needs four small changes first, because it expects four databases and a worker process where the platform gives you one database and a web process. This guide makes each change, explains why, and shows what a healthy first deploy looks like.

CChajio Cloud team November 13, 2026 11 min read
On this page

Before you start

  • A Chajio Cloud App Hosting plan. The Free plan is enough to follow along. Once you order, you get an email with your sign-in details, and the first time you sign in you choose your own password.
  • Your code on GitHub, in a repository you own or can access. A public repository you don't own works too.
  • A Rails app that uses PostgreSQL, such as one made with rails new myapp --database=postgresql, with Gemfile.lock committed. SQLite, the rails new default, can’t keep data here, as the last section explains.

Step 1: Get your app ready

Delete the generated Dockerfile

rails new adds a Dockerfile. When a repository has one, the platform builds it as written, and this one is slow: in our tests it took 35.6 minutes with a build machine to itself, and twice ran past the 45-minute build limit while other builds were running. Without it, the platform builds Rails itself in about six minutes, and the app behaves the same.

git rm Dockerfile

If you keep it anyway, the changes below still apply, except the Procfile and the Ruby version: the Dockerfile’s entrypoint runs db:prepare on every start, and its first line picks the Ruby version.

Add a Procfile

A file named Procfile, no extension, in the root of your repository says how to start your app. This one prepares the database, running any new migrations, then starts Puma on the platform’s port:

Procfile
web: bin/rails db:prepare && bin/rails server --port "$PORT"

Without a Procfile your app still starts, but nothing runs your migrations, and its tables go missing.

Pin your Ruby version

The build reads the Ruby version from Gemfile.lock, not from .ruby-version. Point your Gemfile at the file rails new created:

Gemfile
source "https://rubygems.org"

ruby file: ".ruby-version"

Then run bundle lock and commit Gemfile.lock. It gains a RUBY VERSION section, and the build log confirms it with Ruby version 3.4.12 from Gemfile.lock. Without it, your app builds on a default Ruby that may not be the one you develop on.

Use one database

In production, a new Rails 8 app expects four PostgreSQL databases: one for your data and one each for Solid Cache, Solid Queue and Solid Cable. A managed database is one database, owned by your app’s user, who can’t create others. Point production at it through DATABASE_URL, which the platform sets when you add the database:

config/database.yml: replace the whole production section
production:
  <<: *default
  url: <%= ENV["DATABASE_URL"] %>

Move Solid Cache, Queue and Cable into it

This is the single-database setup the Solid gems document. Each of them ships a schema file, db/cache_schema.rb, db/queue_schema.rb and db/cable_schema.rb. Turn each one into a migration:

bin/rails generate migration CreateSolidCacheTables
bin/rails generate migration CreateSolidQueueTables
bin/rails generate migration CreateSolidCableTables
db/migrate/…_create_solid_cache_tables.rb (the same for queue and cable)
class CreateSolidCacheTables < ActiveRecord::Migration[8.1]
  def change
    # Paste everything between `ActiveRecord::Schema[…].define(version: 1) do`
    # and the final `end` of db/cache_schema.rb here.
  end
end

Delete the three schema files once their contents are in migrations, and run bin/rails db:migrate on your computer to check the migrations apply. Then remove the three settings that send each gem to its own database:

Remove these lines
# config/environments/production.rb
config.solid_queue.connects_to = { database: { writing: :queue } }

# config/cache.yml, under production:
database: cache

# config/cable.yml, under production:
connects_to:
  database:
    writing: cable

Commit and push. The last two changes, a secret key and the job runner, are settings rather than code, and you make them in Step 5.

Step 2: Create a project

A project is the home for one app, along with its database and domain. In your dashboard, open Projects and click New project:

Name
Lowercase letters, numbers and hyphens, up to 24 characters. It becomes part of your app’s free web address and can’t be changed later, so pick something short, like shop or api.
Plan
The plan the project draws its resources from.
Resources
CPU, memory and storage for this project, pre-filled with what your plan has left. The defaults suit most apps.

Click Create project. Setting it up takes about a minute, and you land on the deploy guide straight away, which walks you through three short steps: Source, Configure and Deploy. You can pick your repository while the project finishes setting up.

Step 3: Add a PostgreSQL database

Your app needs its database from the very first start, so create it before you deploy. Open the project’s Databases tab, click Add database, choose PostgreSQL, and enter DATABASE_URL under Connect your app. It takes two to five minutes to set up; wait until its card shows it is ready, then click Deploy at the top of the project to carry on. The full walkthrough is in How to Add a Database to Your App.

Step 4: Connect your repository

On the Source step, choose where your code comes from:

Your repository
Click Connect GitHub and approve access, which lets Chajio Cloud read your code and hear about your pushes. Then choose the repository and the branch to deploy, usually main. From now on, every push to that branch deploys automatically.
Public repository
Paste the URL of any public GitHub repository, no GitHub sign-in needed. Since you don’t own it, pushes can’t trigger deploys: deploy by hand, or tick Auto-deploy new releases to follow the project’s published releases.

If your app lives in a subfolder of the repository, as in a monorepo, set Build context to that folder, for example apps/web. Otherwise leave it as ., the repository root. Then click Connect repository and Continue.

Step 5: Configure

The Configure step has two runtime settings, both changeable later without a rebuild, and your environment variables.

Container port
Leave it at 3000. Your Procfile starts Rails on PORT, so it follows whatever you set here.
Health check path
/up, the health route every new Rails app has. This one matters: a new app has no home page in production, so the default, /, returns 404 and the deploy never becomes ready.

Add two variables:

SECRET_KEY_BASE=paste-the-output-of-bin/rails-secret
SOLID_QUEUE_IN_PUMA=true
  • SECRET_KEY_BASE signs your app’s cookies. Generate one with bin/rails secret. Setting RAILS_MASTER_KEY to the contents of config/master.key works instead. Set one of them yourself, so the key stays the same from one deploy to the next.
  • SOLID_QUEUE_IN_PUMA runs your background jobs inside the web server. Apps here have a web process and no separate worker, so without it jobs are queued and nothing ever runs them.

DATABASE_URL is already listed, locked, because the platform manages it for you.

Variables live on the project's Environment tab. Add each one as a name and a value, then click Save & restart: your app restarts with the new values in moments, no rebuild needed.

Your build receives the variables too. A Dockerfile gets the ones it declares with an ARG line, and an auto-detected build gets all of them. A variable that went into your current build is marked Build. Change one of those and the dashboard offers to Redeploy, because only a new build picks up the new value.

PORT is set by the platform from the container port, so you can't set it yourself. If you attach a database, its connection variable appears here too, locked, because the platform manages it for you.

Click Save and continue.

Step 6: Deploy

The Deploy step shows a summary of the repository, branch and build context. Click Deploy now and the build log starts streaming: your code is fetched, dependencies are installed and your app is built and started. The deployment moves through Queued, Building and Deploying to Success.

The first build takes a few minutes. Later builds reuse the parts that didn't change, so they are usually quicker. When it finishes, your app is live on a free live.chajio.cloud address with HTTPS already switched on. You'll find the address on the project's Overview.

If the deployment fails, the dashboard names the stage that broke and why, and the build log shows the details. Our troubleshooting guide covers the usual causes.

What a healthy first deploy looks like

The build log shows your gems installing and your assets compiling, then the start command it found:

Build log, shortened
## Ruby
- Ruby version `3.4.12` from `Gemfile.lock`
- Bundler version `2.6.9` from `Gemfile.lock`
- Bundle install gems
  - Running `BUNDLE_WITHOUT="development:test" bundle install`
- Default process detection
  - Detected rails app (`rails` gem found)
- Rake assets install
  - Running `rake assets:precompile assets:clean --trace`
## Procfile
- Processes from `Procfile`
  - web: `bin/rails db:prepare && bin/rails server --port "$PORT"`

Then both copies start. In our tests they took 20 to 30 seconds to answer /up, migrations included, and used 170 to 285 MB of memory each with background jobs running in Puma, so allow at least 512 MB per copy.

One crash on the first deploy is normal

Both copies run db:prepare on the empty database at the same moment, and the one that loses stops with ActiveRecord::ConcurrentMigrationError, or with PG::UniqueViolation … schema_migrations_pkey if db/schema.rb is committed. It restarts by itself and was ready about 20 seconds later in our tests. The deploy still succeeds: the platform waits up to five minutes for both copies. Later deploys replace one copy at a time, so it doesn’t happen again.

You don’t need force_ssl or assume_ssl. HTTPS is handled before requests reach your app, and Rails already sees them as secure: request.ssl? is true and request.remote_ip is the visitor’s address. Leave both commented out, as rails new left them.

Connect your domain

Your app already works on its free address. To serve it from your own domain, open the project's Domains tab:

  1. Enter your domain, for example www.example.com, and click Add domain.
  2. The dashboard shows two DNS records to add where your domain's DNS is managed. A TXT record proves the domain is yours. A CNAME record routes visitors to your app, or an A or ALIAS record for a root domain like example.com. Copy the values exactly as shown.
  3. Click Verify. Once both records check out, a free HTTPS certificate is issued automatically, usually in under a minute, and your app is live on your domain.

DNS changes can take a while to spread, so if Verify doesn't find a record yet, give it a few minutes; the dashboard also re-checks on its own. The full walkthrough, including root domains and Cloudflare, is in How to connect your domain to your app.

If you turn on config.hosts in config/environments/production.rb, list your domain and your free address, and keep /up excluded, as the commented host_authorization line does. The health check isn’t addressed to your domain, so a strict host list would otherwise turn it away.

After launch

  • Every push deploys. Push to your branch and a new deployment starts on its own. Your current version keeps serving visitors until the new one passes its health check, so updates don't cause downtime. You can also deploy by hand from the Deployments tab.
  • Roll back in seconds. If a release misbehaves, open Deployments and click Rollback to this version on an earlier successful deployment. Nothing is rebuilt; the previous version simply comes back.
  • Watch it run. The Logs tab streams your app's output live, with secrets hidden. The Overview shows your app's status and address, with Redeploy, Restart and Stop buttons.

Your app runs as two copies

Every plan runs two copies of your app, so a crash or an update never takes it offline. That has one consequence worth designing for: anything an app keeps only in its own memory or on its own disk isn't shared with the other copy, and it's gone after a redeploy.

Sessions in cookies, the Rails default, already work across both copies. Store Active Storage uploads on an S3-compatible service rather than the local disk, which is replaced on every deploy.

The same goes for SQLite: its database is a file inside the app, so it would be lost on every deploy, and each copy would have its own. Keep your data in the managed PostgreSQL database.

Common problems

The app crashes at start with a socket error

ActiveRecord::ConnectionNotEstablished: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory

The cache, queue or cable database still has its own entry in database.yml, so it has no address. Replace the production section and move the Solid gems into one database, as in Step 1.

The app crashes at start asking for a secret

ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `bin/rails credentials:edit`

Set SECRET_KEY_BASE or RAILS_MASTER_KEY on the Environment tab.

The deploy never becomes healthy, and the logs say No route matches [GET] "/"

The health check is asking for a home page your app doesn’t have. Set Health check path to /up.

The first deploy logs ConcurrentMigrationError or schema_migrations_pkey

Both copies set up the database at once. The one that lost restarts by itself and the deploy still succeeds, so there is nothing to do.

The build stops at 45 minutes

It is building the Dockerfile rails new generated. Delete it, add the Procfile and the ruby line in your Gemfile, and push again.

Background jobs are queued but never run

Set SOLID_QUEUE_IN_PUMA=true on the Environment tab so Solid Queue runs inside Puma.

Tables are missing after a deploy

Nothing ran your migrations. Add the Procfile from Step 1, which runs db:prepare before Puma starts.

For anything else, Deployment Failed? How to Find and Fix the Cause explains how to read a failed deployment. Plans and a summary of everything here are on the Ruby on Rails hosting page.

Keep reading

Host your Ruby on Rails app on Chajio Cloud

Free for your first month, with a managed database and two replicas of your app on every plan.

See Ruby on Rails hosting