Módulo 7: Alternative Platforms (Render, Railway, Fly.io)

3. Railway: Deployment para AI Apps

Descripción

En esta cápsula vas a desplegar tu app AI en Railway. Si Render es "Heroku moderno," Railway es "Heroku reimaginado para developers." Railway se diferencia por su developer experience excepcional: deploy desde CLI en una línea, previews automáticos por PR, add-ons integrados (PostgreSQL, Redis, MySQL, MongoDB) con un click, y un modelo de pricing basado en uso real (no en plan fijo). Es la plataforma que muchos developers eligen para prototipos que necesitan estar online rápido.

Contexto: Vienes de desplegar en Render (cápsula 02). La misma app, la misma meta — pero diferente plataforma, diferente experiencia. La comparación directa te da datos reales para tu decision matrix. Railway hace algunas cosas mejor que Render (CLI, add-ons, previews) y otras peor (pricing menos predecible, menos opciones de plan fijo). Al terminar tendrás la experiencia de primera mano para evaluar.


Railway: Overview de la Plataforma

Qué es Railway

Railway es una plataforma cloud que despliega aplicaciones desde Git o desde CLI. Su propuesta de valor es velocidad y simplicidad:

  • Web Services: Apps con servidor (cualquier lenguaje/framework)
  • Add-ons integrados: PostgreSQL, Redis, MySQL, MongoDB — un click para agregar
  • Preview Environments: Cada Pull Request genera un deploy temporal
  • CLI potente: railway up desde tu terminal despliega en segundos
  • Templates: Apps pre-configuradas (Next.js + Prisma, FastAPI + PostgreSQL, etc.)

Modelo de deployment

Opción A: Desde Git (automático)
Tu código (GitHub) → push a main → Railway detecta → Build → Deploy → URL pública

Opción B: Desde CLI (manual)
Terminal → railway up → Railway empaqueta y sube → Build → Deploy → URL pública

Opción C: Desde template
Railway dashboard → Template → Click → Deploy → URL pública

Railway detecta automáticamente tu stack:

Dockerfile presente     → Docker build
requirements.txt        → Python buildpack (Nixpacks)
package.json            → Node buildpack (Nixpacks)
go.mod                  → Go buildpack
Cargo.toml              → Rust buildpack

Pricing (datos actualizados 2026)

Railway usa un modelo de pricing basado en uso, no en planes fijos de recursos:

PlanPrecio baseCréditos incluidosUso
Trial$0$5 de crédito500 horas de ejecución, sin custom domains
Hobby$5/mes$5 de crédito (total $10)Custom domains, deploy desde Git
Pro$20/mes per seat$10 de crédito (total $30)Teams, SLA, soporte prioritario

Costes de uso (después de créditos):

RecursoPrecio
vCPU$0.000463/min ($0.02778/hr)
RAM (GB)$0.000231/min ($0.01388/hr)
Disco (GB)$0.000308/min ($0.01852/hr)
Egress$0.10/GB después de 100 GB/mes incluidos

Estimación para AI app típica (1 vCPU, 1 GB RAM, 24/7):

  • vCPU: $0.02778 × 730 hrs = ~$20/mes
  • RAM: $0.01388 × 730 hrs = ~$10/mes
  • Total infra: ~$30/mes (Hobby: ~$25 después de créditos)

Para AI workloads:

  • Trial: Solo para pruebas rápidas. $5 de crédito se consume en ~1 semana con una app corriendo 24/7.
  • Hobby: Viable para MVPs y proyectos personales. ~$20-30/mes para una app AI básica.
  • Pro: Para equipos. El mismo pricing por uso, pero con SLA y features de equipo.

Limitaciones para AI

LimitaciónImpacto en AIWorkaround
Trial: 500 horas~21 días de ejecución continuaUpgrade a Hobby ($5/mes)
Max 32 GB RAM (Pro)Suficiente para la mayoría, no para modelos XLUsar APIs externas para modelos grandes
Max 32 vCPU (Pro)Suficiente para AI con APIs externasNo correr inferencia local de modelos pesados
Request timeout: 5 minMejor que Render (30s), pero aún limitanteStreaming para respuestas muy largas
No GPUNo puedes correr CUDA/modelos localesAPIs de inferencia (OpenAI, Together AI)
Disco efímero por defectoDatos se pierden en cada deployUsar volumes persistentes (add-on)
Egress: 100 GB/mes incluidosSuficiente para la mayoría de apps AIMonitorear si sirves archivos grandes

Deploy Step-by-Step: App AI en Railway

