TIL: cómo ejecutar un Forgejo Runner local

· Iván Hernández Cazorla - Blog

Hoy he aprendido a ejecutar mis trabajos de CI en Codeberg usando un Forgejo Runner en un dispositivo local


English version available


Hoy, o ayer, según cómo se mire, tuve uno de esos días en los que uno está tan cansado que no consigue concentrarse en lo que debe, pero tampoco quiere echarse una siesta. Y como no, terminé procrastinando. Me vino a la cabeza el sistema de Forgejo Actions, que permite ejecutar pipelines de CI/CD en forjas como Codeberg. Sin embargo, el CI/CD, aunque muy interesante, es un servicio que es caro y delicado. Por eso, proyectos libres como Codeberg, aunque ofrecen este servicio, animan a sus usuarios a usarlo con cabeza y mesuradamente (véase Working with Codeberg's CI y Rules and Conditions del repositorio meta dedicado a esto).

Desde que estuve haciendo pruebas con las Forgejo Actions para un proyecto personal, aún sin publicar, estuve pensando en posibles formas de evitar el uso innecesario o al menos el uso indiscriminado (por ej., no ejecutar la pipeline en cada commit, ni en cada cambio en una PR). Elaboré una prueba de concepto para comprobar si una PR estaba marcada como WIP, que terminé proponiendo en el repositorio actions/meta y el usuario mahlzahn propuso otro enfoque que, aunque no he probado aún, probablemente sea aún más eficiente.

Sin embargo, Forgejo y sus Forgejo Actions tienen un componente muy interesante, que menciona tanto Codeberg como Forgejo en su documentación: Forgejo Runner.

Qué es Forgejo Runner #

En pocas palabras, es un demonio que obtiene workflows de los repositorios en los que haya sido configurados para ejecutarlos y devolver el log de cada paso y su resultado.

Los siguientes aspectos me han parecido bastante interesantes:

  1. No es necesario ser un experto en Forgejo, ni en su código ni en su ecosistema de herramientas para levantar una prueba. Por supuesto, hacerlo profesionalmente conlleva atender muchos detalles, sobre todo relacionados con aspectos de seguridad. Pero, para quien quiera hacer pruebas de concepto o incluso para aliviar el flujo de acciones en los runners que proporciona Codeberg o cualquier otra forja que funcione con Forgejo, es bastante accesible.
  2. No necesitas tu propia instancia de Forgejo, ya que el runner se conecta a cualquier instancia existente (como Codeberg). Tampoco necesitas levantar a su lado otros componentes de Forgejo. Puede funcionar aislado, se puede instalar como un binario, instalarlo como un paquete de NixOS o como un contenedor Docker.
  3. Si es para un proyecto privado o público pero personal, o cuyas pipelines pueden esperar a ser ejecutadas (porque no corre prisa obtener los resultados), montar una instancia local de usar y tirar me suena realmente útil. Pongamos el caso en el que uno está empezando a desarrollar una herramienta, pero quiere dejar testimonio público de los tests o trabaja desde un principio con un enfoque basado en pull requests con CI integrado, o pequeños repositorios en los que trabajan algunos contribuidores. ¿Proyectos grandes? Ahí ya estaríamos hablando de una infraestructura diferente pero también de recursos y herramientas diferentes.
  4. Funciona haciendo pull periódico de los workflows pendientes de ejecutar en uno de los repositorios en los que el runner haya sido utilizado. Esto es muy importante al ejecutarlo en local, ya que no tenemos que darle vueltas a cómo enviar los workflows pendientes desde Codeberg a un servicio que se ejecuta en nuestra red local. Importante: esto no quiere decir que no debamos tener cuidado con otros aspectos, como es la inyección de código arbitrario en nuestra máquina local.

Ejecutar Forgejo Runner en un dispositivo local #

Lo que quería comprobar inicialmente era cuán complejo sería levantar, configurar y utilizar un Forgejo Runner en un dispositivo de mi red local para el caso de uso que expongo en el punto 3 de la sección anterior.

Nota: esta entrada no pretende ser un tutorial detallado ni mucho menos, es más una guía rápida y directa, pero no por ella totalmente correcta, ni tampoco toca todos los detalles que debería tocar. Si prefieres una lectura más formal, detallada y que aborde cada aspecto, empieza por Forgejo Runner installation guide.

Desde mi punto de vista, si uno quiere empezar rápidamente con un Forgejo Runner básicamente necesita:

Tras haber tenido lo anterior en cuenta, en mi caso concreto de hoy, empecé creando una estructura de directorios para el runner:

1$ mkdir -p forgejo-runner/data/.cache
2$ chown -R 1000:1000 forgejo-runner/data
3$ chmod 775 forgejo-runner/data/.cache/ && chmod g+s forgejo-runner/data/.cache/

No sé ustedes pero aunque llevo usando chmod años, es algo que mi cerebro siempre tiene que aprender y mantener fresco, así que la siguiente nota le vendrá genial a mi futuro yo: chmod g+s activa el setgid bit, que básicamente permite que todo archivo o subdirectorio del directorio objetivo heredará el mismo grupo que el directorio padre (es decir, .cache/a tendrá el mismo grupo que .cache).

Luego creé un docker-compose.yml muy mínimo:

1services:
2  forgejo-runner:
3    container_name: forgejo-runner
4    image: data.forgejo.org/forgejo/runner:12
5    command: ["tail", "-f", "/dev/null"]
6    volumes:
7      - /var/run/docker.sock:/var/run/docker.sock
8      - ./data:/data

El tail del command me permite mantener el contenedor vivo, ya que al no haber configuración, forgejo-runner fallaría. Tras esto, procedí a registrar el runner:

1$ docker compose up -d
2$ docker exec -it forgejo-runner sh
3$ cd /data
4$ forgejo-runner register

Tras haber ejecutado forgejo-runner register se iniciaría un registro guiado que preguntará la URL de la instancia de Forgejo, el token, el nombre del runner y sus etiquetas. Importante: la primera parte de la etiqueta es lo que se usa en la clave runs_on de un workflow de Forgejo Actions para determinar en que runner debe ejecutarse, luego el modo y luego la imagen (por ej., test-runner:docker://node:20-bookworm).

Si prefirieses ahorrarte el proceso guiado y ejecutar un único comando, puedes usar parámetros para cada valor. Ejecuta forgejo-runner register --help para más información.

Tras haber registrado el runner, puedes ejecutar cat /data/.runner para ojear el fichero de configuración generado. Cuando termines de curiosear, puedes salir del contenedor (exit).

Tras salir del contenedor sustituí el command del docker-compose.yml con el siguiente:

1command: '/bin/sh -c "forgejo-runner -c /data/config.yml daemon"'

Y ejecuté docker compose down && docker compose up -d && docker compose logs -f y... ¡falló!

forgejo-runner  | time="2026-03-20T17:05:41Z" level=info msg="Fetch interval for connection forgejo-runner-home-lpa has been increased to the minimum of 30 seconds for Codeberg"
forgejo-runner  | time="2026-03-20T17:05:41Z" level=info msg="Starting runner daemon"
forgejo-runner  | Error: cannot ping the docker daemon. is it running? permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.sock/_ping": dial unix /var/run/docker.sock: connect: permission denied

Básicamente falla porque el usuario del contenedor necesita permisos para usar /var/run/docker.sock. Si hacemos ls -la /var/run/docker.sock podremos ver que entre los grupos se encuentra docker, pero necesitamos su gid con getent group docker, que devolverá algo tipo docker:x:yyy.

Así que tras averiguarlo edito el docker-compose.yml y le añado group_add, y restart, que se me había olvidado:

    restart: unless-stopped
    group_add:
      - "<el-gid-de-tu-grupo-de-docker>"

Una vez más, ejecuté docker compose down && docker compose up -d && docker compose logs -f y... ¡funcionó!

forgejo-runner  | time="2026-03-20T17:14:35Z" level=info msg="Fetch interval for connection forgejo-runner-home-lpa has been increased to the minimum of 30 seconds for Codeberg"
forgejo-runner  | time="2026-03-20T17:14:35Z" level=info msg="Starting runner daemon"
forgejo-runner  | time="2026-03-20T17:14:35Z" level=info msg="runner: forgejo-runner-home-lpa, with version: v12.7.2, with labels: [<label>], ephemeral: false, declared successfully"
forgejo-runner  | time="2026-03-20T17:14:35Z" level=info msg="[poller] launched"
forgejo-runner  | time="2026-03-20T17:18:05Z" level=info msg="task 3935967 repo is ivanhercaz/codeberg-test-lab https://data.forgejo.org https://codeberg.org"

Así que fui a forzar la ejecución de una pipeline: la primera vez tardó unos 2 minutos en descargar la imagen de Docker definida en la etiqueta, la segunda vez tardó segundos. Por supuesto, también es importante tener en cuenta que lo que ejecutaba era el workflow check-wip, algo muy ligero.

Potencial y reflexión #

Me parece que tanto Forgejo como Forgejo Runner presentan un enfoque y un ecosistema muy interesante que vale mucho la pena estudiar, tanto para el desarrollo de software de código abierto como para forjas privadas de empresas, asociaciones u otros. Además, facilita que cualquiera pueda montar instancias efímeras o permanentes para ayudar a proyectos que usan Forgejo, como es el caso de Codeberg, a aliviar la carga que conlleva prestar determinados servicios.

Pero no hay que olvidar lo más importante: la seguridad. En las pipelines de CI el código se ejecuta en una máquina de destino, y esa máquina de destino debe estar suficientemente securizada, así como el sistema que gestiona la pipeline, para que la ejecución de ese código no rompa nada ni produzca una brecha de seguridad. Esto es algo que no solemos tener muy presente cuando usamos un CI de terceros, porque de una u otra manera, ese tercero tiene unas políticas de seguridad que obliga al usuario a seguir o, directamente, aplica internamente en su sistema. Por favor, no permitas que se ejecute código arbitrariamente en ninguno de tus dispositivos, sea local o sea un VPS o un servidor en la nube.

A lo largo de esta entrada, que creo que me ha quedado más larga de lo que esperaba, he ido dejando algunos enlaces a documentación de interés y en la que yo me he apoyado para hacer este experimento de hoy. Recomiendo leerla toda, y más.

Sería impresionante montar una infraestructura puramente FOSS, segura y sostenible, sostenida por los propios usuarios, pero el tema de la seguridad es algo que complica mucho ese concepto. Sin embargo, voy a seguir estudiando Forgejo Runner porque me interesa mucho el enfoque de runners efímeros (encender, usar y apagar), así como también quiero probar el enfoque Docker-in-Docker que exponen en la documentación de Forgejo.

last updated:

Todas las entradas están bajo licencia CC BY-SA 4.0. Las imágenes y recursos de terceros tienen su licencia especificada.

All posts are licensed under CC BY-SA 4.0. Third-party images and resources have their licenses specified.

Si te resulta útil lo que escribo, puedes invitarme a un café.

If you find my writing useful, you can buy me a coffee.