Go + systemd Zero-Downtime Deploy Kit

Publica un servicio Go en un VPS sin perder peticiones, usando solo systemd, Caddy y bash. Sin pm2, sin containers.

Comprar en Polar: $29 Pago único · v0.1.0 · descarga inmediata con Polar

Portada de Go + systemd Zero-Downtime Deploy Kit
Salida de una ejecución de pruebas real, no una maqueta.

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ómico current, 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 rollback y 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.

EscenarioEnviadasFallidas
systemctl restart normal, una instanciade 152 a 162de 33 a 42 (todas HTTP 502)
Igual, con lb_try_duration 5s de Caddy1620, pero la petición más lenta tardó 2.3 s
Blue/green, deploy correctode 293 a 2950
Blue/green, release cuyo /health devuelve 500de 458 a 4600
Blue/green, release que se cae al arrancar1320
Blue/green, release que se estropea tras el cambiode 381 a 3830 (la release mala respondió 182 o 183 peticiones antes del rollback)
Blue/green, rollbackde 293 a 2950
Blue/green, rollback con KEEP_PREVIOUS=1de 222 a 2230, el tráfico cambió a los 0.1 s
Blue/green, deploy correcto con peticiones de 2 s en curso2340, las peticiones lentas terminaron, no se cortaron
Modo socket, deploy correctode 268 a 2690
Modo socket, deploy correcto con peticiones de 2 s en cursode 307 a 3100, 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

Obtener

Comprar en Polar: $29

Polar, como merchant of record, se ocupa del pago, los impuestos y las facturas. Al terminar la compra recibes un enlace de descarga.