Gitea es una plataforma Git autoalojada y ligera con incidencias, pull requests y un sistema de CI/CD integrado, Gitea Actions, compatible con la sintaxis de GitHub Actions. En este tutorial partirás de una instalación de Gitea ya funcionando en Ubuntu 24.04 y añadirás lo que la convierte en una plataforma de equipo: un runner act_runner para ejecutar pipelines en contenedores Docker, un workflow de ejemplo, autenticación LDAP, repositorios espejo y copias de seguridad diarias automatizadas.

Requisitos previos

Para seguir esta guía necesitas:

  • Gitea 1.21 o posterior instalado con el binario oficial en Ubuntu 24.04, con la disposición de la documentación de Gitea: binario en /usr/local/bin/gitea, configuración en /etc/gitea/app.ini, datos en /var/lib/gitea y usuario del sistema git. Si usas otras rutas, adáptalas en los comandos.
  • Gitea publicado con HTTPS en un dominio, en la guía git.your_domain.
  • Una cuenta de administrador en Gitea.
  • Docker Engine instalado en la máquina del runner (puede ser el mismo servidor, por ejemplo un VPS de CubePath con al menos 4 GB de RAM, o uno dedicado a CI).
  • Un usuario no root con privilegios sudo.

Paso 1: Comprobar que Gitea Actions está activado

Desde Gitea 1.21, Actions está activado por defecto. Comprueba que no se ha desactivado en la configuración:

sudo grep -A2 '^\[actions\]' /etc/gitea/app.ini

Si el comando no devuelve nada, se usa el valor por defecto y Actions está activo. Si ves ENABLED = false, cámbialo a true y reinicia Gitea con sudo systemctl restart gitea.

Para confirmarlo en la interfaz, entra como administrador en Administración del sitio: debe aparecer la sección Actions > Runners.

Paso 2: Instalar act_runner

act_runner es el runner oficial de Gitea. Recibe trabajos de la instancia y los ejecuta en contenedores Docker. Consulta la última versión estable en https://dl.gitea.com/act_runner/ y guárdala en una variable:

RUNNER_VERSION=0.2.13

Descarga el binario para tu arquitectura (amd64 o arm64) e instálalo:

curl -fL -o act_runner "https://dl.gitea.com/act_runner/${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-linux-amd64"
sudo install -m 0755 act_runner /usr/local/bin/act_runner
act_runner --version
act_runner version v0.2.13

Crea un usuario de sistema propio para el runner y añádelo al grupo docker, ya que lanzará contenedores:

sudo useradd --system --create-home --home-dir /var/lib/act_runner --shell /usr/sbin/nologin act_runner
sudo usermod -aG docker act_runner

Genera la configuración por defecto en /etc/act_runner:

sudo mkdir -p /etc/act_runner
act_runner generate-config | sudo tee /etc/act_runner/config.yaml > /dev/null

El fichero generado está comentado. Los valores por defecto sirven para empezar; el más relevante es runner.capacity, el número de trabajos simultáneos (1 por defecto).

Paso 3: Registrar el runner

En Gitea, ve a Administración del sitio > Actions > Runners > Crear nuevo runner y copia el token de registro. Un runner registrado ahí sirve a toda la instancia; si prefieres uno por organización o repositorio, obtén el token en sus ajustes de Actions.

Registra el runner como el usuario act_runner y desde su directorio de trabajo, porque el registro crea ahí el fichero .runner con sus credenciales. Sustituye your_registration_token:

sudo -u act_runner sh -c 'cd /var/lib/act_runner && act_runner register \
  --no-interactive \
  --config /etc/act_runner/config.yaml \
  --instance https://git.your_domain \
  --token your_registration_token \
  --name runner-01 \
  --labels ubuntu-latest:docker://gitea/runner-images:ubuntu-latest'
INFO Registering runner, arch=amd64, os=linux, version=v0.2.13.
INFO Runner registered successfully.

La etiqueta ubuntu-latest:docker://gitea/runner-images:ubuntu-latest significa que los trabajos con runs-on: ubuntu-latest se ejecutarán en un contenedor de esa imagen, que incluye Node.js y las herramientas habituales de las acciones de GitHub.

Paso 4: Ejecutar el runner como servicio

Crea la unidad de systemd:

sudo nano /etc/systemd/system/act_runner.service
[Unit]
Description=Gitea Actions runner
Documentation=https://gitea.com/gitea/act_runner
After=docker.service
Requires=docker.service

