GitLab Runners
Entendemos un poco los conceptos iniciales, ahora vamos a trabajar con los runners.
Shared Runners
Son runners que son compartidos entre todos los proyectos dentro de una instancia de GitLab. Son proporcionados y gestionados directamente por GitLab. Cuando creas un pipeline en un repositorio, GitLab automáticamente intenta asignar un Shared Runner para ejecutar el trabajo, si el repositorio no tiene runners específicos configurados.
- GitLab cuida de las actualizaciones, mantenimiento y escalabilidad de los runners.
- GitLab puede escalar automáticamente para atender la demanda, asignando recursos conforme sea necesario.
- Gratuito: El uso de Shared Runners es gratuito para cualquier instancia
públicade GitLab (GitLab.com).
¿Dónde está la trampa?
- Los Shared Runners son totalmente
gratuitospararepositorios públicos, sin ninguna limitación de minutos. - Para repositorios privados, todavía tienes acceso a los Shared Runners, pero hay un límite de minutos de ejecución por mes. En el plan gratuito, GitLab ofrece un número fijo de minutos (como por ejemplo, 400 minutos por mes). Después de alcanzar ese límite, necesitarías esperar el próximo ciclo mensual o comprar más minutos. 400 minutos es por cuenta y no por repositorio.
- Si estás en un plan de pago (Premium, Ultimate), el límite de minutos de ejecución para Shared Runners es mucho mayor (con algunos planes ofreciendo hasta 50.000 minutos por mes o más).
GitLab siempre descuenta 1 minuto incluso si tu pipeline se ejecuta por 10 segundos. Si se ejecuta por 1 minuto y 1 segundo descuenta 2 minutos.
Para una empresa que tiene bastante entrega, 400 minutos no dan ni para empezar. Si tu empresa hospeda códigos en GitLab, no paga un plan y no quiere tener problema con el runner, entonces necesitamos proporcionar nuestros propios runners.
El Runner es un agente (servicio) que queda escuchando a GitLab y esperando órdenes. Cuando se dispara un pipeline (por push, merge, manual, etc), GitLab manda la tarea al Runner para ejecutar.
¿Qué hace exactamente el Runner?
- Recibe la instrucción de GitLab CI/CD (con base en el .gitlab-ci.yml).
- Clona el repositorio.
- Lee el pipeline y ejecuta los trabajos, uno a uno.
- Para cada trabajo, necesita un entorno de ejecución.
¿Cómo instalar un Runner?
Si tienes un proyecto en una organización sin estar dentro de un grupo sólo podrías instalar el runner en este proyecto.
¿Qué quiero decir con esto? https://gitlab.com/davidpuziol/devsecops es un proyecto en el namespace personal davidpuziol, es decir, no está dentro de un grupo.
Lo que se podría hacer es registrar un runner en un proyecto, marcarlo como locked: false y run: untagged: true y en cada uno de los proyectos que quisieras usar irías a proyecto > Settings > CI/CD > Runners y activas ese runner manualmente (aparecerá en la lista de "available runners").
No es la mejor cosa a hacer. Lo mejor sería crear un grupo, configurar el gitlab-runner para ese grupo y permitir que el runner sea usado por todos los proyectos dentro de ese grupo.
Como davidpuziol es mi namespace personal, voy a crear un grupo llamado puziol.

Si pensáramos a nivel de empresa, ahora crearíamos otros subgrupos dentro de este grupo, ya no por causa del runner, sino por cuestiones de permisos, variables, etc.
Para instalar un runner para todo ese grupo vamos a Build > Runners y crear un nuevo runner para ese grupo.

Podemos referenciar el runner más tarde por las etiquetas. Es bueno ponerlas.

Lo primero que vamos a hacer es instalar el gitlab-runner que en realidad es un agente, el método con el que ejecutará algo viene después. Si fuéramos a instalar esto dentro de nuestro propio ordenador de desarrollo, el runner sólo estaría disponible cuando el ordenador estuviera encendido. Tú desarrollando algo en tu repositorio, está bien, pero ¿y si otra persona quiere usar el runner y tu ordenador no está en línea?
Vamos a hacer esto para que entiendas.
Como estoy en Mac en este momento voy a seguir el flujo de Mac.
# Cambia para tu sistema operativo
sudo curl --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-arm64
sudo chmod +x /usr/local/bin/gitlab-runner
cd ~
gitlab-runner install
gitlab-runner start
Lo que tenemos hasta ahora es sólo el gitlab-runner, pero necesitamos registrarlo en GitLab.