Paso 1: Preparar y verificar localmente

cd deployment-cloud-guide/module-07/app

# Verificar que la app funciona
docker build -t docusearch-ai .
docker run -p 8000:8000 -e OPENAI_API_KEY=sk-test docusearch-ai
curl http://localhost:8000/health

Paso 2: Deploy desde CLI (la forma más rápida)

# Login (abre browser para autenticación)
railway login

# Crear nuevo proyecto
railway init
# ? Project name: docusearch-ai
# ✅ Project created: docusearch-ai

# Vincular al directorio actual
railway link

# Configurar variables de entorno
railway variables set OPENAI_API_KEY=sk-proj-xxx
railway variables set PLATFORM=railway
railway variables set LOG_LEVEL=info

# Deploy
railway up
# ✅ Uploading... done
# ✅ Building... done
# ✅ Deploying... done
# 🎉 https://docusearch-ai-production.up.railway.app

# Verificar
curl https://docusearch-ai-production.up.railway.app/health

Eso es todo. Tres comandos: railway init, railway variables set, railway up. Tu app está online.

Paso 3: Deploy desde GitHub (automático)

# En Railway Dashboard:
# 1. New Project → Deploy from GitHub Repo
# 2. Seleccionar repositorio
# 3. Railway detecta Dockerfile automáticamente
# 4. Click "Deploy Now"

# Configurar variables:
# Dashboard → tu servicio → Variables → Raw Editor
# OPENAI_API_KEY=sk-proj-xxx
# PLATFORM=railway

A partir de ahora, cada push a main genera un deploy automático.

Paso 4: Configurar railway.toml

Railway permite configuración declarativa con railway.toml:

# railway.toml
[build]
dockerfilePath = "Dockerfile"

[deploy]
startCommand = "uvicorn main:app --host 0.0.0.0 --port $PORT"
healthcheckPath = "/health"
healthcheckTimeout = 30
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 3
git add railway.toml
git commit -m "Add Railway configuration"
git push origin main
# Railway detecta el cambio y redespliega

Paso 5: Generar dominio público

# Desde CLI
railway domain
# ✅ Domain: docusearch-ai-production.up.railway.app

# Custom domain (requiere plan Hobby+)
# Dashboard → tu servicio → Settings → Domains
# Add custom domain: api.tu-dominio.com
# Railway te da un CNAME para configurar en tu DNS

Paso 6: Verificar deployment completo

RAILWAY_URL="https://docusearch-ai-production.up.railway.app"

# Health check
curl $RAILWAY_URL/health

# Inferencia
curl -X POST $RAILWAY_URL/ask \
  -H "Content-Type: application/json" \
  -d '{"question": "¿Qué ventajas tiene Railway?", "max_tokens": 200}'

# Logs
railway logs
# 2026-03-08T15:30:00Z  INFO    Application startup complete
# 2026-03-08T15:30:05Z  INFO    POST /ask 200 1.2s

Railway: Add-ons y Servicios

PostgreSQL en Railway

# Desde CLI
railway add
# ? Select a plugin: PostgreSQL
# ✅ PostgreSQL added to project

# Railway inyecta automáticamente:
# DATABASE_URL=postgresql://user:pass@host:port/dbname
# PGDATABASE, PGHOST, PGPASSWORD, PGPORT, PGUSER

Desde el dashboard:

Dashboard → tu proyecto → New → Database → PostgreSQL
Railway crea la instancia y conecta las variables automáticamente.
import os
DATABASE_URL = os.environ.get("DATABASE_URL")
# Railway inyecta esta variable automáticamente
# No necesitas copiar/pegar la URL manualmente

Redis en Railway

railway add
# ? Select a plugin: Redis
# ✅ Redis added to project

# Railway inyecta:
# REDIS_URL=redis://default:pass@host:port
# REDISHOST, REDISPASSWORD, REDISPORT, REDISUSER
import os
import redis

REDIS_URL = os.environ.get("REDIS_URL")
cache = redis.from_url(REDIS_URL)

Múltiples servicios en un proyecto

Railway permite múltiples servicios (monorepo):

Railway Project: docusearch-ai
├── Service: api (FastAPI)
│   └── Linked to: PostgreSQL, Redis
├── Service: worker (background processing)
│   └── Linked to: PostgreSQL, Redis
├── PostgreSQL instance
└── Redis instance
# Cada servicio ve las variables de los servicios vinculados
# La comunicación interna usa la red privada de Railway
# No expones PostgreSQL a internet — solo los servicios del proyecto acceden

