Los doce factores, en un servidor que posees tú
La metodología de la app de doce factores salió del equipo de ingeniería temprano de Heroku, describiendo las restricciones que una app web necesita cumplir para correr bien en cualquier plataforma moderna. Sobrevivió a la plataforma que la produjo, porque los doce factores nunca fueron realmente sobre Heroku —son sobre qué hace que un software sea portable, escalable y operable, sin importar quién posee la máquina debajo. Aquí es donde aparece cada uno en concreto, en una plataforma construida alrededor de servidores que posees en vez de rentar.
I. Base de código. Un repo, un despliegue —git push, o la misma URL de Git pegada en el panel, reconstruye y redespliega en cada push. Nada de esto depende de quién posee el servidor; es el mismo flujo que Heroku popularizó, corriendo contra infraestructura cuyas llaves tienes tú.
II. Dependencias. Declaradas explícitamente, nunca asumidas del host: requirements.txt, Gemfile, package.json, go.mod, composer.json, mix.exs se leen directamente, y nada del entorno de build o de ejecución se asume ya instalado a nivel del sistema.
III. Configuración. Vive bajo Variables, o se define con railyard env:set my-app KEY=VALUE —nunca horneada dentro del build. El mismo artefacto corre sin cambios desde una rama de preview hasta producción; un bug en staging o es real o no lo es, nunca un artefacto de que staging corra un bundle distinto al de prod.
IV. Servicios de respaldo. Una base de datos, Redis, u otra app en el canvas se referencia de la misma forma sea local o remota: ${{postgres.RAILYARD_PRIVATE_URL}}, resuelto por entorno. Cambia el recurso de respaldo y el código de la app nunca tiene que saberlo.
V. Build, release, run. Tres etapas distintas, no un solo paso difuso: un método de build (Dockerfile primero si existe, después Railpack, Nixpacks o buildpacks) produce un artefacto; un comando de release (php artisan migrate --force, python manage.py migrate --no-input, mix ecto.migrate) corre una vez contra el release nuevo antes de que se mueva el tráfico; después la etapa de ejecución lo arranca de verdad. Cada etapa puede fallar de forma independiente, y de hecho falla así, en vez de un solo script haciendo las tres cosas y ocultando cuál parte se rompió.
VI. Procesos. Sin estado, y más de un tipo compartiendo un solo build: web, worker, scheduler, beat —cada uno arrancado con un comando distinto, cada uno escalado de forma independiente (railyard scale my-app worker=2). Una base de código, varios roles, nunca varias bases de código que mantener sincronizadas.
VII. Vínculo de puertos. La app vincula $PORT ella misma y expone HTTP como servicio —no se inyecta por magia en un contenedor de runtime. Un binario de Go, un proceso de Rails, un release compilado de Elixir: todos vinculan el mismo contrato $PORT, que es justo la razón por la que el mismo método de build funciona en lenguajes completamente distintos.
VIII. Concurrencia. Escala hacia afuera mediante el modelo de procesos, no haciendo que un solo proceso haga más: web=3 worker=2 son dos dimensiones del mismo build escaladas de forma independiente, no un ejercicio de ajuste de hilos dentro de un solo proceso.
IX. Disponibilidad para desecharse. Arranque rápido, apagado elegante, y un release nuevo solo recibe tráfico una vez que su contenedor realmente responde —el viejo sigue sirviendo hasta que el nuevo demuestra que está listo, así que un despliegue malo nunca tiene una ventana en la que nada esté arriba.
X. Paridad entre dev y prod. La única regla que aparece, textual, en la lógica de detección propia de cada framework en esta plataforma: el mismo build corre sin cambios desde el primer push de un desarrollador hasta staging y producción. Sin “Dockerfile de producción” aparte, sin rebuild específico por entorno —la paridad no es una meta a la que apuntar, es el único camino que existe.
XI. Logs como flujos de eventos. La app simplemente imprime a stdout/stderr; nunca administra sus propios archivos de log. Lo que el contenedor imprime es lo que se ve en vivo, lo que se busca después, y lo que se envía a un drain —el trabajo de la app termina en imprimir, como siempre debió ser.
XII. Procesos de administración. Las tareas puntuales corren exactamente en el mismo entorno que la app en vivo, no en una consola mantenida aparte: railyard run my-app -- bin/rails db:migrate arranca un contenedor nuevo desde el release actual —el propio CLI toma prestado el nombre que usaba Heroku para la misma idea (heroku run), porque la idea ya era correcta.
El verdadero punto de volver a esto ahora
Ninguna de estas doce reglas exige rentar infraestructura de quien las escribió. Describen cómo construir software al que no le importa quién posee la máquina donde corre —que es exactamente la propiedad que hace que una app sea portable fuera de cualquier plataforma desde el inicio, incluida esta.
Referencia completa: Métodos de build, Variables y secretos, Documentación del CLI.