← Blog

The twelve factors, on a server you own

Railyard

June 1, 2026

The twelve-factor app methodology came out of Heroku’s early engineering team, describing the constraints a web app needs to satisfy to run well on any modern platform. It outlived the platform that produced it, because the twelve factors were never really about Heroku — they’re about what makes software portable, scalable and operable, independent of who owns the machine underneath it. Here’s where each one actually shows up, concretely, on a platform built around servers you own instead of rent.

I. Codebase. One repo, one deploy — git push, or the same Git URL pasted into the dashboard, rebuilds and redeploys on every push. Nothing here is specific to who owns the server; it’s the same workflow Heroku made popular, running against infrastructure you hold the keys to.

II. Dependencies. Explicitly declared, never assumed from the host: requirements.txt, Gemfile, package.json, go.mod, composer.json, mix.exs are read directly, and nothing about the build or run environment is assumed to already be installed system-wide.

III. Config. Lives under Variables, or set with railyard env:set my-app KEY=VALUE — never baked into the build. The same artifact runs unchanged from a preview branch through production; a bug in staging is either real or it isn’t, never an artifact of staging running a different bundle than prod.

IV. Backing services. A database, Redis, or another app on the canvas is referenced the same way whether it’s local or remote: ${{postgres.RAILYARD_PRIVATE_URL}}, resolved per environment. Swap the backing resource and the app’s code never has to know.

V. Build, release, run. Three distinct stages, not one blurred step: a build method (Dockerfile first if present, then Railpack, Nixpacks, or buildpacks) produces an artifact; a release command (php artisan migrate --force, python manage.py migrate --no-input, mix ecto.migrate) runs once against the new release before traffic moves; then the run stage actually starts it. Each stage can fail independently, and does, rather than one script doing all three and hiding which part broke.

VI. Processes. Stateless, and more than one type sharing a single build: web, worker, scheduler, beat — each started with a different command, each scaled independently (railyard scale my-app worker=2). One codebase, several roles, never several codebases to keep in sync.

VII. Port binding. The app binds $PORT itself and exports HTTP as a service — it isn’t injected into a runtime container by magic. A Go binary, a Rails process, a compiled Elixir release: all bind the same $PORT contract, which is the whole reason the same build method works across completely different languages.

VIII. Concurrency. Scale out via the process model, not by making one process do more: web=3 worker=2 is two independently-scaled dimensions of the same build, not a thread-count tuning exercise inside a single process.

**IX. Disposability. **Fast startup, graceful shutdown, and a new release only takes traffic once its container actually answers — the old one keeps serving until the new one proves it’s ready, so a bad deploy never gets a window where nothing is up.

X. Dev/prod parity. The single rule that shows up, verbatim, in every framework’s own detection logic on this platform: the same build runs unchanged from a developer’s first push through staging and production. No separate “production Dockerfile,” no environment-specific rebuild — parity isn’t a goal to aim for, it’s the only path that exists.

XI. Logs as event streams. The app just prints to stdout/stderr; it never manages its own log files. Whatever the container prints is what shows up live, what gets searched later, and what ships to a drain — the app’s job ends at printing, same as it always should have.

XII. Admin processes. One-off tasks run in the exact same environment as the live app, not a separately-maintained console: railyard run my-app -- bin/rails db:migrate starts a fresh container from the current release — the CLI itself borrows Heroku’s own name for the same idea (heroku run), because the idea was already correct.

The actual point of revisiting this now

None of these twelve rules require renting infrastructure from whoever wrote them. They describe how to build software that doesn’t care who owns the machine it runs on — which is exactly the property that makes an app portable off any platform in the first place, including this one.

Full reference: Build methods, Variables and secrets, CLI docs.

See a twelve-factor app deploy in real time.

Push a repo that already follows these rules — nothing about them changes on a server you own.

Free plan, no card, and 14 days of Pro for every new team. One flat price per team.