Railway: Preview Environments

Una de las features más útiles de Railway para equipos: cada Pull Request genera un deployment temporal con su propia URL y sus propias instancias de base de datos.

main branch → Production deployment
    https://docusearch-ai-production.up.railway.app

PR #42 → Preview deployment (automático)
    https://docusearch-ai-pr-42.up.railway.app
    └── PostgreSQL copia temporal
    └── Redis copia temporal
    └── Variables heredadas + overrides

Configurar Preview Environments

Dashboard → tu proyecto → Settings → Environments
- Enable PR Deploys: ON
- Auto-deploy PR: ON

Cada PR ahora genera un deploy aislado. Cuando el PR se cierra (merge o close), Railway destruye el preview automáticamente.

Variables por entorno

# Variables de producción
railway variables set OPENAI_API_KEY=sk-prod-xxx -e production

# Variables de preview (staging)
railway variables set OPENAI_API_KEY=sk-staging-xxx -e staging

Railway CLI: Comandos Esenciales

# Proyecto
railway init                    # Crear proyecto nuevo
railway link                    # Vincular directorio a proyecto existente
railway status                  # Ver estado del proyecto

# Deploy
railway up                      # Deploy desde directorio actual
railway up --detach             # Deploy sin esperar

# Variables
railway variables              # Listar todas las variables
railway variables set KEY=val  # Agregar/actualizar variable
railway variables delete KEY   # Eliminar variable

# Servicios
railway add                     # Agregar add-on (PostgreSQL, Redis, etc.)

# Logs y debug
railway logs                    # Ver logs en tiempo real
railway logs --num 100         # Últimas 100 líneas

# Dominio
railway domain                  # Generar dominio público

# Entornos
railway environment             # Listar entornos
railway environment production  # Cambiar a producción

# Conectar a base de datos
railway connect postgres        # Abrir psql conectado a tu PostgreSQL
railway connect redis           # Abrir redis-cli conectado a tu Redis

# Ejecutar comando remoto
railway run python manage.py migrate  # Ejecutar en el contexto del servicio

Patterns de Deployment en Railway para AI

Pattern 1: FastAPI + PostgreSQL + Redis

# Setup completo en 5 comandos
railway init
railway add  # → PostgreSQL
railway add  # → Redis
railway variables set OPENAI_API_KEY=sk-xxx
railway up
# main.py — Railway inyecta DATABASE_URL y REDIS_URL automáticamente
import os

DATABASE_URL = os.environ["DATABASE_URL"]
REDIS_URL = os.environ["REDIS_URL"]

Pattern 2: Monorepo con API + Worker

# railway.toml para el servicio API
[build]
dockerfilePath = "Dockerfile.api"

[deploy]
startCommand = "uvicorn api.main:app --host 0.0.0.0 --port $PORT"
healthcheckPath = "/health"
# railway.toml para el worker (en otro servicio del mismo proyecto)
[build]
dockerfilePath = "Dockerfile.worker"

[deploy]
startCommand = "python worker/main.py"

Pattern 3: Scheduled jobs para AI

Railway soporta cron jobs para tareas periódicas:

Dashboard → New Service → Cron Job
Schedule: 0 2 * * * (cada día a las 2 AM)
Command: python scripts/regenerate_embeddings.py

Troubleshooting

Problema 1: "railway up falla — no detecta el proyecto"

Solución: Necesitas vincular el directorio a un proyecto Railway primero:

# Si no has creado proyecto
railway init
railway up

# Si el proyecto existe pero no está vinculado
railway link
# Selecciona tu proyecto de la lista
railway up

Problema 2: "Build falla — Nixpacks no detecta Python"

Solución: Railway usa Nixpacks por defecto. Si tienes Dockerfile, asegúrate de que esté en la raíz o especificado en railway.toml:

# railway.toml — forzar uso de Dockerfile
[build]
dockerfilePath = "Dockerfile"

Si no quieres usar Docker, asegúrate de que requirements.txt esté en la raíz:

ls
# main.py  requirements.txt  railway.toml

Problema 3: "App funciona pero no tiene URL pública"

Solución: Railway no genera dominio automáticamente. Necesitas generarlo:

railway domain
# ✅ https://tu-app-production.up.railway.app

O desde el dashboard: Service → Settings → Networking → Generate Domain.

Problema 4: "Variables de entorno no se aplican"

Solución: Verifica que las variables están en el entorno correcto (production vs staging):

# Ver variables actuales
railway variables

# Verificar entorno activo
railway environment

