Para quién es
Subes un binario Go a un servidor y lo corres con systemd. Hoy tu deploy es scp, systemctl restart y un vistazo a los logs. Eso funciona hasta que un reinicio cae en un minuto de mucho tráfico y Caddy responde 502 durante un par de segundos, o hasta que subes un build que arranca bien y luego falla en cada petición, y nada impide que se lleve el tráfico.
Este kit lo sustituye por un script de deploy que arranca la nueva release junto a la anterior, la comprueba antes de que reciba tráfico, hace el cambio y, si la nueva release se estropea, vuelve atrás por sí solo. Es el equivalente para Go y systemd del kit de Node y pm2 que ya vendemos, para quien no usa pm2 ni quiere usarlo.
Qué incluye
deploy.sh: carpetas de release con un symlink atómicocurrent, deploy de un binario compilado en CI o de un build hecho en el servidor, health checks (N respuestas seguidas), cambio, drain, rollback automático,deploy.sh rollbacky un lock para que dos deploys no puedan solaparse.- Unidades de systemd con hardening:
Type=notify, parada ordenada, política de reinicio, un usuario sin privilegios y los secretos en un archivo de entorno con permisos 600 que el usuario del servicio no puede leer. - Un instalador que prepara el usuario, los directorios, las unidades y el archivo de entorno.
- Una pequeña app de ejemplo en Go (solo biblioteca estándar) con graceful shutdown y un helper probado para socket activation y
sd_notify. - Alertas de fallo a Telegram o a un webhook cuando falla un deploy. Los deploys correctos no dicen nada.
- Un banco de pruebas y la salida de su última ejecución.
Dos formas de hacer el cambio
Blue/green (la opción por defecto). Dos instancias, app@a y app@b, detrás de Caddy. La nueva release arranca en el puerto libre y tiene que responder al health check varias veces seguidas antes de que Caddy sepa de ella. Entonces Caddy se recarga sin cortes, y la instancia anterior hace drain y se detiene. Si la nueva release se estropea después del cambio, el script lo deja todo como estaba. El rollback es el mismo movimiento a la inversa, y con KEEP_PREVIOUS=1 la instancia anterior sigue caliente, así que el rollback es solo un cambio en Caddy.
Socket activation. systemd mantiene abierto el socket de escucha mientras el servicio se reinicia, de modo que las conexiones se encolan en vez de rechazarse. La nueva release se ejecuta antes como canary en otro puerto, así que un build roto nunca llega al reinicio.
Blue/green es la opción por defecto porque mantiene la petición más lenta cerca de la duración de la propia petición. El modo socket existe para los casos en que no puedes correr dos instancias: un puerto fijo que no puedes mover, o un recurso que solo un proceso puede retener. Las mediciones de abajo cubren ambos.
Mediciones
Configuración: un container Debian 12 con systemd 252 como PID 1, Caddy 2.6.2, una app de ejemplo que tarda 1.5 s en arrancar y 20 peticiones por segundo constantes a través de Caddy, con tres ejecuciones por escenario. Una petición cuenta como fallida si hay un error de conexión, un timeout o cualquier estado distinto de 200.
| Escenario | Enviadas | Fallidas |
|---|---|---|
systemctl restart normal, una instancia | de 152 a 162 | de 33 a 42 (todas HTTP 502) |
Igual, con lb_try_duration 5s de Caddy | 162 | 0, pero la petición más lenta tardó 2.3 s |
| Blue/green, deploy correcto | de 293 a 295 | 0 |
Blue/green, release cuyo /health devuelve 500 | de 458 a 460 | 0 |
| Blue/green, release que se cae al arrancar | 132 | 0 |
| Blue/green, release que se estropea tras el cambio | de 381 a 383 | 0 (la release mala respondió 182 o 183 peticiones antes del rollback) |
| Blue/green, rollback | de 293 a 295 | 0 |
Blue/green, rollback con KEEP_PREVIOUS=1 | de 222 a 223 | 0, el tráfico cambió a los 0.1 s |
| Blue/green, deploy correcto con peticiones de 2 s en curso | 234 | 0, las peticiones lentas terminaron, no se cortaron |
| Modo socket, deploy correcto | de 268 a 269 | 0 |
| Modo socket, deploy correcto con peticiones de 2 s en curso | de 307 a 310 | 0, la petición más lenta tardó 5.65 s |
Un deploy en modo blue/green tarda de 8 a 9 segundos, y el tráfico está en la nueva release a los 3.6 segundos aproximadamente. Esas son las cifras de esta app y de esta máquina.
Cómo transcurre el deploy
El README lo recorre en siete pasos. En resumen: se toma el lock, se crea la carpeta de release, se arranca la instancia libre y se espera a que avise de que está lista, se le hace el health check directamente, se escribe el nuevo upstream y se recarga Caddy, se cambia el symlink, se espera, se vuelve a comprobar y solo entonces se detiene la instancia anterior. Cada paso tiene definido su camino de fallo, y los que importan están probados: una release que no pasa el health check, una que se cae al arrancar y una que se estropea después del cambio.
Requisitos
Linux con systemd, Caddy, curl y flock. Go y git solo si compilas en el servidor. Un servicio en un host.
Preguntas frecuentes
Uso Node y pm2. ¿Es para mí?
No; mira el VPS Deploy & Backup Kit, que hace el mismo trabajo para ese stack.
¿Necesito Caddy?
Blue/green lo necesita para el cambio. Medimos ambos modos con Caddy delante.
¿Funcionará en Ubuntu?
Ubuntu también usa systemd y el kit solo necesita systemd y Caddy, así que encaja. Lo probamos en Debian 12.
¿Qué hace un segundo deploy mientras hay otro en marcha?
Termina al instante con el código 75 y no envía ninguna alerta. Eso es el lock.
Licencia
Úsalo en tus propios servidores. Por favor, no revendas los archivos.
Historial de cambios
- 0.1.0: primera versión.
Leer más
- What a plain systemctl restart costs a Go service under load (en inglés)
- systemd socket activation: no dropped requests, but a cost (en inglés)
- Roll back a bad deploy in a minute with release folders (en inglés)
- Database migrations that survive a zero-downtime deploy (en inglés)
- Deploy health checks that catch bad releases (en inglés)
- Caddy blue/green config for a one-server app, line by line (en inglés)
Obtener
Polar, como merchant of record, se ocupa del pago, los impuestos y las facturas. Al terminar la compra recibes un enlace de descarga.