Saltar al contenido principal

Entornos de despliegue

La plataforma corre en tres entornos: local (Docker Compose en la máquina de un desarrollador), development (un despliegue real en Railway, usado para staging/pruebas contra infraestructura con forma de producción) y production (Railway, tráfico real). Esta página es el mapa de cómo se relacionan esos tres — qué NODE_ENV/dominio usa cada uno, qué se comparte vs. qué es separado, y cómo cada cliente (backend, ambos frontends de Next.js, docs, iOS) apunta al correcto. Para la referencia exhaustiva variable por variable (qué hace cada clave de .env, dónde obtenerla), consulta Configuración del entorno.

Los tres entornos

LocalDevelopmentProduction
Dónde correDocker Compose, tu máquinaRailwayRailway
NODE_ENVdevdevelopmentproduction
Backendhttp://localhost:8000dev.api.closegram.comapi.closegram.com
Docshttp://localhost:3005 (o similar)dev.docs.closegram.comdocs.closegram.com
frontend-nextjshttp://localhost:3000dev.closegram.comclosegram.com
frontend-adminhttp://localhost:3001dev.admin.closegram.comadmin.closegram.com
PostgresContenedor Docker local (sin cifrar)Gestionado por Railway, DB_NAME=closegram_devGestionado por Railway, DB_NAME=closegram
BD de analíticaContenedor Docker localANALYTICS_DB_NAME=closegram_analytics_devANALYTICS_DB_NAME=closegram_analytics
Credenciales de terceros (Stripe, Firebase, AWS, Twilio, etc.)Placeholders de dev/modo de pruebaIgual que producción — solo difieren los dos nombres de BD de arribaCredenciales reales

Development reutiliza intencionalmente las llaves de terceros de producción — busca validar contra infraestructura con forma de producción, no ser una copia completamente aislada. Las únicas dos variables de Railway que realmente deberían diferir entre los entornos "development" y "production" de Railway son DB_NAME y ANALYTICS_DB_NAME; todo lo demás (llaves de API, secretos, WEBAUTHN_RP_ID, etc.) tiene el mismo valor en ambos, cambiando solo las variables *_URL y NODE_ENV según la tabla de abajo.

NODE_ENV importa para dos cosas independientes

  1. Qué archivo .env.<NODE_ENV> carga el backend (dev.env.dev, development/production → variables de entorno reales inyectadas por Railway, sin ningún archivo .env local involucrado).
  2. Qué clave de configuración de Sequelize lee sequelize-cli de database/config/config.jsdev, development y production son tres claves separadas ahí. development se agregó específicamente porque el entorno "development" de Railway define NODE_ENV=development, y sin una clave correspondiente, sequelize-cli db:migrate (ejecutado como el preDeployCommand de Railway, ver predeploy.sh) lanza config.development is undefined y aborta el despliegue. Tiene la misma forma que production (SSL requerido — una instancia real de Postgres gestionada) en lugar de la de dev (sin SSL — solo Docker local), ya que la base de datos de development en Railway también es una instancia remota real, solo que separada.

Los seeders también se rastrean, no solo las migraciones

Cada clave de entorno en database/config/config.js define seederStorage: 'sequelize' con seederStorageTableName: 'SequelizeSeedMeta'. Sin esto, sequelize-cli usa por defecto un archivo JSON local para rastrear qué seeders ya corrieron — algo sin sentido contra una instancia real de Postgres, ya que el estado de "ya sembrado" viviría en la máquina que ejecutó npm run seed por última vez, no en la base de datos misma. Con esto, npm run seed (db:seed:all) registra cada archivo de seeder que ejecuta exitosamente en SequelizeSeedMeta (creada automáticamente en el primer run, igual que SequelizeMeta para las migraciones) y omite lo que ya esté registrado en la siguiente corrida — así que volver a correr npm run seed contra una base de datos ya sembrada (el entorno development de Railway, sobre todo, ya que sus 37 archivos de seeders incluyen varios bulk-* que insertan bastantes datos de demo) es un no-op en lugar de reinsertar duplicados o fallar por violaciones de restricción única.

db:reset (reset local completo sin tirar el contenedor de Docker) corre seed:undo:all antes de migrate:undo:all — en ese orden, ya que las funciones down() de los seeders necesitan que las tablas de la app todavía existan, y deshacerlos también limpia SequelizeSeedMeta para que el siguiente npm run seed vuelva a sembrar todo en lugar de ver entradas de "ya hecho" obsoletas y no sembrar nada en las tablas recién recreadas y vacías. db:fresh no necesita esto, ya que docker:reset recrea el contenedor (y por lo tanto SequelizeSeedMeta) desde cero.