# Cambiar a production
railway environment production
railway variables

Problema 5: "El coste se disparó — no entiendo la factura"

Solución: Railway cobra por uso (CPU × tiempo + RAM × tiempo). Una app idle con 1 vCPU y 1 GB RAM consume ~$30/mes. Opciones:

# Ver uso actual
# Dashboard → tu servicio → Metrics

# Reducir recursos
# Dashboard → tu servicio → Settings → Resource Limits
# Max vCPU: 0.5
# Max RAM: 512 MB

Para apps que no necesitan estar 24/7, configura auto-sleep:

Dashboard → Service → Settings → Sleep after inactivity

Ejercicios Prácticos

Ejercicio 1: Deploy desde CLI en Railway

Despliega la misma app AI que usaste en Render, pero usando Railway CLI. Configura las variables y verifica que funciona.

Ver solución
cd deployment-cloud-guide/module-07/app

# Login
railway login

# Crear proyecto
railway init
# Nombre: docusearch-ai-railway

# Configurar variables
railway variables set OPENAI_API_KEY=sk-proj-xxx
railway variables set PLATFORM=railway

# Deploy
railway up

# Generar URL
railway domain

# Verificar
RAILWAY_URL=$(railway domain | grep "https")
curl $RAILWAY_URL/health
# {"status":"healthy","version":"1.0.0","platform":"railway"}

curl -X POST $RAILWAY_URL/ask \
  -H "Content-Type: application/json" \
  -d '{"question": "¿Qué ventajas tiene Railway sobre AWS?", "max_tokens": 200}'

# Ver logs
railway logs --num 20

Ejercicio 2: Agregar PostgreSQL y Redis con un comando

Agrega PostgreSQL y Redis a tu proyecto Railway y verifica que las variables se inyectan automáticamente.

Ver solución
# Agregar PostgreSQL
railway add
# Seleccionar: PostgreSQL

# Agregar Redis
railway add
# Seleccionar: Redis

# Verificar que las variables se inyectaron
railway variables
# DATABASE_URL=postgresql://...
# REDIS_URL=redis://...
# PGHOST=...
# PGPORT=...
# PGDATABASE=...
# PGUSER=...
# PGPASSWORD=...
# REDISHOST=...
# REDISPORT=...
# REDISUSER=...
# REDISPASSWORD=...

# Conectar directamente a la DB desde tu terminal
railway connect postgres
# psql (16.x)
# docusearch=> SELECT version();
#  PostgreSQL 16.x ...

# Conectar a Redis
railway connect redis
# 127.0.0.1:6379> PING
# PONG
# Verificar desde la app que las variables funcionan
# Agregar endpoint de debug (solo para testing):
@app.get("/debug/db")
async def debug_db():
    import psycopg2
    conn = psycopg2.connect(os.environ["DATABASE_URL"])
    cur = conn.cursor()
    cur.execute("SELECT version()")
    version = cur.fetchone()[0]
    conn.close()
    return {"postgres_version": version}
# Redeploy con el nuevo endpoint
railway up

# Verificar
curl $RAILWAY_URL/debug/db
# {"postgres_version":"PostgreSQL 16.x on ..."}

Ejercicio 3: Comparar tiempos de deploy — Render vs Railway

Escribe un script que mida el tiempo desde push hasta que la app responde en ambas plataformas. Documenta la diferencia.

Ver solución
# compare_deploy_times.py
import time
import subprocess
import requests


def measure_deploy_time(platform: str, url: str, push_command: str) -> dict:
    """Mide el tiempo desde push hasta que la app responde."""
    print(f"\n=== {platform} ===")

    print(f"Ejecutando push...")
    start = time.time()

    subprocess.run(push_command, shell=True, capture_output=True)
    push_time = time.time() - start
    print(f"Push completado en {push_time:.1f}s")

    print("Esperando a que la app responda...")
    max_wait = 600  # 10 minutos máximo
    poll_interval = 10
    elapsed = 0

    while elapsed < max_wait:
        try:
            resp = requests.get(f"{url}/health", timeout=5)
            if resp.status_code == 200:
                total_time = time.time() - start
                print(f"App online después de {total_time:.1f}s")
                return {
                    "platform": platform,
                    "push_time_s": round(push_time, 1),
                    "total_deploy_time_s": round(total_time, 1),
                    "status": "success",
                }
        except requests.exceptions.RequestException:
            pass

        time.sleep(poll_interval)
        elapsed += poll_interval
        print(f"  Esperando... ({elapsed}s)")

    return {
        "platform": platform,
        "push_time_s": round(push_time, 1),
        "total_deploy_time_s": max_wait,
        "status": "timeout",
    }


