Esta guía te ayudará a migrar variables de entorno entre plataformas de despliegue (Fly.io ↔ Railway) y rotar secrets de producción de forma segura sin tiempo de inactividad.
Migrar de Fly.io a Railway
Sección titulada «Migrar de Fly.io a Railway»Paso 1: Exportar Variables de Fly.io
Sección titulada «Paso 1: Exportar Variables de Fly.io»Opción A: Via Dashboard Web
Sección titulada «Opción A: Via Dashboard Web»- Ir a fly.io/dashboard
- Seleccionar tu app:
talentbricksai - Ir a Secrets
- Copiar manualmente cada variable (Fly.io no permite exportación masiva)
Opción B: Via CLI (Recomendado)
Sección titulada «Opción B: Via CLI (Recomendado)»# Listar todas las secretsflyctl secrets list -a talentbricksai
# Guardar lista en archivo temporalflyctl secrets list -a talentbricksai > fly-secrets.txtNota: flyctl secrets list solo muestra los nombres, no los valores. Necesitas los valores
originales de tu configuración.
Opción C: Desde tu .env Local (si lo tienes)
Sección titulada «Opción C: Desde tu .env Local (si lo tienes)»Si tienes un .env.production local con todas las variables:
# Asegúrate que tiene todas las variables de produccióncat .env.productionPaso 2: Importar a Railway
Sección titulada «Paso 2: Importar a Railway»Opción A: Via Dashboard Web
Sección titulada «Opción A: Via Dashboard Web»- Ir a railway.app
- Seleccionar tu proyecto
- Ir a Variables
- Click + New Variable
- Agregar cada variable manualmente
Opción B: Via Railway CLI (Recomendado)
Sección titulada «Opción B: Via Railway CLI (Recomendado)»# Instalar Railway CLInpm i -g @railway/cli
# Loginrailway login
# Vincular proyectorailway link
# Importar variables una por unarailway variables set DATABASE_URL="postgresql://..."railway variables set JWT_SECRET="tu_secret"railway variables set STRIPE_API_KEY="sk_live_..."railway variables set STRIPE_WEBHOOK_SECRET="whsec_..."# ... etcOpción C: Importar desde archivo .env
Sección titulada «Opción C: Importar desde archivo .env»# Crear archivo temporal con variablescat > railway.env << 'EOF'DATABASE_URL=postgresql://...JWT_SECRET=...STRIPE_API_KEY=sk_live_...STRIPE_WEBHOOK_SECRET=whsec_...# ... todas las variablesEOF
# Importar todas a la vez (cada línea)cat railway.env | while IFS='=' read -r key value; do railway variables set "$key"="$value"done
# ⚠️ Eliminar archivo temporalrm railway.envPaso 3: Verificar Variables
Sección titulada «Paso 3: Verificar Variables»# Listar variables en Railwayrailway variables
# Verificar que todas las requeridas están presentesrailway run env | grep -E "(DATABASE_URL|JWT_SECRET|STRIPE)"Paso 4: Actualizar URLs y Webhooks
Sección titulada «Paso 4: Actualizar URLs y Webhooks»Después de migrar a Railway, actualiza:
Stripe Webhooks
Sección titulada «Stripe Webhooks»- Ir a dashboard.stripe.com/webhooks
- Editar webhook existente o crear nuevo:
- URL anterior (Fly.io):
https://talentbricksai.fly.dev/stripe-webhook - URL nueva (Railway):
https://talentbricksai-production.up.railway.app/stripe-webhook
- URL anterior (Fly.io):
- Copiar nuevo Signing secret (
whsec_...) - Actualizar en Railway:
Ventana de terminal railway variables set STRIPE_WEBHOOK_SECRET="whsec_nuevo_valor"
Google OAuth
Sección titulada «Google OAuth»- Ir a console.cloud.google.com/apis/credentials
- Editar OAuth client
- Authorized redirect URIs - agregar:
https://talentbricksai-production.up.railway.app/auth/google/callback
GitHub OAuth
Sección titulada «GitHub OAuth»- Ir a github.com/settings/developers
- Editar OAuth App
- Authorization callback URL - cambiar a:
https://talentbricksai-production.up.railway.app/auth/github/callback
Actualizar CLIENT_URL
Sección titulada «Actualizar CLIENT_URL»# Railwayrailway variables set CLIENT_URL="https://talentbricksai-production.up.railway.app"Paso 5: Desplegar y Verificar
Sección titulada «Paso 5: Desplegar y Verificar»# Desplegar en Railwayrailway up
# Ver logsrailway logs --tail
# Verificar que la app inicia sin erroresMigrar de Railway a Fly.io
Sección titulada «Migrar de Railway a Fly.io»Paso 1: Exportar Variables de Railway
Sección titulada «Paso 1: Exportar Variables de Railway»# Listar variablesrailway variables
# Guardar en archivo temporal (manual)railway variables > railway-vars.txtPaso 2: Importar a Fly.io
Sección titulada «Paso 2: Importar a Fly.io»# Configurar cada secretflyctl secrets set DATABASE_URL="postgresql://..." -a talentbricksaiflyctl secrets set JWT_SECRET="tu_secret" -a talentbricksaiflyctl secrets set STRIPE_API_KEY="sk_live_..." -a talentbricksai# ... etcNota: Cada vez que configuras un secret en Fly.io, la app se redespliega automáticamente. Para evitar múltiples redespliegues, puedes usar:
# Configurar múltiples secrets a la vezflyctl secrets set \ DATABASE_URL="postgresql://..." \ JWT_SECRET="tu_secret" \ STRIPE_API_KEY="sk_live_..." \ STRIPE_WEBHOOK_SECRET="whsec_..." \ -a talentbricksaiPaso 3: Actualizar URLs y Webhooks
Sección titulada «Paso 3: Actualizar URLs y Webhooks»Mismo proceso que arriba, pero cambiar URLs de Railway a Fly.io:
- Railway:
https://app.up.railway.app - Fly.io:
https://app.fly.dev
Rotar Secrets de Producción
Sección titulada «Rotar Secrets de Producción»Es buena práctica rotar secrets periódicamente o después de una brecha de seguridad.
JWT_SECRET
Sección titulada «JWT_SECRET»⚠️ Importante: Cambiar JWT_SECRET invalidará todas las sesiones activas. Los usuarios tendrán
que hacer login de nuevo.
# 1. Generar nuevo secretopenssl rand -base64 32
# 2. Configurar en tu plataforma# Fly.io:flyctl secrets set JWT_SECRET="nuevo_valor_aqui" -a talentbricksai
# Railway:railway variables set JWT_SECRET="nuevo_valor_aqui"
# 3. Reiniciar app (automático en Fly.io, manual en Railway)railway up
# 4. Notificar a usuarios (opcional)# Enviar email avisando que necesitan hacer login de nuevoAlternativa sin downtime (requiere código):
-
Soportar múltiples secrets simultáneamente:
app/src/server/auth.ts const JWT_SECRETS = [process.env.JWT_SECRET, // nuevoprocess.env.JWT_SECRET_OLD, // viejo];// Intentar verificar con cada secretfor (const secret of JWT_SECRETS) {try {return jwt.verify(token, secret);} catch (err) {continue;}} -
Configurar ambos secrets:
Ventana de terminal railway variables set JWT_SECRET="nuevo"railway variables set JWT_SECRET_OLD="viejo" -
Después de ~24 horas, remover el viejo:
Ventana de terminal railway variables set JWT_SECRET_OLD=""
Stripe API Keys
Sección titulada «Stripe API Keys»Estrategia sin downtime:
Paso 1: Crear Restricted Key (Preparación)
Sección titulada «Paso 1: Crear Restricted Key (Preparación)»- Ir a dashboard.stripe.com/apikeys
- Click + Create restricted key
- Nombre:
TalentBricks Production - 2024 - Permisos necesarios:
- Checkout Sessions: Write
- Customers: Write
- Subscriptions: Write
- Prices: Read
- Products: Read
- Invoices: Read
- Copiar la nueva API key (
sk_live_...)
Paso 2: Actualizar API Key
Sección titulada «Paso 2: Actualizar API Key»# Railwayrailway variables set STRIPE_API_KEY="sk_live_nuevo_valor"
# Fly.ioflyctl secrets set STRIPE_API_KEY="sk_live_nuevo_valor" -a talentbricksai
# Verificar que funcionarailway logs --tail# Buscar errores relacionados con StripePaso 3: Actualizar Webhook Secret
Sección titulada «Paso 3: Actualizar Webhook Secret»⚠️ Importante: Crear webhook ANTES de cambiar el secret para evitar downtime.
- Ir a dashboard.stripe.com/webhooks
- Click Add endpoint
- Endpoint URL:
https://tu-dominio.com/stripe-webhook - Seleccionar eventos (ver checklist de producción)
- Click Add endpoint
- Copiar Signing secret (
whsec_...) - Actualizar variable:
Ventana de terminal railway variables set STRIPE_WEBHOOK_SECRET="whsec_nuevo" - Opcional: Eliminar webhook viejo después de verificar que el nuevo funciona
Paso 4: Verificar Webhooks
Sección titulada «Paso 4: Verificar Webhooks»# Hacer compra de prueba# Verificar en Stripe Dashboard → Webhooks → Recent deliveries → 200 OKAWS S3 Credentials
Sección titulada «AWS S3 Credentials»Estrategia sin downtime:
Paso 1: Crear Nuevo IAM User
Sección titulada «Paso 1: Crear Nuevo IAM User»- Ir a console.aws.amazon.com/iam
- Users → Create user
- Nombre:
talentbricks-app-v2 - Attach policy: AmazonS3FullAccess (o custom policy)
- Create user
- Security credentials → Create access key
- Copiar Access key ID y Secret access key
Paso 2: Actualizar Credentials
Sección titulada «Paso 2: Actualizar Credentials»# Railwayrailway variables set AWS_S3_IAM_ACCESS_KEY="AKIA_nuevo"railway variables set AWS_S3_IAM_SECRET_KEY="nuevo_secret"
# Fly.ioflyctl secrets set \ AWS_S3_IAM_ACCESS_KEY="AKIA_nuevo" \ AWS_S3_IAM_SECRET_KEY="nuevo_secret" \ -a talentbricksaiPaso 3: Verificar S3 Uploads
Sección titulada «Paso 3: Verificar S3 Uploads»# Intentar subir un archivo de prueba# O hacer upload via dashboard de adminPaso 4: Desactivar IAM User Antiguo
Sección titulada «Paso 4: Desactivar IAM User Antiguo»- Ir a IAM → Users →
talentbricks-app-v1 - Security credentials
- Make inactive en las access keys antiguas
- Esperar 24-48 horas
- Si no hay errores, eliminar user antiguo
CloudFront Private Key
Sección titulada «CloudFront Private Key»⚠️ Complejo: Rotar CloudFront keys requiere actualizar la distribución.
Paso 1: Generar Nuevo Key Pair
Sección titulada «Paso 1: Generar Nuevo Key Pair»# Generar nuevo par de clavesopenssl genrsa -out cloudfront_private_key_new.pem 2048openssl rsa -pubout -in cloudfront_private_key_new.pem -out cloudfront_public_key_new.pemPaso 2: Subir Public Key a CloudFront
Sección titulada «Paso 2: Subir Public Key a CloudFront»- Ir a console.aws.amazon.com/cloudfront
- Public keys → Add public key
- Pegar contenido de
cloudfront_public_key_new.pem - Copiar el Key ID generado
Paso 3: Actualizar Variables
Sección titulada «Paso 3: Actualizar Variables»# Leer private key en una líneaPRIVATE_KEY=$(awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' cloudfront_private_key_new.pem)
# Configurarrailway variables set CLOUDFRONT_KEY_PAIR_ID="K_nuevo_id"railway variables set CLOUDFRONT_PRIVATE_KEY="$PRIVATE_KEY"Paso 4: Verificar Videos
Sección titulada «Paso 4: Verificar Videos»- Abrir curso con video en la app
- Verificar que el video carga correctamente
- Verificar que la URL tiene signature válida
Paso 5: Remover Public Key Antiguo
Sección titulada «Paso 5: Remover Public Key Antiguo»- CloudFront → Public keys
- Encontrar key antiguo
- Disassociate de las distribuciones
- Delete
SendGrid API Key
Sección titulada «SendGrid API Key»Estrategia sin downtime:
Paso 1: Crear Nueva API Key
Sección titulada «Paso 1: Crear Nueva API Key»- Ir a app.sendgrid.com/settings/api_keys
- Click Create API Key
- Nombre:
TalentBricks Production - 2024 - Permisos: Full Access (o Restricted con Mail Send)
- Copiar API key (
SG....)
Paso 2: Actualizar Variable
Sección titulada «Paso 2: Actualizar Variable»railway variables set SENDGRID_API_KEY="SG.nuevo_valor"Paso 3: Verificar Email Delivery
Sección titulada «Paso 3: Verificar Email Delivery»# Registrar nuevo usuario para probar email# O usar "Forgot password" para enviar email de prueba
# Verificar en SendGrid → Activity que el email se envióPaso 4: Eliminar API Key Antigua
Sección titulada «Paso 4: Eliminar API Key Antigua»- SendGrid → Settings → API Keys
- Encontrar key antigua
- Click ••• → Delete
Checklist de Migración sin Downtime
Sección titulada «Checklist de Migración sin Downtime»Sigue este checklist para migrar sin tiempo de inactividad:
Fase de Preparación
Sección titulada «Fase de Preparación»- Backup completo de base de datos
- Documentar todas las variables actuales
- Crear nuevos secrets/keys en servicios externos
- Configurar nuevo entorno en paralelo (si es posible)
Fase de Migración
Sección titulada «Fase de Migración»- Configurar variables en nuevo entorno
- Probar conexión a base de datos
- Probar pagos en Stripe test mode primero
- Configurar webhooks en nuevo dominio (sin eliminar los viejos todavía)
- Hacer deploy de prueba en nuevo entorno
- Verificar logs sin errores
Fase de Switchover
Sección titulada «Fase de Switchover»- Reducir TTL de DNS a 300 segundos (5 minutos)
- Esperar 24 horas para que TTL se propague
- Cambiar DNS para apuntar a nuevo entorno
- Monitorear logs en ambos entornos
- Verificar que webhooks llegan al nuevo endpoint
Fase de Verificación
Sección titulada «Fase de Verificación»- Verificar flujo completo en producción
- Hacer compra de prueba real
- Verificar que emails llegan
- Verificar que videos cargan
- Monitorear errores por 24-48 horas
Fase de Limpieza
Sección titulada «Fase de Limpieza»- Incrementar TTL de DNS a valor normal (3600)
- Eliminar webhooks viejos
- Desactivar secrets viejos (no eliminar todavía)
- Esperar 7 días
- Eliminar secrets viejos permanentemente
- Apagar entorno viejo
Tabla de Rotación de Secrets
Sección titulada «Tabla de Rotación de Secrets»| Secret | Frecuencia Recomendada | Impacto | Complejidad |
|---|---|---|---|
JWT_SECRET | Cada 6 meses o después de brecha | Alto - usuarios deben re-login | Baja |
STRIPE_API_KEY | Cada 12 meses | Medio - pagos temporalmente afectados | Baja |
STRIPE_WEBHOOK_SECRET | Al cambiar endpoints | Bajo - crear nuevo webhook | Baja |
AWS_S3_IAM_ACCESS_KEY | Cada 90 días (buena práctica) | Medio - uploads fallan | Media |
CLOUDFRONT_PRIVATE_KEY | Cada 12 meses | Alto - videos no cargan | Alta |
SENDGRID_API_KEY | Cada 12 meses | Medio - emails no llegan | Baja |
DATABASE_URL | Solo al migrar DB | Crítico - app no funciona | Alta |
Script de Migración Automatizada
Sección titulada «Script de Migración Automatizada»Para migrar todas las variables de Fly.io a Railway:
#!/bin/bashset -e # Exit on error
echo "🚀 Migrando variables de Fly.io a Railway..."
# Variables a migrar (agregar todas las tuyas)VARS=( "DATABASE_URL" "JWT_SECRET" "STRIPE_API_KEY" "STRIPE_WEBHOOK_SECRET" "PAYMENTS_MONTHLY_SUBSCRIPTION_PLAN_ID" "PAYMENTS_ANNUAL_SUBSCRIPTION_PLAN_ID" "SENDGRID_API_KEY" "ADMIN_EMAILS" "CLIENT_URL" "AWS_S3_IAM_ACCESS_KEY" "AWS_S3_IAM_SECRET_KEY" "AWS_S3_FILES_BUCKET" "AWS_S3_REGION" "CLOUDFRONT_DOMAIN" "CLOUDFRONT_KEY_PAIR_ID" "CLOUDFRONT_PRIVATE_KEY")
# Leer de .env local (asegúrate que tiene valores de producción)if [ ! -f ".env.production" ]; then echo "❌ Error: .env.production no encontrado" exit 1fi
source .env.production
# Migrar cada variablefor var in "${VARS[@]}"; do value="${!var}" if [ -z "$value" ]; then echo "⚠️ Saltando $var (vacío)" continue fi
echo "✅ Migrando $var" railway variables set "$var"="$value"done
echo "🎉 Migración completada!"echo "Verifica: railway variables"Uso:
chmod +x migrate-fly-to-railway.sh./migrate-fly-to-railway.shTroubleshooting
Sección titulada «Troubleshooting»Error: “Secret already exists”
Sección titulada «Error: “Secret already exists”»Railway:
# Eliminar primerorailway variables set VARIABLE=""# Luego configurar nuevo valorrailway variables set VARIABLE="nuevo_valor"Fly.io:
# Fly.io permite sobrescribir directamenteflyctl secrets set VARIABLE="nuevo_valor" -a talentbricksaiError: Variables no se actualizan
Sección titulada «Error: Variables no se actualizan»Railway:
# Forzar redesplieguerailway up --detachFly.io:
# Reiniciar appflyctl apps restart talentbricksaiWebhooks siguen llegando al endpoint viejo
Sección titulada «Webhooks siguen llegando al endpoint viejo»Solución:
- Verificar que DNS cambió:
nslookup tu-dominio.com - Esperar propagación de DNS (hasta 48 horas)
- Verificar TTL bajo antes del cambio
Recursos Adicionales
Sección titulada «Recursos Adicionales»- Checklist de Producción
- Variables de Entorno - Referencia completa
- Guía de Despliegue en Fly.io
- Guía de Despliegue en Railway
Soporte
Sección titulada «Soporte»Si encuentras problemas durante la migración:
- Revisar logs:
railway logsoflyctl logs - Verificar que todas las variables están configuradas
- Consultar documentación de la plataforma
- Pedir ayuda en Wasp Discord