↓ Ir al contenido
  1. Artículos/

Hugo en WSL y Ubuntu Server, con despliegue automático desde GitHub

·4 mins· ·
Antonio Rueda
Autor
Antonio Rueda
Escribo sobre desarrollo, matemáticas aplicadas y gestión de equipos en este cuaderno público.
Tabla de contenido

Este es el montaje con el que publico encodigo.es: escribo en WSL, hago git push y, unos segundos después, el artículo está en la web. Sin CI intermedio, sin rama de artefactos y sin subir ficheros a mano.

El truco no es ninguna herramienta nueva: es que el servidor compile el sitio él mismo cada vez que GitHub le avisa.

Lo cuento en orden: Hugo en local, el repositorio, Hugo en el servidor y el despliegue.

1. Hugo en WSL (o en cualquier Ubuntu)
#

WSL es un Ubuntu de verdad, así que los pasos valen igual para un portátil con Linux.

Evita el Hugo de apt: suele ir varias versiones por detrás, y los temas modernos exigen una versión mínima. Descarga el paquete extended de la página de releases de Hugo en GitHub:

VER=0.166.0
wget https://github.com/gohugoio/hugo/releases/download/v${VER}/hugo_extended_${VER}_linux-amd64.deb
sudo apt install ./hugo_extended_${VER}_linux-amd64.deb
hugo version   # debe decir "+extended"

Fija la versión: el tema declara con qué versiones de Hugo es compatible, y conviene usar la misma en local y en el servidor.

Para trabajar:

hugo new content posts/mi-articulo.md   # crea un borrador
hugo server -D                          # vista previa con borradores en http://localhost:1313

Desde Windows, http://localhost:1313 funciona directamente: WSL reenvía el puerto.

2. El repositorio
#

Tres decisiones que luego me ahorraron problemas:

  • El tema, copiado dentro del repo, no como submódulo. El servidor clona el repositorio tal cual y no tiene que resolver nada más.

  • Un .gitignore mínimo:

    /public/
    /resources/_gen/
    .hugo_build.lock
  • El build no puede depender de internet. Si el tema descarga algo de un CDN durante la compilación, el día que el servidor no pueda salir, o no confíe en el certificado, el despliegue se rompe. Lo compruebo en local simulando un entorno sin certificados ni red:

    SSL_CERT_FILE=/nonexistent HTTPS_PROXY=http://127.0.0.1:9 hugo --minify -d /tmp/prueba

    Si eso compila sin errores, compilará en el servidor.

3. Hugo en Ubuntu Server
#

En el servidor, lo mismo que en local: el .deb extended de la misma versión.

sudo apt install ./hugo_extended_0.166.0_linux-amd64.deb

Si el sitio lo gestiona Plesk y la suscripción usa una shell enjaulada (chrootsh), hay un paso más: los comandos del despliegue se ejecutan dentro de la jaula, así que Hugo tiene que estar disponible también ahí.

Dentro de esa jaula, la raíz / es la carpeta del dominio (/var/www/vhosts/tu-dominio). Por eso en la configuración de Plesk verás rutas como /httpdocs en lugar de la ruta completa.

4. Despliegue automático con Plesk Git
#

Conectar el repositorio
#

En Sitios web y dominios → Git → Añadir repositorio:

  1. Repositorio remoto con la URL SSH: git@github.com:usuario/repo.git.

  2. Plesk muestra una clave pública SSH. Añádela en GitHub como deploy key de solo lectura:

    gh repo deploy-key add plesk.pub --repo usuario/repo --title "plesk"
    gh repo deploy-key list --repo usuario/repo   # comprueba que pone read-only
  3. Rama main, modo automático y ruta /hugo. Ahí se guarda el código fuente, fuera de la carpeta pública.

La acción de despliegue
#

Activa las acciones adicionales y pon una sola línea:

umask 022; /usr/local/bin/hugo --source /hugo --destination /httpdocs --cacheDir /hugo-cache --cleanDestinationDir --minify
  • umask 022 deja ficheros en 644 y carpetas en 755, que el servidor web puede leer.
  • --cleanDestinationDir borra de la carpeta pública lo que ya no existe en el sitio. Revisa antes que no haya nada tuyo en httpdocs, porque lo eliminará.
  • --cacheDir fuera del sitio conserva las imágenes ya procesadas entre despliegues.

Antes de apuntar a /httpdocs, pruébalo con una carpeta de staging (--destination /staging) y revisa el resultado.

El webhook
#

Plesk muestra una URL de webhook en la configuración del repositorio. Contiene un token, así que no la guardes en el repo ni la pegues en ningún sitio público. Se da de alta en GitHub:

gh api repos/usuario/repo/hooks \
  -f name=web -F active=true \
  -f 'events[]=push' \
  -f config[url]="$WEBHOOK_URL" \
  -f config[content_type]=json

Para probarlo, un commit vacío:

git commit --allow-empty -m "Prueba de webhook" && git push
gh api repos/usuario/repo/hooks/ID/deliveries --jq '.[0] | "\(.event) \(.status_code)"'

Plesk responde 204: es un éxito, solo que sin cuerpo.

5. Servir el sitio
#

Comprueba qué servidor web tienes delante. En mi caso no había nginx, solo Apache, así que la caché, la compresión, la página 404 y las cabeceras de seguridad van en un .htaccess dentro de static/, que Hugo copia a la raíz en cada build.

En la configuración de hosting del dominio, desactiva PHP: el sitio es estático y así hay una cosa menos que atacar. Deja activada la redirección 301 a HTTPS.

El flujo, al final
#

escribo en WSL → hugo server -D → draft: false → git push
      → webhook → Plesk trae main a /hugo → hugo compila en /httpdocs

Entre el push y la web actualizada pasan unos segundos.

Cuidado con las fechas. Hugo no publica artículos con fecha futura, y una fecha sin hora (2026-09-26) se interpreta como medianoche UTC: un artículo de “hoy” puede no salir hasta las dos de la madrugada. No lo arregles con timeZone en la configuración si compilas dentro de un chroot: sin /usr/share/zoneinfo el build falla entero. Pon la hora con su zona en la propia fecha: 2026-09-26T00:00:00+02:00.

¿Comentarios o correcciones? info@encodigo.es.

Relacionados