GitLab Runner es el agente que ejecuta los jobs definidos en .gitlab-ci.yml. Tener un runner propio te da control sobre el hardware, la caché y las herramientas disponibles, y no consume los minutos de cómputo de GitLab.com. En este tutorial instalarás GitLab Runner en Ubuntu 24.04 desde el repositorio oficial, lo registrarás con el executor Docker, ajustarás la concurrencia y comprobarás que ejecuta un pipeline real.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 GB de RAM y 20 GB libres en disco para imágenes y caché.
  • Un usuario no root con privilegios sudo.
  • Docker Engine instalado desde el repositorio oficial de Docker (el executor Docker lo necesita).
  • Un proyecto en GitLab.com o en tu propia instancia de GitLab, con rol Maintainer u Owner para crear runners.

El runner solo abre conexiones salientes hacia GitLab por HTTPS, así que no necesitas abrir puertos de entrada en el firewall.

Paso 1: Instalar GitLab Runner

GitLab publica un script que añade su repositorio APT y la clave de firma. Descárgalo primero para poder revisarlo antes de ejecutarlo:

curl -fsSL -o gitlab-runner-repo.sh https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh
less gitlab-runner-repo.sh

Ejecútalo e instala el paquete:

sudo bash gitlab-runner-repo.sh
sudo apt install gitlab-runner

El paquete crea el usuario de sistema gitlab-runner y el servicio gitlab-runner, que queda habilitado y en marcha. Compruébalo:

gitlab-runner --version
sudo systemctl status gitlab-runner --no-pager
Version:      18.x.x
...
● gitlab-runner.service - GitLab Runner
     Loaded: loaded (/etc/systemd/system/gitlab-runner.service; enabled; preset: enabled)
     Active: active (running)

Paso 2: Crear el runner en GitLab

Desde GitLab 16 los runners se crean primero en la interfaz, que genera un token de autenticación (empieza por glrt-). Los antiguos tokens de registro están obsoletos.

  1. En tu proyecto, ve a Settings > CI/CD y despliega Runners.
  2. Pulsa New project runner (en algunas versiones, Create project runner).
  3. En Tags escribe docker,linux. Deja Run untagged jobs desmarcado si quieres que el runner solo acepte jobs con esas etiquetas.
  4. Opcionalmente añade una descripción y un Maximum job timeout.
  5. Pulsa Create runner.

GitLab muestra el token glrt-... una sola vez. Cópialo; lo usarás en el siguiente paso.

Paso 3: Registrar el runner con el executor Docker

El executor Docker ejecuta cada job en un contenedor limpio a partir de la imagen indicada en .gitlab-ci.yml, lo que evita que los jobs se contaminen entre sí. Registra el runner sustituyendo your_runner_token por el token del paso anterior. Si usas una instancia propia, cambia también la URL:

sudo gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.com" \
  --token "your_runner_token" \
  --name "ci-runner-01" \
  --executor "docker" \
  --docker-image "alpine:latest"

--name es el nombre local del runner en config.toml y --docker-image es la imagen por defecto para jobs que no declaran image:. Las etiquetas, la descripción y el tiempo máximo ya se definieron en la interfaz.

Runtime platform                                    arch=amd64 os=linux ...
Verifying runner... is valid                        runner=AbCdEf12
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

Comprueba que el runner está registrado y que GitLab lo acepta:

sudo gitlab-runner list
sudo gitlab-runner verify

En GitLab, en Settings > CI/CD > Runners, el runner aparece con un punto verde (Online) al cabo de unos segundos.

Paso 4: Ajustar config.toml

El registro escribe la configuración en /etc/gitlab-runner/config.toml. Ábrelo:

sudo nano /etc/gitlab-runner/config.toml

Verás algo parecido a esto. Los cambios más útiles son concurrent (cuántos jobs puede ejecutar el servicio a la vez, por defecto 1) y los límites de recursos de los contenedores:

concurrent = 4
check_interval = 0

[[runners]]
  name = "ci-runner-01"
  url = "https://gitlab.com"
  token = "glrt-..."
  executor = "docker"
  [runners.docker]
    image = "alpine:latest"
    privileged = false
    pull_policy = ["if-not-present"]
    memory = "2g"
    cpus = "1.5"
    volumes = ["/cache"]
    shm_size = 0
  • concurrent = 4: hasta cuatro jobs en paralelo. Ajústalo a los núcleos y la RAM del servidor.
  • pull_policy = ["if-not-present"]: reutiliza imágenes ya descargadas, lo que acelera mucho los jobs. Usa always si necesitas garantizar siempre la última versión de una etiqueta.
  • memory y cpus: límites por contenedor de job, para que un job descontrolado no tumbe el servidor.
  • volumes = ["/cache"]: volumen donde el runner guarda la caché definida con cache: en los jobs.

