Cómo hacer deploys sin downtime: lo que aprendimos en producción
El deploy a producción debería ser aburrido. Un evento rutinario, sin drama, sin “cerramos por mantenimiento”, sin usuarios que pierden lo que estaban haciendo.
Llegar a ese punto requiere pensar la arquitectura correctamente desde el inicio. Aquí están las estrategias que usamos y cuándo aplica cada una.
El problema con los deploys “ingenuos”
Un deploy típico sin ninguna estrategia especial:
- Detener el servidor actual
- Reemplazar el código
- Iniciar el nuevo servidor
Durante los pasos 1 a 3, la aplicación no responde. Si el inicio tarda 30 segundos, tienes 30 segundos de downtime. Si algo falla en el inicio, el downtime se extiende hasta que alguien lo detecta y actúa.
Para una aplicación interna usada en horario de oficina, esto puede ser aceptable. Para un e-commerce, una plataforma SaaS o cualquier sistema con usuarios activos las 24 horas, no lo es.
Blue-Green deployment: la estrategia más confiable
La idea: mantener dos entornos de producción idénticos (azul y verde). En cualquier momento, uno está activo y el otro está en espera.
Cómo funciona:
- El entorno azul está sirviendo el tráfico actual
- Desplegamos la nueva versión en el entorno verde (que no recibe tráfico)
- Verificamos que el verde funciona correctamente
- Redirigimos el tráfico al verde (cambio instantáneo en el load balancer)
- El azul queda en espera — si hay un problema, revertir es volver a apuntar al azul
Ventaja principal: el rollback es inmediato. Si la nueva versión tiene un bug crítico, volver a la versión anterior toma segundos, no minutos.
Desventaja: necesitas el doble de infraestructura corriendo simultáneamente durante el deploy.
Rolling deployment: para la mayoría de casos
Si tienes múltiples instancias de tu aplicación (horizontal scaling), el rolling deployment actualiza una instancia a la vez mientras las otras siguen sirviendo tráfico.
Cómo funciona:
- Tienes 4 instancias corriendo la versión A
- Detenes la instancia 1, la actualizas a versión B, la vuelves a iniciar
- El load balancer solo envía tráfico a las instancias saludables
- Repites para las instancias 2, 3 y 4
Ventaja: no necesitas infraestructura extra. Simple de implementar con orquestadores modernos (Kubernetes, Railway, ECS).
Consideración importante: durante el rolling update, coexisten la versión A y la versión B. Si hiciste un cambio de base de datos incompatible hacia atrás, esto puede generar errores. Las migraciones tienen que ser backwards-compatible.
Migraciones de base de datos: el problema más subestimado
La parte más delicada de los deploys sin downtime es la base de datos. Cambiar una columna de nombre, eliminar una tabla, cambiar un tipo de dato — estas operaciones pueden romper la versión anterior si se ejecutan antes de que todos los servidores estén en la nueva versión.
La regla: las migraciones de base de datos tienen que ser backwards-compatible con la versión anterior del código por al menos un ciclo de deploy.
Patrón para renombrar una columna sin downtime:
- Deploy 1: agrega la columna nueva, el código escribe en ambas columnas
- Deploy 2: el código lee la columna nueva, sigue escribiendo en ambas
- Deploy 3: el código solo usa la columna nueva, elimina la escritura duplicada
- Migración final: elimina la columna vieja
Sí, son más pasos. Pero evitan el downtime y, más importante, evitan pérdida de datos.
Health checks: el prerequisito de todo lo anterior
Ninguna estrategia de deploy sin downtime funciona sin health checks correctos. El load balancer necesita saber cuándo una instancia está lista para recibir tráfico.
Un health check básico responde en /health con un status 200 cuando la aplicación está lista — conexión a la base de datos verificada, caché caliente, dependencias disponibles.
Si el health check falla, el load balancer no envía tráfico a esa instancia. Nunca.
// Ejemplo básico de health check endpoint
app.get('/health', async (req, res) => {
try {
await db.execute('SELECT 1'); // verificar conexión DB
res.json({ status: 'ok', timestamp: new Date().toISOString() });
} catch (error) {
res.status(503).json({ status: 'error', message: 'Database unavailable' });
}
});
Feature flags: desplegar sin lanzar
Una técnica que complementa todo lo anterior y que cambia la relación con el riesgo: separar el deploy del lanzamiento. Con feature flags, subes código nuevo a producción apagado, y lo enciendes para los usuarios cuando estás listo — sin un nuevo deploy.
Esto resuelve varios problemas a la vez:
- Despliegas funcionalidad incompleta sin riesgo, porque está detrás de un flag apagado. El código convive en producción mucho antes de activarse.
- Activas gradualmente: primero para tu equipo, luego para el 5% de usuarios, luego para todos. Si algo falla, apagas el flag en segundos — sin rollback de código.
- Pruebas en producción real con tráfico real antes de exponer la feature a todos.
Una implementación simple es una tabla de configuración o un servicio como un Edge Config consultado al inicio del request. No necesitas una plataforma cara: para la mayoría de productos, un flag booleano por feature en la base de datos ya entrega el 90% del valor.
Lo que usamos para proyectos en Railway
Railway hace que esto sea considerablemente más simple. Con su sistema de deploys, la nueva versión no recibe tráfico hasta que el health check pasa. Si el deploy falla, el tráfico sigue en la versión anterior automáticamente.
Para la mayoría de proyectos que construimos, esto es suficiente. Para proyectos que requieren zero-downtime absoluto o rollback instantáneo, implementamos blue-green con un proxy de Nginx o Caddy delante.
Si tu aplicación tiene deploys que generan downtime y quieres resolverlo, generalmente es una combinación de infraestructura correcta y migraciones de base de datos bien planificadas. Escríbenos si quieres revisarlo.
Preguntas frecuentes
¿Qué es un deploy sin downtime?
Es actualizar una aplicación en producción sin que ningún usuario note una interrupción: nada de 'cerramos por mantenimiento' ni de usuarios que pierden lo que estaban haciendo. Se logra con estrategias como blue-green o rolling deployment, health checks correctos y migraciones de base de datos compatibles hacia atrás.
¿Cuál es la diferencia entre blue-green y rolling deployment?
Blue-green mantiene dos entornos idénticos (uno activo, uno en espera): despliegas en el inactivo, lo verificas y rediriges el tráfico de golpe, con rollback inmediato. Rolling deployment actualiza una instancia a la vez mientras las demás siguen sirviendo tráfico, sin infraestructura extra. Blue-green da rollback instantáneo pero cuesta el doble de infra durante el deploy; rolling es más económico pero hace coexistir ambas versiones.
¿Por qué las migraciones de base de datos rompen los deploys sin downtime?
Porque durante un rolling deploy coexisten temporalmente la versión vieja y la nueva del código. Si una migración elimina o renombra una columna que la versión anterior todavía usa, esa versión empieza a fallar. La regla: toda migración debe ser compatible hacia atrás con el código anterior por al menos un ciclo de deploy. Renombrar una columna sin downtime toma varios pasos (agregar nueva, escribir en ambas, migrar lecturas, eliminar la vieja).
¿Qué es un health check y por qué es imprescindible?
Es un endpoint (típicamente /health) que responde 200 solo cuando la aplicación está lista para recibir tráfico: conexión a la base de datos verificada, dependencias disponibles. El load balancer lo consulta y nunca envía tráfico a una instancia cuyo health check falla. Sin health checks correctos, ninguna estrategia de deploy sin downtime funciona, porque el sistema no sabe cuándo una instancia nueva está realmente lista.
¿Railway permite deploys sin downtime?
Sí, y los simplifica bastante. En Railway la versión nueva no recibe tráfico hasta que su health check pasa, y si el deploy falla, el tráfico permanece en la versión anterior de forma automática. Para la mayoría de proyectos esto es suficiente. Para casos que exigen zero-downtime absoluto o rollback instantáneo, se complementa con blue-green usando un proxy Nginx o Caddy delante.
Compartir artículo