Por qué autoalojar Gitea en un VPS
Gitea consume apenas 200 a 300 MB de RAM en reposo, lo que la convierte en una de las forjas Git más eficientes para autoalojar. En un VPS, usted conserva el control total de su código fuente: ningún dato es analizado por un tercero, ninguna limitación arbitraria sobre el número de repositorios privados o de colaboradores, y ningún coste que crezca con su equipo. Para una agencia o un desarrollador independiente, un único VPS puede alojar el conjunto de los proyectos de clientes con permisos aislados por organización. Gitea integra de forma nativa un CI/CD compatible con los flujos de trabajo de GitHub Actions (Gitea Actions), un registro de paquetes y un editor web, lo que cubre la casi totalidad de un ciclo de desarrollo sin dependencia externa.
Beneficios concretos de un Gitea autoalojado
- Huella de memoria mínima: funciona con holgura en un VPS de 2 GB de RAM incluso con varias decenas de usuarios.
- Repositorios privados sin facturación por puesto ni límite impuesto, a diferencia de los planes SaaS.
- Gitea Actions integrado para ejecutar sus pipelines CI/CD sin servicio externo.
- Registro de paquetes (Docker, npm, Composer, Maven) alojado en la misma instancia.
- Autenticación granular: LDAP, OAuth2, tokens de acceso personales y claves SSH por usuario.
- Copia de seguridad trivial: todo cabe en un volumen de datos y una base de datos.
Requisitos de hardware y software
Gitea es muy frugal. Para un equipo pequeño (hasta 20 usuarios), un VPS con 2 vCPU y 2 GB de RAM basta de sobra; prevea 4 GB si activa Gitea Actions con runners locales que compilan código. Reserve al menos 20 a 40 GB de almacenamiento SSD según el tamaño de sus repositorios. En cuanto al software: Docker Engine y el plugin Docker Compose instalados, un nombre de dominio (o subdominio del tipo git.yourdomain.com) que apunte a la IP del VPS mediante un registro A, y el puerto 22 reservado al SSH del servidor — Gitea expondrá su propio SSH en otro puerto para evitar el conflicto.
Desplegar Gitea con Docker y SSL
Preparar el VPS y Docker
Conéctese por SSH, actualice el sistema y luego instale Docker y el plugin Compose. Cree un directorio dedicado:
mkdir -p /opt/gitea && cd /opt/gitea. Cree también un volumen de datos que sobreviva a las actualizaciones del contenedor.Redactar el docker-compose.yml
Defina dos servicios:
gitea(imagengitea/gitea:1.25) y una basepostgres:16. Monte./gitea:/datapara la persistencia, fijeUSER_UID/USER_GIDen 1000, y mapee el SSH de Gitea al puerto 2222 del host:"2222:22". Deje el puerto HTTP 3000 interno, será servido por el reverse proxy.Lanzar los contenedores
Ejecute
docker compose up -dy luegodocker compose logs -f giteapara seguir la inicialización. Compruebe que la conexión a PostgreSQL funciona antes de continuar.Configurar el reverse proxy
Con Caddy, basta una sola línea:
git.yourdomain.com { reverse_proxy localhost:3000 }. Caddy obtiene y renueva automáticamente el certificado Let's Encrypt. Con Nginx, cree un bloqueserverque haga de proxy haciahttp://127.0.0.1:3000y utilicecertbot --nginxpara el SSL.Finalizar la instalación web
Abra
https://git.yourdomain.com, complete el asistente indicando la URL base HTTPS y el host de la base de datos (db:5432). Defina correctamente elROOT_URL, de lo contrario los enlaces de clonado serán erróneos. Despliegue «Ajustes opcionales > Ajustes de la cuenta de administrador», cree allí su cuenta de administrador y valide.Asegurar el SSH de los repositorios
Configure sus clientes para clonar mediante
ssh://[email protected]:2222/..., o añada un bloqueHosten~/.ssh/configpara ocultar el puerto. Desactive el registro público en la administración si la instancia es privada.
Gitea Actions: CI/CD autoalojado en su VPS
Gitea Actions es el motor CI/CD nativo de Gitea, compatible con la sintaxis de los flujos de trabajo de GitHub Actions. Permite ejecutar pipelines directamente en su infraestructura, sin dependencia de ningún servicio externo.
Para activarlo, añada a su app.ini:
[actions]
ENABLED = trueRegistre después un runner act_runner en un contenedor separado en el mismo VPS. Limite su RAM mediante mem_limit para que una compilación exigente no ahogue a Gitea. Para pipelines pesados (compilación, pruebas de integración), dedique un segundo VPS al runner.
Un flujo de trabajo mínimo para un proyecto Node.js va en .gitea/workflows/ci.yml:
on: [push, 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 testEl runner ejecuta este flujo de trabajo en cada push. Puede combinar múltiples trabajos, usar matrices de versiones y compartir artefactos entre pasos — exactamente como GitHub Actions, pero en su propio servidor.
Limite la RAM del contenedor act_runner a 2 GB mediante mem_limit: 2g en su docker-compose.yml si su VPS dispone de 4 GB en total. Así, incluso una compilación que consuma todo lo que se le asigna deja 2 GB para Gitea — suficiente para seguir atendiendo los push y pull requests durante la compilación. Para compilaciones que producen artefactos voluminosos (imágenes Docker, binarios), añada un volumen separado montado en /home/runner/work para no llenar el volumen de datos principal de Gitea.
Gitea o Forgejo en 2026: ¿cuál elegir?
Desplace la tabla
| Criterio | Gitea 1.25 | Forgejo v16 |
|---|---|---|
| Gobernanza | Sociedad comercial (Gitea Ltd) | Asociación sin ánimo de lucro (Codeberg e.V.) |
| Licencia | MIT | MIT — 100 % software libre, sin edición empresarial |
| Recomendado por Awesome-Selfhosted | No (retirado en 2022) | Sí — recomendación por defecto desde 2024 |
| Federación ActivityPub | No prevista | En despliegue progresivo desde Forgejo v7 (abril de 2024), GA en v16 |
| Compatibilidad con la API de Gitea | Referencia | Compatible: misma API, mismos webhooks, mismo formato de datos |
| Seguridad — auditorías públicas | Escasas | Auditorías de código publicadas por la comunidad Codeberg |
| Hoja de ruta comunitaria | Dirigida por Gitea Ltd | Gobernanza abierta, RFC públicas, voto de los contribuidores |
| Migración desde Gitea | N/A | Sin pérdida de datos: mismo esquema de base y mismo formato de volumen |
Migrar de Gitea a Forgejo
Hacer una copia de seguridad de la instancia Gitea
Antes de cualquier operación, haga un dump completo:
docker exec -u git gitea gitea admin dump -c /data/gitea/conf/app.ini. Recupere el archivo generado fuera del contenedor y compruebe que el volumen./giteaestá incluido en su snapshot del VPS.Comprobar la compatibilidad de versión
Forgejo v16 admite la migración desde Gitea 1.20 y posteriores. Si su instancia funciona con una versión anterior, suba primero a Gitea 1.21 o 1.22 mediante
docker compose pull && docker compose up -d, y luego compruebe la ausencia de errores en los logs antes de continuar.Sustituir la imagen Docker
En su
docker-compose.yml, sustituyaimage: gitea/gitea:latestporimage: codeberg.org/forgejo/forgejo:latest. El volumen de datos (./gitea:/data) y la base PostgreSQL permanecen sin cambios — Forgejo lee el mismo esquema y el mismoapp.ini.Reiniciar y dejar que migre
Ejecute
docker compose pull && docker compose up -d. Forgejo aplica automáticamente las migraciones de esquema necesarias al arrancar. Sigadocker compose logs -f forgejohasta el mensaje que indica que el servidor HTTP escucha en el puerto 3000.Regenerar las claves SSH
Como en toda actualización que cambia el binario referenciado en
authorized_keys, regenere las entradas:docker exec -u git forgejo forgejo admin regenerate keys. Compruebe después que ungit clonepor SSH funciona desde un puesto cliente.Probar y validar
Abra la interfaz web, compruebe los repositorios, los webhooks y los runners de Forgejo Actions. Los runners
act_runnerregistrados con Gitea son compatibles — vuelva a conectarlos al nuevo token de registro si la clave ha cambiado.
Forgejo v16: las novedades clave (julio de 2026)
Forgejo v16.0.0 introduce la federación ActivityPub en disponibilidad general para las issues y las pull requests: una issue abierta en una instancia Forgejo puede recibir comentarios de usuarios de otra instancia, sin cuenta compartida. La versión integra también un gestor de secretos de instancia (cifrado AES-256 en reposo), un nuevo motor de búsqueda de código basado en el índice Bleve v2 con soporte de expresiones regulares, y mejoras significativas del planificador de tareas en segundo plano. En el plano de la seguridad, los tokens de API se almacenan ahora hasheados en base de datos (bcrypt) y no en texto plano — una migración automática al arrancar convierte los tokens existentes.
Administración diaria: webhooks, repositorios y limpieza de huérfanos
Una vez Gitea en producción, vuelven regularmente algunas operaciones de mantenimiento.
Gestionar los webhooks. Gitea registra el historial de cada envío en «Configuración del repositorio → Webhooks → Entregas recientes». Un webhook fallido (timeout, error HTTP 5xx) puede reproducirse manualmente desde ese historial. Si sus runners están en la misma red privada que Gitea, añada sus rangos IP a ALLOWED_HOST_LIST en la sección [webhook] de app.ini — de lo contrario las llamadas hacia localhost se bloquean por defecto desde Gitea 1.20.
Purgar los repositorios huérfanos. Un repositorio eliminado mediante la interfaz deja sus datos Git (objects/) en disco hasta la próxima ejecución de las tareas en segundo plano. Fuerce un paso inmediato desde el admin: «Administración del sitio → Tareas en segundo plano → Ejecutar: Purgar repos eliminados». El espacio liberado solo es visible tras docker exec -u git gitea gitea admin storage --remove-unlisted.
Cuota de almacenamiento por organización. Para instancias multi-inquilino, fije una cuota de repositorios por organización en app.ini bajo [repository]: MAX_CREATION_LIMIT = 50. Esto impide que una cuenta cree cientos de repositorios sin supervisión y llene el volumen de datos.
Resolución avanzada de problemas: SSH y clonado HTTPS
Permission denied (publickey) tras una actualización. La ruta del binario referenciado en authorized_keys ha cambiado (comprobado en 1.27). El servicio Gitea responde con normalidad por HTTPS, lo que enmascara el problema. Corrija con: docker exec -u git gitea gitea admin regenerate keys. Compruebe que un git clone por SSH funciona antes de dar por terminada la actualización.
Timeout o error SSL al clonar por HTTPS. Tres causas comunes: el ROOT_URL en app.ini no coincide con el dominio HTTPS real (enlaces rotos + posible bucle de redirección), el reverse proxy no reenvía la cabecera X-Forwarded-Proto: https (Gitea genera entonces URLs en HTTP), o el certificado Let's Encrypt ha caducado (Caddy y certbot lo renuevan automáticamente, pero solo si el DNS sigue apuntando al VPS). Verifique ROOT_URL primero: es la causa más frecuente tras una migración de dominio.
Clonado SSH lento o que se bloquea. El upload-pack SSH usa el mismo proceso Git que las operaciones web. Si su act_runner está utilizando todos los núcleos de CPU disponibles durante una compilación, las operaciones Git pueden quedar a la espera. Añada cpus: '1.5' al runner en el compose para reservarle núcleos sin privarlo por completo.
Database migration failed: column already exists. La base recibió una migración parcial (parada durante la actualización). Restaure el dump realizado antes de la actualización, parta de un volumen limpio y vuelva a lanzar.
Gitea 28.1.0: la comparación de PR falla con exit 128
Tras actualizar a Gitea 28.1.0, la vista de comparación de pull requests falla silenciosamente en repositorios grandes (superiores a 1,4 GB) con el error exit 128 "unable to fetch head branch". La causa raíz es un archivo de bloqueo obsoleto (commit-graph.lock) dejado por un git fetch ejecutado sin el indicador --no-write-commit-graph — comportamiento introducido en esta versión (issue upstream #39795, reportada el 15 de octubre de 2026).
Diagnóstico. Compruebe la presencia del archivo de bloqueo en el repositorio afectado:
docker exec -u git gitea find /data/gitea/repositories -name 'commit-graph.lock'Corrección inmediata. Elimine el archivo de bloqueo obsoleto:
docker exec -u git gitea find /data/gitea/repositories -name 'commit-graph.lock' -deleteLa comparación de PR se restaura de inmediato sin necesidad de reiniciar el contenedor.
Recomendación. La corrección definitiva fue integrada en PR #39798 y se incluye en Gitea 28.1.1+. Planifique la actualización a 28.1.1 o superior cuanto antes; las instancias que gestionan repositorios de gran tamaño se ven afectadas sistemáticamente tras un fetch concurrente.
Gitea Actions: valores YAML multilínea corruptos desde v28
Desde Gitea v28, los escalares de bloque plegado YAML (>) utilizados en campos with: de pasos de flujo de trabajo son malinterpretados en ejecución: los valores multilínea se truncan o concatenan sin saltos de línea, produciendo errores silenciosos difíciles de diagnosticar (issue upstream #39775, reportada el 15 de octubre de 2026).
Síntoma típico. Un parámetro with: definido en varias líneas con >:
- uses: some-action@v1
with:
config: >
línea uno
línea dos
línea tres…es recibido por la acción como línea uno línea dos línea tres (los saltos de línea se pierden), en lugar del valor esperado con separación por líneas.
Soluciones alternativas. Dos enfoques equivalentes:
1. Reemplace > (plegado) por | (literal) — los saltos de línea se conservan tal cual:
with:
config: |
línea uno
línea dos
línea tres2. Utilice una cadena entre comillas con \n explícitos si el valor debe permanecer en una sola línea lógica.
Este comportamiento afecta a todos los flujos de trabajo que usan bloques > dentro de with: desde la migración a v28; los bloques | y los escalares en línea no se ven afectados. Se está evaluando una corrección oficial en el repositorio upstream.
Pasar a Gitea 1.27 sin perder los accesos SSH
La versión 1.27 modifica la ruta interna del binario gitea referenciado en authorized_keys. Durante la actualización, las entradas existentes apuntan a la ruta antigua: cualquier intento de clonado o de push por SSH falla en silencio con Permission denied (publickey), sin mensaje de error del lado del servidor que apunte a la actualización. Después de cada actualización a la 1.27 (o a cualquier versión que cambie esa ruta), ejecute dentro del contenedor: docker exec -u git gitea gitea admin regenerate keys. El comando reescribe todas las entradas authorized_keys con la ruta correcta del binario. Compruebe después que un git clone por SSH funciona antes de dar por terminada la actualización.
JWT_SECRET_URI y JWT_SECRET no deben coexistir en app.ini. Si ambas claves están presentes, Gitea o Forgejo carga una u otra según el orden de lectura, lo que invalida en silencio todos los tokens OAuth2 y Actions emitidos antes de la actualización. Los usuarios ven errores de autenticación intermitentes, sin ningún mensaje que señale app.ini como causa. Elija un solo mecanismo: JWT_SECRET (valor en texto plano) o JWT_SECRET_URI (ruta hacia un archivo secreto), y elimine el otro. Reinicie el contenedor tras la modificación.
Para ir más lejos: Forgejo y CI/CD avanzado
Si desea profundizar en la automatización alrededor de su forja Git, el artículo Alojar Forgejo en su VPS detalla la configuración de una instancia Forgejo con runners Actions dedicados, la configuración del registro OCI integrado y el endurecimiento de la seguridad (fail2ban, restricción de rangos IP). Para equipos que quieran una cadena de despliegue continuo completa, es el complemento natural de esta guía.
La documentación oficial
Para la configuración avanzada y las opciones propias de la herramienta, consulte la documentación oficial de Gitea o la documentación de Forgejo. Esta guía cubre la puesta en línea en un VPS y la migración; las documentaciones de los editores siguen siendo la referencia para los ajustes finos y los casos de uso específicos.