GitLab Runner detecta los cambios en config.toml y los recarga solo, pero reiniciar el servicio confirma que el archivo es válido:

sudo systemctl restart gitlab-runner
sudo journalctl -u gitlab-runner -n 20 --no-pager

Si hay un error de sintaxis, aparecerá en el log.

Paso 5: Ejecutar un pipeline de prueba

Crea o edita .gitlab-ci.yml en la raíz del proyecto. Las tags hacen que GitLab envíe los jobs a tu runner:

stages:
  - test

default:
  tags:
    - docker

check-runner:
  stage: test
  image: python:3.12-slim
  script:
    - python --version
    - echo "Ejecutado en $CI_RUNNER_DESCRIPTION"
  cache:
    key: "$CI_COMMIT_REF_SLUG"
    paths:
      - .cache/pip

Haz commit y push. En Build > Pipelines abre el job; el log debe empezar indicando tu runner y el executor:

Running with gitlab-runner 18.x.x (...)
  on ci-runner-01 AbCdEf12, system ID: s_...
Preparing the "docker" executor
Using Docker executor with image python:3.12-slim ...
...
$ python --version
Python 3.12.x
Job succeeded

En el servidor puedes seguir la actividad del runner en tiempo real:

sudo journalctl -u gitlab-runner -f

Paso 6: Construir imágenes Docker desde los jobs (opcional)

Para ejecutar docker build dentro de un job hay que usar Docker-in-Docker, que necesita contenedores privilegiados. Actívalo solo en runners dedicados a proyectos de confianza, porque un job privilegiado puede tomar el control del servidor.

En /etc/gitlab-runner/config.toml, dentro de [runners.docker], cambia privileged y añade el volumen de certificados:

    privileged = true
    volumes = ["/certs/client", "/cache"]

Reinicia el servicio:

sudo systemctl restart gitlab-runner

Un job que construye y publica en el registro de contenedores de GitLab queda así:

build-image:
  stage: build
  image: docker:27
  services:
    - docker:27-dind
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  tags:
    - docker
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
    - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" .
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"

Recuerda añadir build a la lista stages del archivo.

Alternativa: executor shell

Si prefieres que los jobs se ejecuten directamente en el servidor, con las herramientas instaladas en el sistema, registra el runner con --executor "shell" y sin --docker-image. Los jobs se ejecutan como el usuario gitlab-runner, en su directorio /home/gitlab-runner. Es más rápido, pero los jobs comparten estado y cualquier job puede leer lo que dejaron los anteriores.

Mantenimiento

Actualiza el runner junto con el resto del sistema; el servicio se reinicia solo tras la actualización:

sudo apt update && sudo apt install --only-upgrade gitlab-runner

El executor Docker acumula imágenes y volúmenes de caché. Revisa el espacio con docker system df y limpia periódicamente lo que no se usa:

sudo docker system prune --filter "until=168h"

Para retirar un runner, bórralo en GitLab (Settings > CI/CD > Runners) y elimina su entrada local:

sudo gitlab-runner unregister --name ci-runner-01

Haz copia de /etc/gitlab-runner/config.toml: contiene los tokens y toda la configuración del runner.

Solución de problemas

El job se queda en pending con el mensaje "This job is stuck": ningún runner online tiene todas las etiquetas del job. Comprueba que las tags del job coinciden con las del runner, o marca Run untagged jobs en el runner.

Cannot connect to the Docker daemon at unix:///var/run/docker.sock: Docker no está instalado o no está en marcha. Comprueba con sudo systemctl status docker.

Con executor shell: ERROR: Job failed: prepare environment: exit status 1: en Ubuntu, el archivo /home/gitlab-runner/.bash_logout limpia la consola y rompe la preparación del job. Elimínalo con sudo rm /home/gitlab-runner/.bash_logout.

Jobs Docker-in-Docker fallan con error during connect: falta privileged = true, el volumen /certs/client o la variable DOCKER_TLS_CERTDIR en el job.

Conclusión

Tienes un GitLab Runner propio en Ubuntu 24.04, registrado con un token de autenticación, ejecutando cada job en un contenedor aislado con límites de CPU y memoria y caché local. Como siguientes pasos, puedes añadir más runners con etiquetas distintas para separar cargas, construir imágenes con Docker-in-Docker en un runner dedicado o añadir análisis de calidad con SonarQube a tus pipelines.