Hay una cosa que me sigue haciendo gracia del mundo self-hosted. Montamos infra cada vez más compleja para acabar resolviendo tareas que, en el fondo, se arreglan con dos comandos bien puestos y un poco de criterio. Publicar una web estática es una de ellas.
He probado flujos más elaborados. CI/CD con más capas, builders remotos, pipelines con demasiadas piezas, deploys muy listos sobre el papel y algo menos listos cuando fallan a las dos de la mañana. Al final he vuelto a lo que mejor me funciona para proyectos pequeños y medianos que controlo yo: Hugo para generar el sitio y rsync para dejarlo en el servidor.
No es sexy. Precisamente por eso me gusta.
Si tienes un homelab, un VPS barato o una máquina en casa detrás de un proxy, este enfoque encaja muy bien. No dependes de un servicio externo para publicar cada cambio, entiendes exactamente qué pasa en cada paso y, cuando algo se rompe, el radio de explosión es pequeño. Eso en un entorno personal vale bastante más que un dashboard bonito.
Por qué sigo prefiriendo esto a un pipeline más aparatoso#
No tengo nada contra Netlify, Vercel o Cloudflare Pages. De hecho, para ciertos proyectos son opciones cojonudas. Las usas, te olvidas y listo. Pero cuando una web vive dentro de tu propia infraestructura o forma parte del ecosistema del homelab, a mí me gusta que el despliegue también viva ahí.
Hay tres motivos muy concretos.
El primero es control. No tengo que esperar a que un tercero construya nada por mí ni adaptar mi flujo a cómo le gusta trabajar a una plataforma externa.
El segundo es simplicidad real. El verbo importante aquí es real. Mucha gente confunde simplicidad con ocultación. Un panel con cuatro botones puede parecer simple, pero si debajo hay veinte pasos que no controlas, solo has cambiado claridad por decoración.
El tercero es velocidad de diagnóstico. Si el build falla, lo veo al momento. Si el rsync no sincroniza algo, sé dónde mirar. Si un permiso quedó mal en el servidor, la solución está ahí mismo. No tengo que ir persiguiendo logs repartidos por media docena de servicios.
El caso de uso donde este método brilla#
No sirve para todo. Pero para esto sí me parece redondo.
- blogs y documentación en Hugo
- sitios estáticos corporativos pequeños
- webs personales
- microsites de campaña
- páginas internas del lab
- documentación publicada desde un repo Git
Cuando el sitio son HTML, CSS, JS y recursos generados localmente, el despliegue con rsync tiene una lógica casi insultante de lo bien que funciona. Generas la carpeta public, sincronizas contra el destino, eliminas lo que sobra y sigues con tu vida.
Eso sí, conviene decir una cosa. Si estás desplegando una aplicación compleja con build distribuido, secretos delicados, previews por rama, equipos grandes y múltiples entornos, seguramente te compense algo más formal. Este artículo va de otra liga. Va de hacerlo bien sin montar una feria.
Mi flujo real, que no tiene misterio y justo por eso merece la pena#
Mi rutina suele ser esta.
- Escribo o actualizo contenido en el repo.
- Lanzo
hugo --minifypara generar el sitio final. - Reviso que no haya errores de build.
- Sincronizo
public/con el servidor usando rsync. - Ajusto permisos si el destino lo necesita.
- Compruebo la web en vivo.
No hay mucho más. Y me encanta poder decir eso sin que sea una carencia.
En muchos proyectos personales el problema no es técnico. El problema es que convertimos una tarea trivial en una cadena de dependencias innecesarias porque nos da paz mental pensar que “ya está profesionalizado”. Luego una actualización de la pipeline te rompe el deploy y la profesionalización se convierte en una excursión de depuración.
Aquí no. Aquí el flujo cabe en un script corto y lo entiendes entero.
La base del sistema: Hugo hace su parte y no molesta#
Una de las razones por las que este enfoque funciona tan bien es Hugo. Genera muy rápido, no pide demasiada infraestructura y deja una carpeta final lista para servir con cualquier nginx, Caddy o servidor HTTP decente.
Eso cambia bastante la conversación frente a otros stacks. Si tu generador tarda una eternidad, depende de media luna de paquetes y necesita un entorno delicado para compilar, ya empiezas el despliegue con mala cara. Hugo normalmente va como un tiro.
Para mí tiene dos ventajas enormes en este contexto.
La primera es la velocidad. Puedes regenerar el sitio constantemente sin sentir que estás lanzando un trabajo de CI por cada coma cambiada.
La segunda es la predictibilidad. Lo que sale de hugo --minify es la versión final. No hay demasiada magia intermedia.
El script que me gusta tener a mano#
No necesitas un script, pero ayuda. Yo prefiero dejarlo empaquetado para no repetir pasos y no olvidarme de nada. Una versión genérica quedaría así:
| |
Ya está. Ese es el corazón del sistema.
Hay gente que ve esto y piensa que es demasiado simple para ser serio. Yo veo lo contrario. Si una tarea puede resolverse bien con algo así, complicarla no te hace más profesional. Te hace más propenso a arreglar cosas que tú mismo has sobrearquitectado.
Qué hace cada parte y por qué está ahí#
set -e#
Lo primero que quiero en un deploy pequeño es que el script falle en cuanto una parte se tuerce. No quiero que el build reviente y aun así intente sincronizar una carpeta a medias. Tampoco quiero que falle el rsync y el script siga sonriendo como si nada.
hugo --minify#
Aquí genero el sitio final y además reduzco un poco el peso del resultado. No espero milagros de la minificación, pero ya que estoy, prefiero publicar la versión compacta.
Si el proyecto tiene problemas de plantillas, contenido roto o referencias incorrectas, este paso me lo deja claro antes de tocar el servidor.
rsync -avz --delete#
Esta es la joya del asunto.
-amantiene la sincronización de forma cómoda para archivos y directorios-vda visibilidad-zcomprime durante el envío cuando compensa--deleteelimina en el destino lo que ya no existe en local
Ese --delete hay que usarlo con respeto, pero también con convicción. Si no lo usas, el servidor acaba acumulando basura vieja y te preguntas por qué siguen apareciendo recursos o páginas que creías borradas hace una semana.
Yo prefiero que el destino sea un espejo limpio de public/. Para eso está rsync.
Ajuste de permisos#
Esta parte me la enseñó la experiencia, que suele ser un profesor algo borde.
Si despliegas desde macOS hacia un servidor Linux con nginx, a veces te puedes encontrar con permisos menos amables de lo que esperabas. El resultado es tan elegante como un 403 servido con indiferencia por el servidor web.
Por eso me gusta rematar con un ajuste claro de permisos para archivos y directorios. No es la parte más sofisticada del mundo, pero me ha evitado más de un “pero si el deploy fue bien, qué demonios pasa ahora”.
Lo bueno de rsync frente a copiar archivos a pelo#
A veces alguien propone la versión cavernícola del asunto. Genera la carpeta y súbela entera con SCP. Funciona, sí. Igual que también funciona pegar un mueble con cinta americana.
rsync tiene varias ventajas prácticas.
Solo mueve lo que cambió#
En sitios con bastantes imágenes o recursos, esto se nota mucho. No vuelves a mandar todo el proyecto por capricho. Envías las diferencias.
Mantiene el destino limpio#
Con --delete evitas la clásica colección de restos arqueológicos de versiones anteriores.
Es fácil de automatizar#
No necesitas montar nada raro. Lo metes en un script, en un cron o en una tarea manual y listo.
Te da visibilidad suficiente#
No tiene una UI preciosa, pero te dice bastante sobre lo que está pasando. Para mí eso vale más que un check verde sin contexto.
Dónde lo he visto fallar de verdad#
Aquí viene la parte útil. El método es simple, pero no infalible. Estos son los fallos más típicos que me he encontrado.
1. El build pasa, pero el sitio queda roto#
Esto suele ocurrir cuando el problema no está en Hugo sino en la lógica del contenido. Enlaces mal escritos, assets que cambian de sitio, rutas rotas, plantillas que asumen algo que ya no existe. El deploy aquí no tiene culpa.
Por eso me gusta revisar localmente antes de empujar. Parece obvio y aun así mucha gente lo trata como una sugerencia decorativa.
2. Permisos raros en el servidor#
Ya lo comenté antes, pero merece repetirlo. Si el servidor recibe archivos con permisos poco adecuados, nginx o el servicio que uses puede negarse a servirlos. No es un fallo complejo. Es un fallo cabrón porque parece que todo fue bien hasta que abres la web.
3. Sincronizar al destino equivocado#
Aquí rsync no perdona. Si defines mal la ruta remota y llevas --delete, el susto puede ser memorable.
Mi consejo es simple. Usa variables claras, revisa el destino antes de automatizar nada y, si es la primera vez, prueba sin --delete o ejecuta un --dry-run.
4. Confundir contenido generado con repositorio fuente#
La carpeta public/ es un artefacto de build. No la trato como fuente de verdad. La fuente vive en el repo. Parece de primero de despliegues, pero cuando alguien empieza a editar cosas directamente en el servidor, el sistema se convierte en una trampa.
Lo que gané al dejar de perseguir pipelines más complejas#
Lo más valioso no es técnico. Es mental.
Con un flujo Hugo + rsync tengo menos superficie de fallo, menos piezas que vigilar y menos tiempo perdido en mantenimiento de la propia maquinaria de despliegue. Puedo dedicar más atención al contenido, al diseño del sitio o a cualquier otra cosa más interesante que pelearme con una cadena de CI que hoy decidió reinventarse.
También hay otra ventaja menos evidente. Cuando el deploy depende de un script corto, la documentación casi se escribe sola. Cualquier persona que mire el archivo entiende en un minuto qué pasa. Si un día vuelvo a tocar ese proyecto dentro de seis meses, no necesito reconstruir un mapa mental absurdo.
Y eso, para mí, es oro puro.
Cuándo sí usaría otra cosa#
No me caso con este método por romanticismo. Hay contextos donde no es la mejor opción.
Buscaría algo más avanzado si necesito:
- previews automáticas por rama
- varios entornos con aprobaciones
- despliegues coordinados en equipo
- integraciones fuertes con tests y checks previos
- publicación distribuida en edge sin gestionar origen propio
Ahí sí tiene sentido mirar plataformas gestionadas o pipelines más formales.
Pero incluso en esos casos intentaría no perder una idea básica. La complejidad tiene que pagar alquiler. Si añades tres capas nuevas, deberían resolverte un problema real, no darte solo la sensación de que ahora el sistema parece más de empresa.
Consejos que me han ahorrado tiempo#
Voy a dejar algunos muy concretos porque son de los que luego echas de menos.
Usa rutas remotas explícitas#
Nada de variables ambiguas o directorios a medias. Quiero ver claro a dónde va el contenido.
Ten acceso SSH limpio y sin fricción#
Si cada deploy te pide malabares, acabarás buscando atajos peores. Claves SSH bien montadas y fuera drama.
Prueba local antes de sincronizar#
Parece una obviedad. Sigue siendo necesaria.
No mezcles la publicación con cambios manuales en el servidor#
Servidor de destino limpio. Repo como fuente. Punto.
Si el sitio es importante, guarda copia del contenido fuente#
rsync sincroniza el resultado, no sustituye un backup del proyecto.
Si te preocupa romper algo, usa --dry-run#
No es cobardía. Es higiene.
Mi opinión después de bastante darle vueltas#
Cada vez me interesan menos las pipelines que impresionan y más las que sobreviven al uso diario. Con el tiempo uno aprende a desconfiar un poco de las soluciones que parecen demasiado elegantes sobre una presentación y luego te exigen media tarde de mantenimiento cuando un detalle cambia.
Hugo + rsync tiene algo muy valioso. No pretende ser más de lo que es. Genera archivos. Los sincroniza. Mantiene el destino al día. Fin.
Y justo ahí está su fuerza.
Si tienes un homelab y quieres publicar una web estática sin depender de medio planeta para cada cambio, este método sigue siendo buenísimo. Barato, rápido, entendible y fácil de reparar. No necesito mucho más para recomendarlo.
A veces la mejor automatización no es la que hace veinte cosas. Es la que hace cuatro, las hace bien y no te pide una relación tóxica a cambio.
Con los despliegues estáticos, rsync lleva muchos años demostrando que sigue en forma. Y Hugo es el compañero perfecto para aprovecharlo sin montar un circo alrededor.
Yo, desde luego, no tengo ninguna prisa por cambiar esta pareja por algo más moderno solo para que parezca más moderno. Ya he caído en esa trampa otras veces y casi siempre acaba igual: más paneles, más magia y bastante menos paz.
Prefiero esto. Funciona. Entiendo cada pieza. Y cuando publico algo, sé exactamente qué ha pasado entre mi repo y la web final. En un homelab, esa claridad vale más que muchas promesas de comodidad.