[Service]
ExecStart=/usr/local/bin/act_runner daemon --config /etc/act_runner/config.yaml
ExecReload=/bin/kill -s HUP $MAINPID
WorkingDirectory=/var/lib/act_runner
User=act_runner
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Actívala y revisa el registro:

sudo systemctl daemon-reload
sudo systemctl enable --now act_runner
sudo journalctl -u act_runner -n 10 --no-pager
INFO Starting runner daemon
INFO runner: runner-01, with version: v0.2.13, with labels: [ubuntu-latest], declare successfully

En Administración del sitio > Actions > Runners, runner-01 debe aparecer con estado Idle.

Paso 5: Crear un pipeline CI/CD

Los workflows se guardan en .gitea/workflows/ dentro del repositorio. Si el repositorio es nuevo, activa Actions en Ajustes del repositorio > Unidades > Actions.

Este ejemplo para un proyecto Node.js ejecuta las pruebas en cada push y pull request, y despliega por SSH solo cuando el push llega a main:

name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '22'

      - run: npm ci
      - run: npm test

  deploy:
    needs: test
    if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Desplegar por SSH
        env:
          DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
        run: |
          install -m 700 -d ~/.ssh
          echo "$DEPLOY_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
          ssh deploy@"$DEPLOY_HOST" 'cd /srv/app && git pull && docker compose up -d --build'

Guárdalo como .gitea/workflows/ci.yml. Gitea descarga actions/checkout y actions/setup-node desde GitHub por defecto, así que el runner necesita salida a Internet.

Crea los secretos DEPLOY_HOST (IP o nombre del servidor de destino) y DEPLOY_KEY (clave privada SSH de un usuario deploy con acceso limitado) en Ajustes del repositorio > Actions > Secretos. Los secretos no se muestran en los registros de los trabajos.

Haz commit y push, y abre la pestaña Actions del repositorio. Verás la ejecución con sus dos trabajos; un círculo verde en cada uno indica que terminó correctamente.

Paso 6: Notificar eventos con webhooks

Los webhooks avisan a servicios externos (Slack, Discord, Matrix, Telegram o una URL propia) cuando ocurre algo en el repositorio. Gitea incluye formatos nativos para los principales servicios de chat, así que no necesitas scripts intermedios.

Ve a Ajustes del repositorio > Webhooks > Añadir webhook, elige el tipo (por ejemplo Discord o Slack), pega la URL de entrada que te da ese servicio y selecciona los eventos, como Push o Pull request. Para un webhook de tipo Gitea hacia tu propia aplicación, rellena también Secreto: Gitea firmará cada petición con HMAC-SHA256 en la cabecera X-Gitea-Signature, que tu aplicación debe verificar.

Tras guardarlo, pulsa Probar entrega. En Entregas recientes verás la petición enviada y el código de respuesta; un 200 o 204 confirma que llega.

Paso 7: Conectar Gitea con LDAP o Active Directory

Con LDAP, los usuarios inician sesión en Gitea con sus credenciales corporativas y la cuenta se crea en el primer acceso. Antes de configurar Gitea, comprueba desde el servidor que la cuenta de servicio puede buscar usuarios. Instala el cliente y lanza una búsqueda de prueba (te pedirá la contraseña de la cuenta de enlace):

sudo apt install ldap-utils
ldapsearch -x -H ldaps://ldap.your_domain -D "cn=gitea-bind,ou=Servicios,dc=example,dc=com" -W -b "ou=Usuarios,dc=example,dc=com" "(sAMAccountName=usuario_prueba)" mail

Si devuelve el usuario con su atributo mail, añade la fuente de autenticación con la CLI de Gitea, ejecutada como el usuario git. Este ejemplo es para Active Directory por LDAPS; en OpenLDAP usa uid en lugar de sAMAccountName:

sudo -u git gitea admin auth add-ldap \
  --config /etc/gitea/app.ini \
  --work-path /var/lib/gitea \
  --name "Active Directory" \
  --security-protocol LDAPS \
  --host ldap.your_domain \
  --port 636 \
  --bind-dn "cn=gitea-bind,ou=Servicios,dc=example,dc=com" \
  --bind-password 'your_bind_password' \
  --user-search-base "ou=Usuarios,dc=example,dc=com" \
  --user-filter "(&(objectClass=user)(sAMAccountName=%s))" \
  --username-attribute sAMAccountName \
  --firstname-attribute givenName \
  --surname-attribute sn \
  --email-attribute mail

Comprueba que la fuente existe:

sudo -u git gitea admin auth list --config /etc/gitea/app.ini --work-path /var/lib/gitea
ID	Name			Type			Enabled
1	Active Directory	LDAP (via BindDN)	true