Los seeders son solo para local y development — nunca production

Los seeders insertan datos de demo/dummy desechables (usuarios falsos, posts/follows/comentarios masivos, una cuenta admin, etc.). database/config/config.js rechaza correr cualquier comando db:seed* (db:seed, db:seed:all, db:seed:undo, db:seed:undo:all) cuando NODE_ENV resuelve a production, sin importar si se invoca vía npm run seed o directamente con sequelize-cli — las migraciones y el arranque normal de la app no se ven afectados, ya que la verificación solo se activa para comandos de seed.

Correr seeders opcionalmente como parte de un deploy

El paso 3 de predeploy.sh corre sequelize-cli db:seed:all cuando la variable de Railway RUN_SEEDS_ON_DEPLOY está en true en ese servicio — sin definir (o cualquier otro valor) lo omite, que es lo que pasa hoy por defecto en todos los servicios. Es opt-in y no incondicional a propósito: predeploy.sh es el mismo script para cada entorno de Railway, así que un paso de seed incondicional también se dispararía en production. Dos cosas independientes lo mantienen seguro:

  1. Opt-in: define RUN_SEEDS_ON_DEPLOY=true solo en el servicio de development. Nunca lo definas en production.
  2. Respaldo: aunque se definiera por error en production, el guard de producción de database/config/config.js (arriba) rechazaría correr el comando de seed — y un fallo de seed es deliberadamente no fatal en predeploy.sh (mismo patrón que el paso de migración de analítica), así que nunca abortaría el deploy, solo omitiría el sembrado y registraría por qué.

Como el sembrado se rastrea vía SequelizeSeedMeta (ver arriba), dejar RUN_SEEDS_ON_DEPLOY=true indefinidamente es seguro — cada deploy después del primero es un no-op para los seeders que ya corrieron.

Backend: checklist del dashboard de Railway

Por entorno de Railway (development y production se configuran por separado en el dashboard de Railway — no se leen de ningún archivo versionado):

VariableValor en DevelopmentValor en Production
NODE_ENVdevelopmentproduction
DB_NAMEclosegram_devclosegram
ANALYTICS_DB_NAMEclosegram_analytics_devclosegram_analytics
FRONTEND_URLhttps://dev.closegram.comhttps://closegram.com
ADMIN_FRONTEND_URLhttps://dev.admin.closegram.comhttps://admin.closegram.com
WEBAUTHN_RP_IDclosegram.comclosegram.com
WEBAUTHN_ORIGINhttps://dev.closegram.comhttps://closegram.com
RUN_SEEDS_ON_DEPLOY (opcional — ver abajo)true, si quieres que cada deploy (re)siembre datos de demoSin definir — nunca lo definas en producción
Todo lo demás (DB_HOST/DB_USER/DB_PASSWORD, AWS, Stripe, PayPal, Firebase, Apple, APNs, Twilio, BigQuery, etc.)Mismo valor que ProductionMismo valor que Development

Un único WEBAUTHN_RP_ID de closegram.com es válido en closegram.com, dev.closegram.com, admin.closegram.com y dev.admin.closegram.com — los relying-party IDs de WebAuthn coinciden con el dominio registrable y sus subdominios, así que las passkeys no necesitan un RP ID por subdominio. WEBAUTHN_ORIGIN, a diferencia del RP ID, sí necesita coincidir exactamente con el origen en el que está el navegador, por eso difiere entre dev y prod.

El estado en vivo de cada una de estas (si está configurada, sin exponer nunca su valor) es visible en /system/environment en el panel de administración — consulta Estado del entorno. Revisa esa página después de configurar un entorno de Railway en lugar de re-derivar la lista a mano.

frontend-nextjs / frontend-admin: checklist del dashboard de Railway

Ambas son apps de Next.js desplegadas vía Docker en Railway. Las variables NEXT_PUBLIC_* se insertan en el bundle de JS en tiempo de build, así que deben declararse como ARG de Docker (ya hecho en ambos Dockerfile) y configurarse como variables de build en Railway, no solo de runtime — un valor configurado solo en runtime nunca llega al bundle.

