Deploying Ruby on Rails on Vessl
Vessl detects Rails via Gemfile and config/application.rb, builds with Nixpacks, and auto-detects bundle exec rails db:migrate as the post-deploy command when a database is attached.
Prerequisites
- A server added and provisioned in Vessl
- A GitHub repository with your Rails app
Gemfileandconfig/application.rbat the repository root
What Vessl auto-detects
When Vessl finds Gemfile + config/application.rb it sets:
| Setting | Auto-filled value |
|---|---|
| Build engine | Nixpacks |
| Port | 3000 |
| Post-deploy command | bundle exec rails db:migrate (only if a database add-on is attached) |
Nixpacks runs bundle install and detects the Puma web server from your Gemfile.lock, starting the app with bundle exec puma.
Step 1 — Make sure Puma is in your Gemfile
Nixpacks starts Rails via Puma. If Puma isn't in your Gemfile, add it:
gem "puma", ">= 5.0"
And in config/puma.rb, bind to the port Vessl injects:
port ENV.fetch("PORT", 3000)
environment ENV.fetch("RAILS_ENV", "development")
Step 2 — Create the app
- In your project, click New Application.
- Select your Rails repository and branch.
- Vessl detects Node.js for the stack type (if you have a
package.json) or falls back to auto-detect. The port defaults to3000. - Set the Start command to:
bundle exec puma -C config/puma.rb
- Click Deploy.
Step 3 — Set production environment variables
Rails requires a few variables in production:
| Variable | Value | Notes |
|---|---|---|
RAILS_ENV | production | Required |
SECRET_KEY_BASE | 128+ char hex string | Run rails secret locally to generate |
RAILS_LOG_TO_STDOUT | 1 | Routes logs to stdout for Vessl to capture |
RAILS_SERVE_STATIC_FILES | 1 | Lets Rails serve public/assets/ directly |
Generate SECRET_KEY_BASE locally:
rails secret
Then paste the output into the Variables tab.
Step 4 — Asset pipeline
Run assets:precompile at build time so compiled assets are baked into the image:
Set the Build command to:
bundle exec rails assets:precompile
This compiles CSS and JS with Sprockets or Propshaft and puts the results in public/assets/. With RAILS_SERVE_STATIC_FILES=1 (set above), Rails serves them directly.
Step 5 — Add a database
Open the app → Overview → Connected Services → Add → PostgreSQL. Vessl injects:
DATABASE_URL=postgresql://<user>:<pass>@<host>:5432/<db>
DB_HOST=<internal hostname>
DB_PORT=5432
DB_DATABASE=<db name>
DB_USERNAME=<username>
DB_PASSWORD=<password>
Rails reads DATABASE_URL automatically in production — no config/database.yml changes needed. The post-deploy command bundle exec rails db:migrate runs on every deploy.
Step 6 — Add Redis (optional)
For Action Cable, Sidekiq, or Rails cache:
- Open the app → Overview → Connected Services → Add → Redis.
- Vessl injects
REDIS_URL,REDIS_HOST,REDIS_PORT,REDIS_PASSWORD.
Action Cable:
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") %>
Sidekiq:
Sidekiq.configure_server do |config|
config.redis = { url: ENV.fetch("REDIS_URL") }
end
Step 7 — Background workers
For Sidekiq or other background job processors, add a daemon from the Daemons tab:
| Worker | Command |
|---|---|
| Sidekiq | bundle exec sidekiq |
| Good Job | bundle exec good_job start |
Each daemon runs in its own container with the same environment variables as your web app.
Common issues
SECRET_KEY_BASE is missing
Rails refuses to start in production without SECRET_KEY_BASE. Set it in the Variables tab (generate with rails secret locally).
assets:precompile fails
This usually means a dependency is missing at build time. Make sure all gems referenced in your assets (e.g. sass-rails, terser) are in the production group of your Gemfile (or not in development-only groups).
PG::ConnectionBad on startup
The database isn't reachable yet (first deploy, add-on still starting) or DATABASE_URL isn't set. Attach a PostgreSQL add-on from Overview → Connected Services and redeploy.
Assets return 404
Make sure RAILS_SERVE_STATIC_FILES=1 is set and assets:precompile ran during the build. Check the build logs to confirm it completed without errors.
bundle exec rails db:migrate runs but schema changes don't apply
The migration may have rolled back due to an error. Check the post-deploy output in the deployment drawer for the specific migration that failed and its error message.