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=postgresqlOr switch an existing app:
bin/rails db:system:change --to=postgresql
bundle installdb: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=productionand runs the app as a regular user, not root. - The entrypoint runs
./bin/rails db:preparebefore the server starts. - Puma runs behind Thruster. Thruster listens on port
80on all interfaces and passes requests to Puma on port3000.
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:
| Variable | Database |
|---|---|
DATABASE_URL | Primary |
CACHE_DATABASE_URL | Solid Cache |
QUEUE_DATABASE_URL | Solid Queue |
CABLE_DATABASE_URL | Solid 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
| Variable | Notes |
|---|---|
RAILS_MASTER_KEY | The contents of config/master.key. Git ignores that file, and without it Rails cannot read its credentials, including secret_key_base. |
DATABASE_URL | Plus the three Solid URLs. See Connect a Database. |
SOLID_QUEUE_IN_PUMA | true 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, setHTTP_PORTand 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
S3service inconfig/storage.yml, with the bucket'sendpoint,region: auto, andforce_path_style: false.
Next Steps
Deploy PHP / Laravel
Deploy a Laravel or PHP app on Out Plane with FrankenPHP, Octane, or Nginx, connect it to managed PostgreSQL, and run artisan migrations.
Deploy Spring Boot
Deploy a Spring Boot app on Out Plane, bind it to the port you expose, connect it to managed PostgreSQL, and run Flyway or Liquibase migrations.