Ir al contenido

CI/CD con GitLab Runner local para desplegar automáticamente un blog Hugo

·3 mins
Rogelio Guerra Riverón
Autor
Rogelio Guerra Riverón
Construyendo mi propia infraestructura web desde cero. Aquí documento cada paso: servidores, redes, contenedores y lo que vaya surgiendo.

Introducción
#

Cansado de hacer deploy manual de mi blog Hugo cada vez que publico un artículo. Decidí montar un pipeline CI/CD local con GitLab Runner. El resultado: automático, confiable y sin depender de servicios externos.

Requisitos previos
#

Necesitas:

  • Un servidor con Docker instalado
  • Un repositorio en GitLab (puede ser autohospedado o gitlab.com)
  • Hugo instalado localmente para testing
  • Acceso SSH configurado en tu servidor

Instalación de GitLab Runner
#

Lo primero es instalar GitLab Runner en tu servidor. Yo lo hice en Docker porque ya tenía el demonio corriendo.

docker pull gitlab/gitlab-runner:latest

docker run -d --name gitlab-runner \
  --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  gitlab/gitlab-runner:latest

Esto monta el socket de Docker para que el runner pueda ejecutar contenedores anidados. Importante para construir imágenes.

Registrar el Runner
#

Necesitas un token de tu proyecto GitLab. Lo encuentras en: Configuración del proyecto → CI/CD → Runners

Luego ejecutas:

docker exec -it gitlab-runner gitlab-runner register \
  --url https://gitlab.com/ \
  --registration-token TU_TOKEN_AQUI \
  --executor docker \
  --docker-image alpine:latest \
  --docker-volumes /var/run/docker.sock:/var/run/docker.sock \
  --description "Runner Local Hugo"

Escoge Docker como executor. Es lo más limpio para este caso.

Configurar el pipeline
#

En la raíz de tu repositorio creas .gitlab-ci.yml:

stages:
  - build
  - deploy

variables:
  DEPLOY_PATH: /home/deploy/blog-hugo/public

build:
  stage: build
  image: alpine:latest
  before_script:
    - apk add --no-cache hugo git
  script:
    - hugo --minify
    - echo "Build completado"
  artifacts:
    paths:
      - public/
    expire_in: 1 week
  only:
    - main

deploy:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache openssh-client rsync
    - mkdir -p ~/.ssh
    - echo "$DEPLOY_KEY" | base64 -d > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H localhost >> ~/.ssh/known_hosts
  script:
    - rsync -avz --delete public/ deploy@localhost:${DEPLOY_PATH}
    - ssh deploy@localhost 'sudo systemctl restart nginx'
  only:
    - main
  when: on_success

Variables de entorno
#

En Configuración del proyecto → CI/CD → Variables, añade:

  • DEPLOY_KEY: Tu clave SSH privada en base64 (cat ~/.ssh/id_rsa | base64 -w0)
  • DEPLOY_PATH: Ruta donde quieres los archivos (yo uso /home/deploy/blog-hugo/public)

Usuario de deploy
#

En tu servidor creas un usuario específico:

sudo useradd -m -s /bin/bash deploy
sudo usermod -aG docker deploy
sudo mkdir -p /home/deploy/blog-hugo/public
sudo chown deploy:deploy /home/deploy/blog-hugo

Configura la clave SSH pública del runner:

sudo -u deploy ssh-keygen -t ed25519 -N "" -f /home/deploy/.ssh/id_rsa
cat /home/deploy/.ssh/id_rsa.pub >> /home/deploy/.ssh/authorized_keys

Verificar que funciona
#

Haz un push a la rama main:

git add .
git commit -m "Test CI/CD"
git push origin main

En GitLab ves el pipeline en tiempo real. Si todo está bien, en segundos tu blog estará desplegado.

sudo -u deploy cat /home/deploy/blog-hugo/public/index.html

Notas finales
#

  • El runner local nunca sale de tu red. Total control.
  • Los tiempos de build son rápidos porque todo está en la máquina local.
  • Si necesitas cachear dependencias, configura volumes persistentes en Docker.
  • He puesto restricciones a la rama main para evitar deploys accidentales.

Después de tres meses funcionando sin problemas. Es simple pero efectivo.