Por qué dejar GitHub por una forja self-hosted
GitHub sigue siendo la referencia, pero en 2026 han convergido tres tendencias que convierten la salida en algo concreto y ya no teórico.
Primera tendencia: las caídas. Varios incidentes documentados en githubstatus.com afectaron a Git Operations y Actions en agosto de 2026 y bloquearon pipelines en producción en el peor momento. El hilo de Hacker News «Alternatives to GitHub» alcanzó 472 puntos y 299 comentarios el 2026-08-17: señal de que la cuestión ya no es académica.
Segunda tendencia: el coste de los minutos de CI. GitHub Actions es gratuito hasta cierta cuota mensual en los repositorios públicos, pero todo equipo que supere esa cuota en repositorios privados paga por minuto, con un multiplicador según el OS del runner. Un runner Linux factura los minutos a la tarifa estándar; un runner macOS los factura a la tarifa más alta. Estos detalles pueden consultarse en github.com/pricing.
Tercera tendencia: la soberanía del código. Algunos equipos prefieren que el único acceso a su repositorio sea root en su propia VM, y no un token SaaS revocable de forma unilateral.
Lo que aporta Gitea self-hosted en concreto
- Historial completo preservado: la importación mediante la API de Gitea consume un archivo tar.gz de GitHub — commits, ramas y tags intactos.
- Issues y labels migrados: el endpoint
POST /api/v1/repos/migratetransfiere issues, milestones y labels en una sola pasada. - Runner de CI nativo: gitea-runner (basado en act) entiende la sintaxis de los workflows de GitHub Actions — la mayoría de los pipelines funcionan sin reescritura.
- Coste previsible: un VPS con 1 vCPU y 1 GB de RAM basta para un equipo pequeño; el software es open source, sin licencia ni cuota de minutos.
- Webhooks compatibles: Gitea expone los mismos eventos (
push,pull_request,release) que GitHub — las integraciones de terceros (Slack, Woodpecker CI) siguen conectadas sin modificación. - Token de API local: los accesos se gestionan en su propia interfaz, nunca en la de un tercero.
Requisitos previos
Antes de empezar, reúna los elementos siguientes.
Del lado de GitHub: un token de acceso personal (Settings → Developer settings → Personal access tokens) con los scopes repo y read:org. Téngalo a mano: lo usará en cada llamada curl.
Del lado de Gitea: una instancia de Gitea ya instalada (véase nuestra guía «Alojar Gitea en su propio VPS») y un token de administrador (gitea-admin → Settings → Applications → Generate Token). Dirección base indicada como https://git.votre-domaine.com.
Del lado del equipo local: git 2.x, curl y jq instalados. Cuente con 1 GB de RAM como mínimo en el VPS para importar repositorios de tamaño habitual.
Migración paso a paso
Crear la organización Gitea de destino
En la interfaz de Gitea, vaya a
+→ New Organization y elija un nombre idéntico al de su organización GitHub (esto simplificará la actualización de los remotes).O mediante la API:
curl -s -X POST https://git.votre-domaine.com/api/v1/orgs \ -H "Authorization: token VOTRE_TOKEN_GITEA" \ -H "Content-Type: application/json" \ -d '{"username":"mon-org","visibility":"private"}'Listar y exportar los repositorios de GitHub
Recupere la lista de sus repositorios (paginada a 100 por página):
curl -s -H "Authorization: token VOTRE_TOKEN_GITHUB" \ "https://api.github.com/orgs/MON_ORG/repos?per_page=100&page=1" \ | jq -r '.[].name' > repos.txtEl endpoint de GitHub utilizado aquí es
GET /repos/{owner}/{repo}/tarball/{ref}para el archivo completo, yGET /orgs/{org}/repospara el inventario. Consulte la documentación oficial en docs.github.com.Importar cada repositorio mediante la API de Gitea
El endpoint
POST /api/v1/repos/migratede la API de Gitea v1 acepta una URL de origen GitHub (documentación oficial: gitea.io). Para cada repositorio:while IFS= read -r REPO; do curl -s -X POST https://git.votre-domaine.com/api/v1/repos/migrate \ -H "Authorization: token VOTRE_TOKEN_GITEA" \ -H "Content-Type: application/json" \ -d "{\n \\\"clone_addr\\\": \\\"https://github.com/MON_ORG/$REPO\\\",\n \\\"auth_token\\\": \\\"VOTRE_TOKEN_GITHUB\\\",\n \\\"uid\\\": 2,\n \\\"repo_name\\\": \\\"$REPO\\\",\n \\\"issues\\\": true,\n \\\"labels\\\": true,\n \\\"milestones\\\": true,\n \\\"mirror\\\": false\n }" done < repos.txtuides el identificador numérico de su organización Gitea (visible en la APIGET /api/v1/orgs/mon-org). El campomirror: falseindica una importación puntual: póngalo entruesi desea una sincronización continua durante la fase de transición.Redirigir los remotes locales
Para cada repositorio clonado en su equipo:
git remote set-url origin https://git.votre-domaine.com/mon-org/mon-repo.gitO por SSH si ha añadido su clave pública en Gitea (
Settings → SSH / GPG Keys):git remote set-url origin [email protected]:mon-org/mon-repo.gitCompruébelo con
git remote -vy después pruebe ungit fetchpara confirmar que el remote responde.Actualizar los webhooks
En Gitea, para cada repositorio: Settings → Webhooks → Add Webhook. Pegue la URL de su CI o de su servicio de terceros (Slack, Woodpecker CI, etc.) sustituyendo
github.comporgit.votre-domaine.com.Los eventos disponibles (
push,pull_request,issues,release) son idénticos a los de GitHub: los payloads tienen la misma estructura de base para la mayoría de las integraciones.Migrar el pipeline de CI a gitea-runner
Instale el runner oficial en su VPS:
wget -O gitea-runner https://dl.gitea.com/act_runner/latest/act_runner-latest-linux-amd64 chmod +x gitea-runner ./gitea-runner register --no-interactive \ --instance https://git.votre-domaine.com \ --token VOTRE_TOKEN_RUNNER \ --name vps-runner \ --labels ubuntu-latest:docker://node:20-bullseye ./gitea-runner daemon &El token del runner se genera en Gitea: Site Administration → Runners → Create new Runner Token.
Sus archivos
.github/workflows/*.ymlexistentes los entiende gitea-runner sin modificación para las acciones habituales (actions/checkout,actions/setup-node, etc.). Renombre la carpeta.github/workflows/como.gitea/workflows/para activar el runner de Gitea de forma limpia (ambas rutas están soportadas, pero.gitea/es la ruta nativa).
Verificaciones posteriores a la migración
Una vez terminada la migración, valide estos tres puntos antes de cortar el acceso a GitHub.
Clones SSH: desde un equipo recién clonado, git clone [email protected]:mon-org/mon-repo.git debe funcionar sin pedir contraseña si su clave pública está registrada en Gitea.
Webhooks activos: en cada repositorio, abra Settings → Webhooks y haga clic en Test Delivery. Un código 200 en los logs de entrega confirma que su CI recibe los eventos.
Historial intacto: git log --oneline -10 en el repositorio migrado debe mostrar los mismos diez últimos commits que en GitHub y en el mismo orden.
Si migra varias decenas de repositorios, lance las llamadas curl de migración en paralelo con xargs -P 4 para dividir entre cuatro el tiempo de espera. Gitea gestiona las importaciones concurrentes sin pérdida: el único límite es el ancho de banda de salida de GitHub y el de entrada de su VPS.
Resolución de problemas — errores habituales
Estas son las tres situaciones más frecuentes durante una migración.
SSH: `Permission denied (publickey)`
Síntoma:
git clone [email protected]:…falla conPermission denied (publickey).Comprobación: en Gitea, Settings → SSH / GPG Keys — la clave pública que utiliza debe figurar ahí. Añádala si falta (
cat ~/.ssh/id_ed25519.pub | pbcopyoxclip).Si la clave está presente pero el error persiste, compruebe que el puerto SSH de Gitea es efectivamente el
22(o el puerto configurado):ssh -vT [email protected] -p 22mostrará la negociación y el mensaje de aceptación de Gitea si la clave se reconoce.Remote: `fatal: repository not found`
Síntoma:
git fetchdevuelvefatal: repository 'https://git.votre-domaine.com/…' not found.Diagnóstico:
git remote -v— compruebe que la URL apunta realmente a su instancia de Gitea y no agithub.com. Si la URL es correcta, conéctese a la interfaz de Gitea y confirme que el repositorio existe con el nombre de organización correcto. UnPOST /api/v1/repos/migrateque ha fallado en silencio deja el repositorio ausente, sin mensaje de error en el remote local.Webhooks silenciosos tras la migración
Síntoma: un
git pushno dispara ninguna build en su CI.Diagnóstico: en Gitea, Site Administration → System Settings → Git Hooks — active el registro de los hooks. Después, en el repositorio, Settings → Webhooks → última entrega: el campo «Response» muestra el código HTTP devuelto por su CI. Un
404significa que la URL del webhook apunta a una ruta inexistente; un401significa un secreto HMAC mal configurado.Active el modo debug del runner: en el archivo de configuración de gitea-runner, ponga
log_levelendebugy reinicie el daemon. Los logs mostrarán la recepción de cada evento y el código de activación del job.
Alojar Gitea en un VPS ServOrbit
Gitea funciona con holgura en un VPS con 1 vCPU y 1 GB de RAM para un equipo pequeño. Para una organización de diez desarrolladores con una CI activa, 2 vCPU y 2 GB son un punto de partida razonable: el runner y Gitea pueden convivir en la misma VM con ese dimensionamiento.
Si la administración del servidor no es su prioridad, la opción administración VPS de ServOrbit cubre exactamente este caso: actualizaciones de seguridad, supervisión e intervención en caso de incidente — usted conserva el acceso root y nosotros mantenemos la VM en buen estado. Su forja sigue siendo suya, sin compartir la infraestructura con otros equipos.
Despliegue la plantilla Gitea desde el Marketplace, seleccione su OS (Ubuntu o Debian) y el tamaño de su VPS, y dispondrá de una instancia configurada en unos minutos. Planes VPS desde {{vps.start.price}}.