Ciclo completo: setup · variables · entornos · deploy · health-check · rollback automático
# Instalar CLI (una sola vez) $ npm i -g @railway/cli # Autenticarse $ railway login Opening browser for authentication... Logged in as dev@nutritrack.app ✓ # Inicializar proyecto desde el repo $ railway init ✔ Created project "nutritrack-api" in workspace "NutriTrack" ✔ Linked to environment: production # Verificar contexto siempre antes de ejecutar comandos $ railway status Project: nutritrack-api Service: web Environment: production URL: https://nutritrack-api.up.railway.app
En CI/CD (GitHub Actions) usar token headless: RAILWAY_TOKEN=xxx railway up --detach. El token de proyecto (RAILWAY_TOKEN) tiene scope limitado; para operaciones de cuenta usar RAILWAY_API_TOKEN.
$ railway add --database postgres ✔ Added PostgreSQL service ✔ DATABASE_URL auto-injected ✔ PGHOST, PGPORT, PGUSER set # Verificar conexión $ railway connect psql (PostgreSQL 16) nutritrack=# \dt
$ railway add --database redis ✔ Added Redis service ✔ REDIS_URL auto-injected ✔ REDISHOST, REDISPORT set # Conectar a Redis shell $ railway connect redis 127.0.0.1:6379> PING PONG
# Inyectar variables sensibles (nunca en .env del repo) $ railway variable set \ JWT_SECRET=nt_jwt_prod_s3cr3t_2025 \ SENDGRID_KEY=SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ STRIPE_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ NODE_ENV=production \ PORT=3000 ✔ 5 variables set in production # Listar todas (los valores secretos se enmascaran en la UI) $ railway variable list DATABASE_URL = postgres://railway:abc123@db.railway.app:5432/railway [auto] REDIS_URL = redis://default:xyz789@redis.railway.app:6379 [auto] JWT_SECRET = nt_jwt_prod_s3cr3t_2025 [set] SENDGRID_KEY = SG.xxxxx... [set] STRIPE_KEY = sk_live_xxxxx... [set] NODE_ENV = production [set] PORT = 3000 [set]
# Crear entorno de staging que clona la config de production $ railway environment new staging ✔ Created environment "staging" (cloned from production) # Desplegar en staging (sin afectar production) $ railway up -e staging -s web Uploading 1.8 MB... Build started (staging) ✔ Deployment live at nutritrack-api-staging.up.railway.app # Sobrescribir variables sólo en staging $ railway variable set -e staging \ STRIPE_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxx \ NODE_ENV=staging # Cambiar contexto al entorno de production $ railway environment ? Select environment: ❯ production staging
# 1. Ejecutar migraciones usando env vars de Railway (sin desplegar) $ railway run npx prisma migrate deploy Prisma Migrate ✔ 4 migrations applied successfully - 20250102_init - 20250210_add_meals_table - 20250315_add_clinic_plans - 20250401_add_audit_log # 2. Desplegar la API (detach = no bloquear terminal en logs) $ railway up --detach Uploading 2.1 MB... Build started (detached) Deployment ID: dep_K9mXv7pQr2nLw # 3. Asignar dominio personalizado $ railway domain api.nutritrack.app ✔ Custom domain added ✔ SSL certificate issued (Let's Encrypt) Add CNAME api.nutritrack.app → nutritrack-api.up.railway.app
Script listo para GitHub Actions. Despliega, espera hasta 60 s a que GET /api/health responda 200 y hace rollback automático si falla.
#!/usr/bin/env python3 # deploy_railway.py — NutriTrack API · Deploy con health-check y auto-rollback # Uso: python deploy_railway.py --env production --health https://api.nutritrack.app/api/health import subprocess, time, httpx, argparse, sys def run(cmd: list[str], **kw) -> subprocess.CompletedProcess: """Ejecuta un comando railway y lanza excepción en error.""" return subprocess.run(cmd, check=True, **kw) def deploy_with_health_check( env: str, health_url: str, service: str = "web", timeout: int = 60, ) -> bool: # 1. Verificar contexto run(["railway", "status"]) # 2. Correr migraciones antes del deploy print("[1/3] Migraciones Prisma...") run(["railway", "run", "-e", env, "npx", "prisma", "migrate", "deploy"]) # 3. Desplegar en modo detach print("[2/3] Desplegando en Railway...") run(["railway", "up", "--detach", "-e", env, "-s", service]) # 4. Health-check loop print(f"[3/3] Health-check → {health_url} (timeout: {timeout}s)") deadline = time.time() + timeout while time.time() < deadline: try: r = httpx.get(health_url, timeout=5) if r.status_code == 200: print("✅ Deploy saludable — NutriTrack API activa") return True except httpx.RequestError: pass elapsed = int(timeout - (deadline - time.time())) print(f" Esperando... ({elapsed}s)") time.sleep(3) # 5. Rollback automático print("❌ Health-check falló. Iniciando rollback...") run(["railway", "rollback", "-e", env]) print("⏪ Rollback completado. Versión anterior restaurada.") return False if __name__ == "__main__": ap = argparse.ArgumentParser() ap.add_argument("--env", default="production") ap.add_argument("--health", default="https://api.nutritrack.app/api/health") ap.add_argument("--service", default="web") ap.add_argument("--timeout", type=int, default=60) args = ap.parse_args() ok = deploy_with_health_check(args.env, args.health, args.service, args.timeout) sys.exit(0 if ok else 1)
# Ver logs de build (errores de dependencias, npm ci, etc.) $ railway logs --build Step 3/7: RUN npm ci npm WARN deprecated bcrypt@5.1.0: use bcryptjs instead ✔ Build completed in 41s # Ver últimas 50 líneas de logs de runtime $ railway logs -n 50 [2025-06-14 09:12:33] INFO Server listening on port 3000 [2025-06-14 09:12:33] ERROR connect ECONNREFUSED 127.0.0.1:5432 [2025-06-14 09:12:33] FATAL Database connection failed — process exiting # CAUSA: DATABASE_URL apunta a localhost. Corregir: $ railway variable set \ DATABASE_URL=postgres://railway:abc123@db.railway.app:5432/railway ✔ Variable updated $ railway redeploy ✔ Redeploying web... [2025-06-14 09:15:01] INFO Database connected ✓ [2025-06-14 09:15:02] INFO Server listening on port 3000 ✓
railway up — deploy + stream logsrailway up --detach — deploy sin bloquearrailway up -e staging -s web — target específicorailway redeploy — rebuild mismo códigorailway restart — reiniciar sin rebuildrailway rollback — restaurar versión anteriorrailway variable list — listar variablesrailway variable set K=V — crear/actualizarrailway variable delete K — eliminarrailway run cmd — comando con env varsrailway shell — shell interactivorailway add --database postgres — añadir DBrailway add --database redis — añadir Redisrailway domain api.ejemplo.com — dominio customrailway scale — escalar serviciorailway volume add — volumen persistenterailway connect — shell de base de datosrailway status — contexto actualrailway logs — logs en tiempo realrailway logs --build — logs de buildrailway logs -n 100 — últimas N líneasrailway ssh — SSH al contenedorrailway environment new staging — nuevo entornoAñadir RAILWAY_TOKEN como secret en GitHub: Settings → Secrets → Actions → New repository secret.
name: Deploy NutriTrack API on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node 20 uses: actions/setup-node@v4 with: node-version: '20' - name: Install Railway CLI run: npm i -g @railway/cli - name: Deploy with health-check env: RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} run: | pip install httpx python deploy_railway.py \ --env production \ --health https://api.nutritrack.app/api/health \ --timeout 60