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:1313Desde 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
.gitignoremínimo:/public/ /resources/_gen/ .hugo_build.lockEl 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/pruebaSi 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.debSi 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:
Repositorio remoto con la URL SSH:
git@github.com:usuario/repo.git.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-onlyRama
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 --minifyumask 022deja ficheros en 644 y carpetas en 755, que el servidor web puede leer.--cleanDestinationDirborra de la carpeta pública lo que ya no existe en el sitio. Revisa antes que no haya nada tuyo enhttpdocs, porque lo eliminará.--cacheDirfuera 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]=jsonPara 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 /httpdocsEntre el push y la web actualizada pasan unos segundos.
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.