Al ejecutar tendremos algunas preguntas
❯ gitlab-runner register --url https://gitlab.com --token glrt-xxxxxxxxxxxxxxxxxxxxx
Runtime platform arch=arm64 os=darwin pid=84345 revision=0f67ff19 version=17.11.0
WARNING: Running in user-mode.
WARNING: Use sudo for system-mode:
WARNING: $ sudo gitlab-runner...
# Si estás usando gitlab.com y no es autohospedado entonces puedes presionar enter
Enter the GitLab instance URL (for example, https://gitlab.com/):
[https://gitlab.com]:
Verifying runner... is valid runner=yG54yKH4o
Enter a name for the runner. This is stored only in the local config.toml file:
[MacBookPro.localdomain]: mac-general # Pon un nombre para ese runner que sea fácil identificar de dónde vino
# En este momento avisaremos cuál será el ejecutor. Dónde será ejecutado el código. Como no quiero un entorno aislado voy a poner docker. Observa que podría ser hasta un virtual box.
Enter an executor: ssh, parallels, docker-windows, docker+machine, kubernetes, instance, custom, shell, docker-autoscaler, virtualbox, docker:
docker
# ¿Cuál será la imagen predeterminada si no se especifica en el .gitlab-ci.yml?
Enter the default Docker image (for example, ruby:2.7):
debian:bullseye-slim # Esta es mi elegida y ya vamos a hablar sobre ella.
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Si no definimos una imagen en el archivo de configuración de pipeline entonces vamos a usar esta, pero ¿por qué esta?
Existen dos imágenes que me gusta mucho mantener como predeterminadas. Antiguamente me gustaba usar alpine, pero hoy he pensado más en trabajar con algo de base debian que queda más fácil para los desarrolladores encontrar documentación en internet.
¿Por qué usar debian:bullseye-slim?
- Más ligero que Ubuntu: El slim remueve todo lo innecesario (como docs, dev tools, etc.) quedando más rápida para descargar y arrancar en el pipeline.
- Compatible con la mayoría de los paquetes: Todo lo que funciona en Debian estándar o en Ubuntu probablemente va a funcionar aquí.
- Menos dolor de cabeza que alpine, que rompe compilaciones por usar musl en vez de glibc.
- Más fácil de depurar: Viene con utilidades básicas que no necesitas instalar manualmente, tipo bash, coreutils, etc.
- Base confiable y segura: Debian tiene mantenimiento de seguridad fuerte y la versión bullseye es una de las estables más recientes (aún con actualizaciones).
Entonces volviendo a nuestro grupo, ¿qué tenemos?

Importante... Si definimos que docker será nuestro ejecutor, es necesario que docker esté instalado en el host.
Esto fue mostrado sólo para que entiendas cómo funciona gitlab-runner pero no es así como trabajamos en una empresa. El mejor modo de proporcionar los runners es a través de un clúster kubernetes, principalmente si la cantidad de pipelines es muy alta.
Podríamos instalar vía docker también. Yo hago esto para proyectos personales ejecutando un docker en un homelab que tengo aquí.
mkdir -p $HOME/gitlab-runner/config
docker run -d --name gitlab-runner --restart always \
-v $HOME/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
Lo que el contenedor guarde en /etc/gitlab-runner será expuesto en ~/gitlab-runner/config. Tenemos el gitlab-runner ejecutándose, pero necesitamos configurar, entonces vamos a ejecutar el register dentro del contenedor con el comando exec y seguir el mismo flujo.
❯ docker exec -it gitlab-runner gitlab-runner register
Runtime platform arch=arm64 os=linux pid=13 revision=0f67ff19 version=17.11.0
Running in system-mode.
Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.com/
Enter the registration token:
glrt-xxxxxxxxxxxxxxxxxxxxxx # pega el token aquí que aparecerá en la página
Verifying runner... is valid runner=N9u4maijw
Enter a name for the runner. This is stored only in the local config.toml file:
[67245dbfb3fa]: docker-mac-general
Enter an executor: custom, parallels, docker, docker-windows, instance, shell, ssh, virtualbox, docker+machine, kubernetes, docker-autoscaler:
docker
Enter the default Docker image (for example, ruby:2.7):
debian:bullseye-slim
Runner registered successfully. Feel free to start it, but if its running already the config should be automatically reloaded!
Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
# El archivo que está en /etc/gitlab-runner/config.toml es este.
❯ cat ~/gitlab-runner/config/config.toml
concurrent = 1
check_interval = 0
shutdown_timeout = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "docker-mac-general"
url = "https://gitlab.com/"
id = 47095881
token = "glrt-xxxxxxxxxxxxxxxxxxxxxx"
token_obtained_at = 2025-04-21T18:28:10Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"
[runners.cache]
MaxUploadedArchiveSize = 0
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false
image = "debian:bullseye-slim"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
network_mtu = 0
Cuando defines privileged: true en el runner Docker executor, básicamente estás diciendo:
"Ese contenedor puede hacer casi todo lo que el host puede." Es decir:
- Puede montar volúmenes del host.
- Puede acceder a dispositivos.
- Puede controlar la red.
- Y lo más importante: puede escapar del aislamiento del contenedor, si se utiliza mal.
Pero veremos más adelante que algunas veces esto es necesario, principalmente en el uso de servicios.

Si quieres eliminar es sólo ir en la X y eliminar. Aquellos que no quieras. Esto eliminará este runner del repositorio, pero es bueno remover también lo que hicimos.
Para remover lo que creamos vía docker.
❯ docker rm gitlab-runner --force
❯ docker rmi gitlab-runner:latest
Untagged: docker.io/gitlab/gitlab-runner:latest
Deleted: b717673dd24a18fcc29ea4ca8eee0f3fdfe1563b0393ecd16d11755c5542f01b
rm -rf ~/gitlab-runner/config/config.toml
Para remover el gitlab-runner en el host
gitlab-runner uninstall
rm -rf /usr/local/bin/gitlab-runner
Si fuéramos a instalar el gitlab-runner en un clúster kubernetes podríamos utilizar lo siguiente.
helm repo add gitlab https://charts.gitlab.io
helm repo update
helm show values gitlab/gitlab-runner > values.yaml
Altera los siguientes campos del values.yaml. Para ajustes más finos es bueno estudiar más a fondo sobre esto.
gitlabUrl: https://gitlab.com/
concurrent: 10 # Cuántos pods serán ejecutados al mismo tiempo. Depende del tamaño de la empresa y de cuánto recurso tienes en tu clúster.
rbac:
create: true
rules:
# - apiGroups: [""]
# resources: ["*"]
# verbs: ["*"]
# Analiza las reglas que necesitas. Descomentar lo de arriba daría total poder a este runner.
- resources: ["events"]
verbs: ["list", "watch"]
- resources: ["namespaces"]
verbs: ["create", "delete"]
- resources: ["pods"]
verbs: ["create","delete","get"]
- apiGroups: [""]
resources: ["pods/attach","pods/exec"]
verbs: ["get","create","patch","delete"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get","list"]
- resources: ["secrets"]
verbs: ["create","delete","get","update"]
- resources: ["serviceaccounts"]
verbs: ["get"]
- resources: ["services"]
verbs: ["create","get"]
serviceAccount:
create: true
name: "gitlab-runner-sa"
runners:
# Aquí definimos la imagen predeterminada.
config: |
[[runners]]
[runners.kubernetes]
namespace = "{{.Release.Namespace}}"
image = "debian:bullseye-slim"
tags = "general-debian"
secret: gitlab-runner-token
El secret abajo estaría definido lo que se espera de valores dentro de este secret. El valor runner-registration-token necesita existir aunque no esté definido.
Crea el archivo secret.yaml con el siguiente contenido.
apiVersion: v1
kind: Secret
metadata:
name: gitlab-runner-token
namespace: gitlab
type: Opaque
stringData:
runner-token: "glrt-xxxxxxxxxxxx" # Cambia por tu token
runner-registration-token: ""
kubectl create ns gitlab
kubectl apply -f secret.yaml
helm upgrade --namespace gitlab -f values.yaml gitlab-runner gitlab/gitlab-runner
Sólo un pod existirá en tu clúster. Este es el pod que recibe las llamadas, no el que ejecuta. Cada vez que algo sea ejecutado un nuevo pod será creado en el futuro, por eso tenemos el concurrent: 10 para decir que podemos tener hasta 10 pods al mismo tiempo ejecutando trabajos.
Hosted GitLab Runner
En algunos casos, tu proyecto necesita ejecutarse en un ordenador con una arquitectura específica — algo que puede no estar disponible en tu infraestructura actual. Para eso, GitLab ofrece Hosted Runners, que permiten ejecutar trabajos en entornos gestionados por ellos, con diferentes sistemas operativos y configuraciones.
Puedes usar etiquetas específicas para elegir el tipo de runner necesario, como ordenadores con GPU, arquitectura ARM, macOS, o incluso Windows (en planes de pago).
Consulta abajo algunas opciones disponibles:
⚠️ Si ninguna etiqueta (veremos más adelante) es especificada, GitLab usará por defecto un ordenador Linux x86-64 Small. Dependiendo del tipo de compilación o carga de trabajo, esto puede impactar bastante en el tiempo de ejecución de tus trabajos.
Un ejemplo clásico es el despliegue de aplicaciones iOS en la App Store, que exige un entorno macOS. Si toda tu infraestructura está basada en Linux, los hosted runners de GitLab pueden resolver esto de forma simple e integrada.
Generalmente las etiquetas aparecen de la siguiente forma:
- saas-linux-small-amd64
- saas-linux-medium-arm64
- saas-linux-medium-amd64-gpu-standard
- saas-macos-medium-m1
- saas-macos-large-m2pro
- macos-14-xcode-15
- macos-15-xcode-16
- saas-windows-medium-amd64
Aunque no lo uso, es bueno saber que existe en caso de necesidad.