VariableValor en DevelopmentValor en Production
NEXT_PUBLIC_GRAPHQL_URLhttps://dev.api.closegram.com/web/graphqlhttps://api.closegram.com/web/graphql
NEXT_PUBLIC_SITE_URL (solo frontend-nextjs)https://dev.closegram.comhttps://closegram.com
Cualquier otra NEXT_PUBLIC_* (Firebase, Stripe, PayPal, Google Maps, Giphy, AdSense, GAM, GA, Apple Sign-In)Mismo valor que ProductionMismo valor que Development

El CORS del backend (ALLOWED_ORIGINS en apps/backend/api/server.js) se construye a partir de FRONTEND_URL/ADMIN_FRONTEND_URL, así que una vez que esos están bien configurados por entorno en el servicio del backend (ver arriba), ambos frontends pueden alcanzarlo sin configuración de CORS separada.

docs: checklist del dashboard de Railway

apps/docs es un build estático de Docusaurus sin variables específicas por entorno — docs.closegram.com y dev.docs.closegram.com sirven el mismo output de build. No aplica un checklist tipo backend/frontend aquí; la única preocupación por entorno es a qué despliegue enruta Railway cada dominio.

iOS

Config/APIConfig.swift resuelve uno de tres entornos al iniciar, en este orden:

  1. Flag de compilación de Swift DEV_ENVIRONMENT — si está definido, siempre resuelve a development sin importar la configuración de build. Es el único mecanismo que funciona para un build archivado/distribuido (TestFlight, ad hoc), ya que en ese punto no hay un proceso de Xcode que lo lance ni un entorno de shell que leer. Se define vía OTHER_SWIFT_FLAGS="-DDEV_ENVIRONMENT".
  2. Variable de entorno de proceso API_ENVIRONMENT, leída solo en builds DEBUG (es decir, cuando se lanza desde Xcode) — "development" o "production"; cualquier otro valor (incluyendo no definida) cae a local. Se define en la pestaña "Arguments" → Environment Variables del Run action del scheme de Xcode, el mismo lugar donde ya vive API_HOST.
  3. Fallback: builds DEBUG sin ninguno de los anteriores definido usan local por defecto; los builds Release usan production por defecto.

Cada entorno resuelve su propia URL de GraphQL HTTP/WS y su URL de LiveKit WS:

LocalDevelopmentProduction
GraphQLhttp://<API_HOST>:8000/web/graphqlhttps://dev.api.closegram.com/web/graphqlhttps://api.closegram.com/web/graphql
GraphQL WSws://<API_HOST>:8000/web/graphqlwss://dev.api.closegram.com/web/graphqlwss://api.closegram.com/web/graphql
LiveKitws://<API_HOST>:7880wss://livekit.closegram.comwss://livekit.closegram.com

<API_HOST> es la variable de entorno API_HOST (usa 127.0.0.1 por defecto) — configúrala con la IP local de tu máquina (ipconfig getifaddr en0 en macOS) para que un dispositivo físico en la misma red pueda alcanzar tu backend local; el simulador puede usar 127.0.0.1 directamente.

Development y production actualmente apuntan al mismo despliegue de LiveKit (livekit.closegram.com) — hoy no existe una instancia de LiveKit separada para dev. Si eso cambia, agrega un caso development a APIConfig.liveKitWSUrl.

Fastlane

build_dev, build_release, beta y beta_nogit aceptan una opción de lane env: (que cae a la variable de shell API_ENVIRONMENT si se omite):

# Apunta el build compilado a dev.api.closegram.com
bundle exec fastlane build_dev env:development

# Apunta un build de TestFlight a dev.api.closegram.com en lugar de producción
bundle exec fastlane beta env:development

Cuando env resuelve a "development", el lane pasa xcargs: 'OTHER_SWIFT_FLAGS="-DDEV_ENVIRONMENT"' a build_app, que es exactamente el flag de compilación que APIConfig.swift revisa primero (ver arriba) — así que esta es la única forma confiable de apuntar un build archivado a development, ya que los builds archivados no tienen variables de entorno de runtime que leer.

release (el lane de envío a la App Store) deliberadamente no tiene override de env — un build de App Store siempre apunta a producción, por diseño; no existe un escenario donde enviar un build apuntando a development a la App Store sea correcto.

Alternativamente, define API_ENVIRONMENT=development directamente en fastlane/.env (copiado de .env.default, que documenta esta variable) en lugar de pasar env: en cada invocación.