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:latestEsto 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_successVariables 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-hugoConfigura 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_keysVerificar que funciona#
Haz un push a la rama main:
git add .
git commit -m "Test CI/CD"
git push origin mainEn 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.htmlNotas 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
mainpara evitar deploys accidentales.
Después de tres meses funcionando sin problemas. Es simple pero efectivo.