Cierra sesión e inicia sesión con un usuario del directorio. La fuente se puede editar después en Administración del sitio > Fuentes de autenticación.

Paso 8: Crear repositorios espejo

Un espejo (mirror) es una copia de solo lectura de un repositorio externo que Gitea sincroniza periódicamente. Es útil para tener una copia local de dependencias o de proyectos alojados en GitHub.

Desde la interfaz, usa + > Nueva migración, elige el origen y marca Este repositorio será un espejo. Para automatizarlo, usa la API. Crea antes un token de acceso en Configuración > Aplicaciones con permiso de escritura sobre repositorios y sustituye your_api_token y your_user:

curl -fsS -X POST "https://git.your_domain/api/v1/repos/migrate" \
  -H "Authorization: token your_api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "clone_addr": "https://github.com/go-gitea/gitea.git",
    "repo_name": "gitea-mirror",
    "repo_owner": "your_user",
    "mirror": true,
    "mirror_interval": "8h0m0s",
    "private": true
  }'

La respuesta es el JSON del repositorio creado con "mirror": true. Para forzar una sincronización sin esperar al intervalo:

curl -fsS -X POST "https://git.your_domain/api/v1/repos/your_user/gitea-mirror/mirror-sync" \
  -H "Authorization: token your_api_token"

Paso 9: Automatizar copias de seguridad con gitea dump

gitea dump genera un único fichero con los repositorios, la base de datos exportada, la configuración y los datos adjuntos. Crea un directorio para las copias, propiedad del usuario git:

sudo install -d -o git -g git -m 750 /var/backups/gitea

Crea el script de copia:

sudo nano /usr/local/bin/gitea-backup
#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/var/backups/gitea"
KEEP_DAYS=7
STAMP="$(date +%Y%m%d-%H%M%S)"

cd "$BACKUP_DIR"
/usr/local/bin/gitea dump \
  --config /etc/gitea/app.ini \
  --work-path /var/lib/gitea \
  --tempdir "$BACKUP_DIR" \
  --skip-log \
  --file "$BACKUP_DIR/gitea-dump-$STAMP.zip"

find "$BACKUP_DIR" -maxdepth 1 -name 'gitea-dump-*.zip' -mtime +"$KEEP_DAYS" -delete

Hazlo ejecutable y pruébalo como el usuario git:

sudo chmod 755 /usr/local/bin/gitea-backup
sudo -u git /usr/local/bin/gitea-backup
sudo ls -lh /var/backups/gitea
-rw------- 1 git git 148M Sep 25 12:40 gitea-dump-20260925-124002.zip

Prográmalo cada noche con un temporizador de systemd. Primero el servicio:

sudo nano /etc/systemd/system/gitea-backup.service
[Unit]
Description=Copia de seguridad de Gitea

[Service]
Type=oneshot
User=git
ExecStart=/usr/local/bin/gitea-backup

Después el temporizador:

sudo nano /etc/systemd/system/gitea-backup.timer
[Unit]
Description=Copia diaria de Gitea

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

Actívalo y comprueba la próxima ejecución:

sudo systemctl daemon-reload
sudo systemctl enable --now gitea-backup.timer
systemctl list-timers gitea-backup.timer

Solución de problemas

El runner aparece como Offline. Revisa sudo journalctl -u act_runner -n 50 --no-pager. Si ves errores de conexión, comprueba desde la máquina del runner que la URL responde con curl -I https://git.your_domain. Si el error es de permisos sobre /var/run/docker.sock, confirma que act_runner está en el grupo docker y reinicia el servicio.

Los trabajos quedan en espera ("Waiting"). Ningún runner tiene la etiqueta del runs-on. Compara las etiquetas del runner en la lista de runners con las del workflow.

Los usuarios LDAP no pueden iniciar sesión. Repite la búsqueda con ldapsearch sustituyendo %s por el usuario real. Si falla con LDAPS, comprueba que el servidor confía en el certificado del controlador de dominio.

gitea dump falla por permisos. Ejecútalo siempre como git; con otro usuario no puede leer /var/lib/gitea ni /etc/gitea/app.ini.

Conclusión

Tu instancia de Gitea ya ejecuta pipelines CI/CD con un runner propio en Docker, autentica contra el directorio corporativo, mantiene espejos de repositorios externos y guarda una copia diaria con rotación de 7 días. Como siguientes pasos, puedes añadir más runners con etiquetas distintas (por ejemplo arm64), activar el registro de paquetes y contenedores integrado de Gitea para publicar artefactos desde los workflows y practicar la restauración de una copia en un servidor de pruebas.