← Blog

Phoenix deployen, wenn die Job-Queue schon im Release steckt

Railyard

14. Oktober 2026 · 2 Min. Lesezeit

Die meisten Frameworks brauchen einen zweiten deploybaren Prozess, nur um Hintergrundjobs laufen zu lassen — einen Worker-Dyno, einen separaten Container, etwas, das mit dem Build der Web-App synchron gehalten werden muss. Phoenix meistens nicht, weil Oban als Teil desselben OTP-Supervision-Trees läuft wie der Web-Endpoint selbst: Der Job-Runner ist keine zweite App, er lebt bereits in dem Moment, in dem web bootet.

Was aus mix.exs gelesen wird

  • Datenspeicher werden anhand von Abhängigkeiten erkannt, nicht doppelt deklariert: ecto_sql/postgrex provisioniert Postgres, ecto_ch/ch provisioniert ClickHouse, redix provisioniert Redis.
  • SECRET_KEY_BASE wird einmalig generiert, du musst ihn nicht selbst erzeugen und speichern.
  • MIX_ENV=prod und PHX_SERVER=true werden automatisch gesetzt — die zwei Umgebungsflags, die ein kompiliertes Phoenix-Release tatsächlich braucht, um Traffic zu bedienen statt untätig zu bleiben.
railyard create my-app https://github.com/me/my-app --addons postgres

oder dieselbe Git-URL über das Dashboard, sobald GitHub verbunden ist.

Der Teil, der bei kompilierten Releases wirklich anders ist

Ein kompiliertes Elixir-Release hat zur Laufzeit kein mix — der Migrationsbefehl, der in der Entwicklung funktioniert (mix ecto.migrate), existiert in dem Artefakt, das tatsächlich ausgeliefert wird, nicht. Railyard findet, wo das Release seinen eigenen Migrator ablegt, und führt diesen stattdessen aus (bin/migrate, falls das Release einen definiert, sonst bin/<app> eval), und mix phx.server wird genauso zu bin/<app> start. Dieser Unterschied — Dev-Befehl versus das, was das kompilierte Release tatsächlich bereitstellt — ist das mit Abstand häufigste Problem bei einem ersten Elixir-Deploy auf jeder Plattform, und er wird behandelt, bevor die erste Migration überhaupt läuft.

Falls sich ein dedizierter Worker-Knoten tatsächlich lohnt

Oban braucht für die meisten Apps keinen zweiten Prozess — aber falls das Job-Volumen so hoch ist, dass eine langsame Queue anfangen könnte, Web-Requests auszuhungern, ist das Aufspalten auf einen eigenen Knoten immer noch eine Ein-Zeilen-Ergänzung, keine Architekturänderung:

web:    bin/my_app start
worker: bin/my_app eval "MyApp.Release.worker()"
railyard scale my-app worker=2

Dasselbe Release, derselbe Build, nur anders gestartet — hinzufügen oder bearbeiten über den Workers-Tab, ohne Änderung am Repo.

Config und Secrets

Alles, was config/runtime.exs mit System.get_env/2 liest — BASE_URL, HTTP_PORT, LISTEN_IP, DATABASE_URL, CLICKHOUSE_DATABASE_URL und alles andere — ist eine einfache Variable unter Variables, oder gesetzt via railyard env:set my-app KEY=VALUE, nie fest in das kompilierte Release gebacken. Dasselbe Release läuft unverändert von Staging bis Produktion.

Vollständige Referenz: Phoenix- und Elixir-Docs, Worker und geplante Jobs.

Deploye jetzt eine Phoenix-App.

mix.exs wird automatisch gelesen — ecto.migrate, SECRET_KEY_BASE und die Datenbank sind direkt verdrahtet.

Kostenloser Plan, keine Kreditkarte, und 14 Tage Pro für jedes neue Team. Ein fester Preis pro Team.