Deploying Rails without writing a Dockerfile
The assumption behind most “how to containerize Rails” guides is that you need to write a Dockerfile first. Most of the time you don’t — the build method only needs to become your problem when the defaults genuinely can’t figure out your app, which is rarer than it sounds.
What actually picks the build method
| Method | When it runs |
|---|---|
| Dockerfile | the repo has one that actually builds the app — a dev-only Dockerfile expecting a bind mount gets skipped automatically, not blindly run |
| Railpack | the default for any repo without a Dockerfile |
| Nixpacks | fallback when Railpack can’t plan the repo |
| Buildpacks | only when chosen explicitly under Settings → Build method |
A fresh Rails app with no Dockerfile builds on Railpack with nothing written by hand. The ordering matters more than the names: Railyard checks for a real Dockerfile first, and only reaches for the automatic builders when there isn’t one.
What a Rails app gets without being asked
This is the part that replaces what a hand-written Dockerfile would otherwise need to do:
RAILS_ENV,RACK_ENV,RAILS_SERVE_STATIC_FILESandRAILS_LOG_TO_STDOUTget set if you haven’t already.- Assets build with
SECRET_KEY_BASE_DUMMY=1, so a real secret isn’t required at build time —SECRET_KEY_BASEitself gets generated once and encrypted. pg,mysql2/trilogy, andredis/sidekiqin the Gemfile map to the matching managed database — Rails 8’s extra databases (cache, queue, cable) declared inconfig/database.ymleach get their own connection URL automatically.- Sidekiq, Solid Queue, GoodJob, Resque or Delayed Job — whichever is actually in the
Gemfile.lock— gets offered as the worker process. - Active Storage on disk gets a persistent volume with no setup; attach a bucket instead and the S3 variables get injected the same way.
assets:precompileruns in the build step automatically, Node toolchain included — atailwindcss:buildor esbuild step chained onto it just works, no separate Dockerfile stage.
When you actually need to write one
A Dockerfile earns its place when the app depends on something genuinely outside Ruby’s own ecosystem — a compiled binary the automatic builders don’t know to install, a non-standard runtime step, a base image mandated for compliance reasons. Short of that, the automatic path handles old native gems (nio4r, Puma 5 and earlier) compiling cleanly under current GCC without any Dockerfile changes.
The release step runs bin/rails db:prepare before each version takes traffic by default — a Procfile’s own release: line overrides it if you’ve already got one. Set a custom release_command only when db:prepare genuinely isn’t enough, like a one-off data migration script.
Full framework reference: Rails, and what the build step actually does: Build methods.