Deploying Phoenix when your job queue is already inside the release
Most frameworks need a second deployable process just to run background jobs — a worker dyno, a separate container, something to keep in sync with the web app’s build. Phoenix usually doesn’t, because Oban runs as part of the same OTP supervision tree as the web endpoint itself: the job runner isn’t a second app, it’s already alive the moment web boots.
What’s read from mix.exs
- Datastores are detected from dependencies, not declared twice:
ecto_sql/postgrexprovisions Postgres,ecto_ch/chprovisions ClickHouse,redixprovisions Redis. SECRET_KEY_BASEis generated once, not left for you to produce and store yourself.MIX_ENV=prodandPHX_SERVER=trueare set automatically — the two environment flags a compiled Phoenix release actually needs to serve traffic instead of sitting idle.
railyard create my-app https://github.com/me/my-app --addons postgres
or the same Git URL from the dashboard once GitHub’s connected.
The part that’s genuinely different about compiled releases
A compiled Elixir release has no mix at runtime — the migration command that works in development (mix ecto.migrate) doesn’t exist in the artifact that actually ships. Railyard finds wherever the release puts its own migrator and runs that instead (bin/migrate if the release defines one, otherwise bin/<app> eval), and mix phx.server becomes bin/<app> start the same way. That distinction — dev command versus what the compiled release actually exposes — is the single most common thing that trips up a first Elixir deploy on any platform, and it’s handled before the first migration ever runs.
If a dedicated worker node is actually worth it
Oban needs no second process for most apps — but if job volume is high enough that a slow queue could start starving web requests, splitting it onto its own node is still a one-line addition, not an architecture change:
web: bin/my_app start
worker: bin/my_app eval "MyApp.Release.worker()"
railyard scale my-app worker=2
Same release, same build, started differently — add or edit it from the Workers tab with no repo change required.
Config and secrets
Everything config/runtime.exs reads with System.get_env/2 — BASE_URL, HTTP_PORT, LISTEN_IP, DATABASE_URL, CLICKHOUSE_DATABASE_URL and anything else — is a plain var under Variables, or set via railyard env:set my-app KEY=VALUE, never baked into the compiled release. The same release runs unchanged from staging through production.
Full reference: Phoenix and Elixir docs, Workers and scheduled jobs.