Out Plane
Frameworks

Deploy Ruby on Rails

Deploy a Ruby on Rails 8 app on Out Plane with its generated Dockerfile and Thruster, connect managed PostgreSQL, and prepare the database on boot.

This guide deploys a Rails 8 application with the Dockerfile that rails new generates, and connects it to a managed PostgreSQL database. For the shared deploy flow, see Deploy from GitHub.

Prepare Your App

Use PostgreSQL

rails new uses SQLite by default, and the container filesystem is reset on every redeploy. Create the app with PostgreSQL:

rails new myapp --database=postgresql

Or switch an existing app:

bin/rails db:system:change --to=postgresql
bundle install

db:system:change updates config/database.yml, the Gemfile, and the database packages in the Dockerfile. Commit the updated Gemfile.lock too, because the image installs gems in deployment mode.

The Generated Dockerfile

Rails 8 writes a production Dockerfile and bin/docker-entrypoint into your app. Deploy them as they are:

  • The build installs your gems and precompiles assets without your master key, so it needs no build variables.
  • The image sets RAILS_ENV=production and runs the app as a regular user, not root.
  • The entrypoint runs ./bin/rails db:prepare before the server starts.
  • Puma runs behind Thruster. Thruster listens on port 80 on all interfaces and passes requests to Puma on port 3000.

An app created with --skip-thruster runs Puma directly on port 3000. Rails binds 0.0.0.0 in production, so that works too.

Connect a Database

Create a managed PostgreSQL database before you deploy, because the app prepares its databases as it boots. For PostgreSQL, Rails 8 generates four production databases: the primary one, plus cache, queue, and cable for Solid Cache, Solid Queue, and Solid Cable.

In the database's Access tab, create a logical database for each, such as app, app-cache, app-queue, and app-cable. Then copy a connection string for each from the Connection panel into these environment variables:

VariableDatabase
DATABASE_URLPrimary
CACHE_DATABASE_URLSolid Cache
QUEUE_DATABASE_URLSolid Queue
CABLE_DATABASE_URLSolid Cable

Rails merges each one into the matching entry under production: in config/database.yml, so the file needs no edits. Keep the four databases separate: db:prepare loads a schema only into a database Rails has not set up yet, so a shared database would miss the Solid tables. Connections require SSL, so make sure each string includes sslmode=require.

If your config/database.yml has a single production database, set only DATABASE_URL.

Deploy

Create an application from your repository with the Dockerfile build method. Out Plane previews the Dockerfile and prefills port 80 from its EXPOSE line, or 3000 for an app created with --skip-thruster. Keep that port public, with the HTTP protocol.

Leave the start command empty, so the image runs its entrypoint and Thruster. Add the variables from Environment Variables, then select Deploy app.

Run Migrations

Out Plane has no pre deploy hook, and a Rails 8 app does not need one. The entrypoint runs ./bin/rails db:prepare every time the server starts. On the first deploy it loads db/schema.rb and the Solid schemas into the new databases and runs db/seeds.rb. After that, each deploy runs your pending migrations.

A start command replaces the image's entrypoint as well, so with one set, db:prepare no longer runs.

If you run more than one instance, each one runs db:prepare as it starts. Rails holds a PostgreSQL advisory lock while it migrates, so instances do not run the same migration twice. Make the first deploy with one instance, because loading a schema into a new database takes no lock.

Use the browser terminal for one off commands, such as ./bin/rails console.

Environment Variables

VariableNotes
RAILS_MASTER_KEYThe contents of config/master.key. Git ignores that file, and without it Rails cannot read its credentials, including secret_key_base.
DATABASE_URLPlus the three Solid URLs. See Connect a Database.
SOLID_QUEUE_IN_PUMAtrue runs Solid Queue inside Puma, so the web app also processes background jobs.

RAILS_ENV is already production in the image. If you do not use encrypted credentials, set SECRET_KEY_BASE instead of RAILS_MASTER_KEY.

Production Notes

  • Without SOLID_QUEUE_IN_PUMA, jobs are queued but never run. To run them in their own app instead, deploy the same repository again with no ports and the start command ./bin/jobs, and share the variables with an environment variable group.
  • Thruster ignores PORT. To listen on another port, set HTTP_PORT and expose the same port.
  • Rails 8 logs to standard output in production, so requests and errors appear in the runtime logs.
  • If you turn on config.hosts, include your app's URL, such as {name}-{port}-{team}.outplane.app, and any custom domain. Rails blocks every other host.
  • Active Storage saves uploads to local disk by default, which is reset on every redeploy. Store them in an S3 compatible bucket with the S3 service in config/storage.yml, with the bucket's endpoint, region: auto, and force_path_style: false.

Next Steps

On this page