render_result = measure_deploy_time(
    platform="Render",
    url="https://docusearch-ai.onrender.com",
    push_command="git push origin main",
)

railway_result = measure_deploy_time(
    platform="Railway",
    url="https://docusearch-ai-production.up.railway.app",
    push_command="railway up",
)

print("\n=== Resultados ===")
print(f"Render:  {render_result['total_deploy_time_s']}s")
print(f"Railway: {railway_result['total_deploy_time_s']}s")
diff = render_result["total_deploy_time_s"] - railway_result["total_deploy_time_s"]
faster = "Railway" if diff > 0 else "Render"
print(f"{faster} es {abs(diff):.0f}s más rápido")
## Resultados típicos

| Métrica | Render | Railway |
|---------|--------|---------|
| Push/upload | ~2s | ~5s (railway up empaqueta local) |
| Build | ~60-120s | ~30-90s |
| Deploy | ~30-60s | ~10-30s |
| **Total** | **~120-180s** | **~60-120s** |

Railway tiende a ser más rápido porque Nixpacks tiene cache agresivo
y el deploy pipeline es más corto. Render prioriza estabilidad sobre
velocidad en el pipeline.

Ejercicio 4: Configurar Preview Environment para tu app

Configura Preview Environments en Railway para que cada PR genere un deploy aislado con su propia base de datos.

Ver solución
# 1. En Railway Dashboard:
#    Project → Settings → Environments
#    - Enable PR Deploys: ON

# 2. Crear una rama de feature
git checkout -b feature/add-streaming
# Agregar endpoint de streaming a main.py
from fastapi.responses import StreamingResponse
import json
import asyncio


@app.post("/ask/stream")
async def ask_stream(query: Query):
    async def generate():
        api_key = os.environ.get("OPENAI_API_KEY")
        import openai

        client = openai.OpenAI(api_key=api_key)

        stream = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "Eres un asistente de documentación."},
                {"role": "user", "content": query.question},
            ],
            max_tokens=query.max_tokens,
            stream=True,
        )

        for chunk in stream:
            if chunk.choices[0].delta.content:
                token = chunk.choices[0].delta.content
                yield f"data: {json.dumps({'token': token})}\n\n"
                await asyncio.sleep(0)

        yield "data: [DONE]\n\n"

    return StreamingResponse(generate(), media_type="text/event-stream")
# 3. Push la rama
git add -A
git commit -m "Add streaming endpoint"
git push origin feature/add-streaming

# 4. Crear PR en GitHub
gh pr create --title "Add streaming endpoint" --body "Agrega /ask/stream con SSE"

# 5. Railway detecta el PR y crea un preview automáticamente
# Dashboard muestra:
#   Production: https://docusearch-ai-production.up.railway.app
#   PR #1:      https://docusearch-ai-pr-1.up.railway.app

# 6. Verificar el preview
curl https://docusearch-ai-pr-1.up.railway.app/health

# 7. El preview tiene su propia instancia de PostgreSQL y Redis
# Los datos de producción NO se afectan

# 8. Al mergear el PR, Railway destruye el preview automáticamente

Resumen

  • Railway prioriza developer experience: railway up despliega en segundos desde tu terminal.
  • Pricing basado en uso es flexible pero menos predecible que Render — una app 24/7 con 1 vCPU + 1 GB RAM cuesta ~$25-30/mes.
  • Add-ons integrados (PostgreSQL, Redis) se agregan con un comando y las variables se inyectan automáticamente.
  • Preview Environments son la feature estrella para equipos: cada PR genera un deploy aislado con su propia DB.
  • Railway CLI es más potente que Render: railway connect postgres abre psql directo a tu base de datos.
  • Request timeout de 5 minutos es mejor que Render (30s) para AI workloads con respuestas largas.
  • Trial ($5 crédito) se consume rápido. Hobby ($5/mes + uso) es el mínimo viable.
  • El patrón más rápido: railway init + railway add (PostgreSQL) + railway variables set + railway up.

Recursos Adicionales

  1. Railway Documentation — Documentación oficial completa
  2. Railway CLI Reference — Referencia completa de comandos CLI
  3. Railway Templates — Templates pre-configurados para deploy rápido
  4. Railway Nixpacks — El build system que Railway usa por defecto
  5. Railway Pricing Calculator — Calculadora de pricing por uso
  6. Railway Changelog — Novedades y updates de la plataforma