Docs menu

Deploying Rails

Deploy a Ruby on Rails app on Primcloud: Procfile, RAILS_MASTER_KEY, DATABASE_URL, migrations, and asset compilation.

On this page

Rails apps deploy on Primcloud through the standard Ruby build. Primcloud detects Rails from the railties gem in Gemfile.lock, generates a default web process that runs bin/rails server, and runs rake assets:precompile during the build. This page covers what Rails needs on top of the language defaults: environment variables, migrations, asset gotchas.

Starter template#

Use Heroku's heroku/ruby-rails-sample as a working reference. It's a small Rails app that boots on Primcloud with no changes beyond setting RAILS_MASTER_KEY and pointing at a PostgreSQL database.

If you're bootstrapping a new app from scratch, rails new produces a repo that deploys as-is, with two adjustments:

  1. Use PostgreSQL (gem 'pg'), not SQLite. Primcloud's filesystem is ephemeral, so SQLite data doesn't survive a pod restart.
  2. Commit Gemfile.lock. Some .gitignore templates exclude it; don't.

Procfile for Rails on Primcloud#

You don't strictly need a Procfile. Primcloud auto-generates a web process for Rails apps:

web: bin/rails server --binding "[::]" --port "$PORT" --environment "$RAILS_ENV"

Ship a Procfile when you need more than just web: background workers, schedulers, release commands. A minimal Rails app with Sidekiq:

Procfile
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq

If you write your own web line, Primcloud uses it verbatim, and the generated default is discarded. Bind to $PORT explicitly, and remember to enable IPv6 binding ("[::]") if you want IPv6 support.

Puma config. Rails 7+ generates a config/puma.rb that reads workers and threads from WEB_CONCURRENCY and RAILS_MAX_THREADS. Both are worth tuning per compute size. Start with WEB_CONCURRENCY=2 and RAILS_MAX_THREADS=5 on Standard 1x.

Release-time migrations. Rails migrations don't run automatically. Options:

  • Ship a release process type in your Procfile: release: bin/rails db:migrate. This runs after the build succeeds, before the new pods take traffic, and the deploy fails if migrations fail.
  • Or run migrations manually from the Tasks feature after a deploy.

Env vars this framework expects#

Set these in the Secrets panel. Primcloud provides defaults for the ones it can; the rest are on you.

Provided by Primcloud (override in the panel to change):

Variable Default Notes
RAILS_ENV production Which Rails environment to boot.
RACK_ENV production Rack fallback.
RAILS_SERVE_STATIC_FILES enabled Puma serves public/ files.
RAILS_LOG_TO_STDOUT enabled Logs stream to the Logs page.
SECRET_KEY_BASE Random, cached per app Rails session/cookie signing key.

You must set (Rails won't boot without these when your app relies on them):

  • DATABASE_URL: connection string for your PostgreSQL database. Format: postgres://user:pass@host:5432/dbname.
  • RAILS_MASTER_KEY: the 32-byte hex value from config/master.key (or config/credentials/production.key). Required if your app reads Rails.application.credentials, which every fresh Rails app does. Never commit master.key to your repo.

Commonly set (depending on the app):

  • REDIS_URL: if you're using Sidekiq, ActionCable's Redis adapter, or a Redis-backed cache. Format: redis://user:pass@host:6379/0.
  • WEB_CONCURRENCY: number of Puma worker processes. Start with 2 on Standard 1x.
  • RAILS_MAX_THREADS: threads per Puma worker. Default 5 from the Rails template.

Framework gotchas on Primcloud#

SQLite doesn't work for production. Primcloud's filesystem is ephemeral: files written to disk don't survive a pod restart, and different replicas each have their own filesystem. Use PostgreSQL. rails new --database=postgresql from the start; if you're on SQLite already, swap the adapter and re-run bundle install.

Assets precompile at build time. rake assets:precompile runs during the build, not on the running pod. Anything the compile step needs (encrypted credentials, database access, external services) must be available then. This is why RAILS_MASTER_KEY needs to be set: Rails initializes the app during precompile, and initialization reads credentials.

If your compile step tries to reach the database (config.eager_load = true combined with certain initializers), it will fail at build time unless DATABASE_URL is also reachable. Options:

  • Set SECRET_KEY_BASE_DUMMY=1 in the panel (Rails 7.1+). Rails uses a placeholder key during precompile so credentials access doesn't fail the build.
  • Restructure the code so DB access only happens at request time, not during initialization.

Node/Yarn for asset pipelines. If your app uses jsbundling-rails (esbuild, Rollup, Webpack) or cssbundling-rails, commit a package.json (and a yarn.lock if you use Yarn). The build detects them and installs Node.js so the JS toolchain is available during rake assets:precompile. If you bundle with Bun, commit bun.lock instead and Bun is installed for the asset build.

No Rails cache warmer runs. Nothing preloads Rails' cache after deploy. Rails will lazy-load classes on the first few requests, which can spike latency. Enable config.eager_load = true in config/environments/production.rb (Rails does this by default in newer versions) so classes load at boot instead.

Solid Cable / Solid Cache / Solid Queue. Rails 8's default adapters use PostgreSQL for background jobs, cable, and cache. They work on Primcloud; just make sure the tables exist. bin/rails db:migrate creates them from the generated migrations.

Active Storage. Local disk storage doesn't survive pod restarts. Configure config/storage.yml to use S3, GCS, or another remote backend, and set the corresponding credentials in the Secrets panel.

Troubleshooting#

ActiveRecord::PendingMigrationError. Migrations weren't run before the app started. Add a release: bin/rails db:migrate line to your Procfile, or run migrations from the Tasks feature before your next deploy.

ActiveSupport::MessageEncryptor::InvalidMessage. RAILS_MASTER_KEY is missing or wrong. Copy the value from your local config/master.key into the Secrets panel exactly (no trailing newline).

SECRET_KEY_BASE missing in production. You explicitly unset it, and Rails now can't boot. Delete the override in the Secrets panel. Primcloud falls back to its cached random default.

Assets 404 in production. Either RAILS_SERVE_STATIC_FILES was disabled, or rake assets:precompile didn't run. Check the build log for the precompile step's output; check the env panel for RAILS_SERVE_STATIC_FILES=enabled.

"Not found: /rails/info/routes" or "Blocked host" in production. Rails' config.hosts allowlist rejects requests whose Host header isn't in the list. Add your Primcloud domain and any custom domain:

config/environments/production.rb
config.hosts << ENV.fetch("APP_HOSTNAME", "your-app.primcloud.app")
config.hosts << "yourdomain.com"

Or, if you're behind trusted infrastructure, config.hosts.clear disables the check entirely.