Por qué GitLab CE en lugar de GitHub o Bitbucket
GitHub y Bitbucket son servicios gestionados: prácticos al principio, pero su modelo de precios evoluciona con el tamaño del equipo, los minutos de CI están contados en los planes gratuitos y su código se aloja en una infraestructura de terceros. GitLab CE invierte esa ecuación: usted despliega la forja en su VPS, conserva el control total del código y de los datos, y la CI/CD va incluida sin cuota de minutos impuesta por un tercero. Para una agencia o un equipo de desarrolladores que factura a clientes o trata datos sensibles, ese control suele ser innegociable. GitLab CE es hoy una de las forjas git más desplegadas en self-hosted: su base de código es pública, su núcleo es libre y sus diez años de existencia le han dado una comunidad de colaboradores activa.
Lo que GitLab CE incluye de entrada
- Forja git completa: repositorios privados y públicos, merge requests, revisión de código, protección de ramas y CODEOWNERS.
- CI/CD integrada sin límite de minutos impuesto: pipelines YAML (
.gitlab-ci.yml), environments, deploy tokens y artifacts. - Registry Docker privado alojado en su dominio:
docker pull git.yourdomain.com/group/image:tagsin cuenta de Docker Hub. - Wikis y páginas por proyecto y por grupo, con renderizado Markdown completo.
- Gestión de issues y milestones: tablero kanban, etiquetas, iteraciones y burndown chart incluidos.
- Runners en paralelo: registre tantos runners como quiera en su infraestructura o en sus CI workers.
- Webhooks para avisar a una herramienta externa (Slack, PagerDuty, su servidor de despliegue) en cada push o merge.
- RBAC granular con cinco niveles de acceso (Guest, Reporter, Developer, Maintainer, Owner) y soporte opcional de LDAP/SAML.
Requisitos previos: lo que hace falta antes de empezar
GitLab CE consume mucha memoria — es uno de sus inconvenientes más citados frente a Forgejo o Gitea. Prevea 4 GB de RAM como mínimo para un uso ligero (menos de diez usuarios activos); se recomiendan 8 GB en cuanto añada runners en la misma máquina o active el registry Docker. Por debajo de 4 GB, Puma y Sidekiq compiten por la memoria y GitLab responde con 502 bajo carga. En disco, cuente al menos 20 GB para la instalación y los repositorios iniciales — prevea 50 GB si piensa guardar imágenes Docker en el registry. También necesita: un nombre de dominio apuntando a la IP del VPS, los puertos 80, 443 y 22 abiertos en su cortafuegos, y Docker Engine con el plugin Compose v2 instalados.
De la instalación a su primer proyecto
Crear el árbol de carpetas
Cree una carpeta dedicada y los tres volúmenes persistentes que utiliza GitLab:
mkdir -p /opt/gitlab/{config,logs,data}. Sin ellas, la configuración y los repositorios desaparecen en cadadocker compose down.Escribir el fichero docker-compose.yml
Cree
/opt/gitlab/docker-compose.yml. La claveGITLAB_OMNIBUS_CONFIGconcentra toda la configuración específica de su instancia — sustituyagit.yourdomain.compor su dominio real. Defina el servicio gitlab con la imagengitlab/gitlab-ce:17.2.1-ce.0,restart: unless-stopped, puertos 80, 443 y 22, volúmenes,shm_size: 256myenv_file: .env.Rellenar GITLAB_OMNIBUS_CONFIG
En
GITLAB_OMNIBUS_CONFIG, defina como mínimo:external_url 'https://git.yourdomain.com';letsencrypt['enable'] = true; parámetros SMTP —gitlab_rails['smtp_enable'] = true, dirección, puerto 587, usuario, contraseña desde ENV, autenticaciónlogin,smtp_enable_starttls_auto = true. Todas las líneas deben estar dentro del bloqueGITLAB_OMNIBUS_CONFIG.Crear el fichero .env para los secretos
Cree
/opt/gitlab/.envy restrinja sus permisos:touch /opt/gitlab/.env && chmod 600 /opt/gitlab/.env. AñadaGITLAB_SMTP_PASSWORD=su_contraseña_smtp. Añada.enva su.gitignore.Arrancar GitLab y esperar a la inicialización
Lance la stack:
docker compose -f /opt/gitlab/docker-compose.yml up -d. Espere unos 5 minutos antes de acceder a la interfaz web. Siga el avance condocker compose -f /opt/gitlab/docker-compose.yml logs -f gitlaby espere el mensajegitlab Reconfigured!.Recuperar la contraseña root inicial
Recupérela con:
docker exec -it gitlab-gitlab-1 grep 'Password:' /etc/gitlab/initial_root_password. Este fichero se elimina automáticamente 24 horas después — anote la contraseña de inmediato.Conectarse, cambiar la contraseña y desactivar el registro abierto
Abra
https://git.yourdomain.com. Conéctese conroot. Cambie la contraseña en User Settings → Password. Desactive el registro público en Admin Area → Settings → General → Sign-up restrictions.Registrar un GitLab Runner
Instale
gitlab-runnery recupere el token de registro en Admin Area → Runners. Registre:gitlab-runner register --url https://git.yourdomain.com --registration-token VOTRE_TOKEN --executor docker --docker-image alpine:latest.Comprobar el envío de correos
Pruebe desde la consola Rails:
docker exec -it gitlab-gitlab-1 gitlab-rails consoley luegoNotify.test_email('[email protected]', 'Test GitLab', 'Bonjour').deliver_now.
Copias de seguridad automáticas. GitLab incluye un comando de copia de seguridad completa: docker exec -t gitlab-gitlab-1 gitlab-backup create. Prográmelo en crontab -e con 0 3 * * * docker exec -t gitlab-gitlab-1 gitlab-backup create CRON=1. Sincronice la carpeta de archivos con un almacenamiento externo (S3, rclone) — una copia local no es una copia de seguridad.
Configurar el runner Docker y escribir su primer pipeline CI/CD
El executor Docker es el más recomendado: cada job corre en un contenedor aislado. Tras registrar el runner, su fichero /etc/gitlab-runner/config.toml contiene un bloque [runners.docker]. Añada volumes = ["/cache"] y pull_policy = ["if-not-present"] para acelerar los builds.
Ejemplo de pipeline básico en .gitlab-ci.yml:
stages:
- test
- build
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
test:unit:
stage: test
image: node:20-alpine
cache:
key: $CI_COMMIT_REF_SLUG
paths:
- node_modules/
script:
- npm ci
- npm test
only:
- merge_requests
- main
build:image:
stage: build
image: docker:26
services:
- docker:26-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
only:
- main
deploy:prod:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
script:
- ssh -o StrictHostKeyChecking=no [email protected] "docker pull $DOCKER_IMAGE && docker compose up -d"
only:
- main
when: manualLas variables CI_REGISTRY_USER, CI_REGISTRY_PASSWORD y CI_REGISTRY se inyectan automáticamente por GitLab CE. La variable SSH_PRIVATE_KEY se define en Settings → CI/CD → Variables marcada como Masked. El job deploy:prod tiene when: manual — no se dispara automáticamente.
Configuración detallada del SMTP
GitLab envía correos en cada merge request, mención, pipeline completado o alerta de seguridad. Una configuración SMTP ausente o errónea corta silenciosamente esas notificaciones.
Bloque completo en GITLAB_OMNIBUS_CONFIG para un relay SMTP autenticado (STARTTLS, puerto 587):
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = 'smtp.yourdomain.com'
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = '[email protected]'
gitlab_rails['smtp_password'] = ENV['GITLAB_SMTP_PASSWORD']
gitlab_rails['smtp_authentication'] = 'login'
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['gitlab_email_from'] = '[email protected]'
gitlab_rails['gitlab_email_reply_to'] = '[email protected]'Para TLS directo (puerto 465), sustituya smtp_enable_starttls_auto = true por smtp_tls = true. Las claves fuera del bloque omnibus se ignoran en silencio — pruebe siempre con Notify.test_email(…).deliver_now desde la consola Rails.
Copia de seguridad y restauración de GitLab
El comando docker exec -t gitlab-gitlab-1 gitlab-backup create genera un tar completo con repositorios, base de datos PostgreSQL y artifacts. Importante: no incluye /etc/gitlab/gitlab.rb ni /etc/gitlab/gitlab-secrets.json — guárdelos por separado:
tar czf /opt/gitlab/config-secrets-$(date +%Y%m%d).tar.gz \
/opt/gitlab/config/gitlab.rb \
/opt/gitlab/config/gitlab-secrets.jsonRestauración en tres pasos: instale la misma versión menor que produjo la copia; copie el tar en /opt/gitlab/data/backups/; luego:
docker exec -it gitlab-gitlab-1 gitlab-ctl stop puma
docker exec -it gitlab-gitlab-1 gitlab-ctl stop sidekiq
docker exec -it gitlab-gitlab-1 gitlab-backup restore BACKUP=<timestamp>_<version>
docker exec -it gitlab-gitlab-1 gitlab-ctl restart
docker exec -it gitlab-gitlab-1 gitlab-rake gitlab:check SANITIZE=truePlanifique la copia a las 3 de la madrugada y sincronice a almacenamiento externo.
Resolución de problemas: las tres averías más frecuentes
502 Bad Gateway al arrancar. GitLab tarda unos 5 minutos en estar plenamente operativo. Si el 502 persiste, compruebe que Puma ha arrancado: docker exec gitlab-gitlab-1 gitlab-ctl status puma. La falta de RAM es la causa más frecuente. El SMTP no funciona. Verifique que el bloque gitlab_rails['smtp_*'] esté dentro de GITLAB_OMNIBUS_CONFIG — una clave fuera del bloque es un fallo silencioso. Runner no conectado. Compruebe con gitlab-runner verify --url https://git.yourdomain.com. Un certificado TLS autofirmado o un DNS no resoluble desde la máquina del runner son las causas habituales.
Diagnóstico avanzado: Sidekiq bloqueado, timeout de Puma y OOM en Gitaly
Sidekiq queue stuck. Compruebe el estado:
docker exec gitlab-gitlab-1 gitlab-ctl status sidekiq
docker exec gitlab-gitlab-1 gitlab-rake gitlab:sidekiq:checkPara vaciar una cola sin reiniciar: docker exec -it gitlab-gitlab-1 gitlab-rails console -e production luego Sidekiq::Queue.new('mailers').clear. Para reiniciarlo sin perder jobs pendientes: docker exec gitlab-gitlab-1 gitlab-ctl restart sidekiq.
Timeout de Puma. Añada en GITLAB_OMNIBUS_CONFIG: puma['worker_timeout'] = 90 (predeterminado 60 s). Si persisten los timeouts, la causa casi siempre es falta de RAM o un número de workers inadecuado.
OOM en Gitaly. Verifique con dmesg | grep -i kill. Reduzca la caché: gitaly['configuration']['git']['catfile_cache_size'] = 5 (predeterminado 100).
Contenedor que sale al arrancar. Revise los logs: docker compose logs gitlab | tail -50. Causas frecuentes: conflicto en el puerto 22, volumen no escribible o sintaxis Ruby inválida en GITLAB_OMNIBUS_CONFIG.
Seguridad: CVE-2026-90970 y actualización crítica
El 2 de octubre de 2026, GitLab divulgó CVE-2026-90970, una vulnerabilidad crítica (CVSS 9.9) que afecta al componente AI Gateway de los despliegues self-hosted. El fallo es una inyección de plantillas en el lado del servidor (SSTI) en los templates de flujo personalizados: un usuario autenticado con acceso a la plataforma Duo Agent puede enviar una configuración de flujo especialmente diseñada para escapar del sandbox y ejecutar comandos arbitrarios en el host subyacente.
Qué está afectado. Únicamente las instancias self-hosted que despliegan el componente AI Gateway. GitLab.com y las instancias Dedicated ya fueron parcheadas automáticamente. La explotación requiere acceso a Duo Agent Platform y no se han notificado ataques en la naturaleza en el momento de la divulgación.
Versiones vulnerables y versiones corregidas.
| Rama | Última versión vulnerable | Versión corregida |
|---|---|---|
| 19.4.x | 19.4.0 | 19.4.1 |
| 19.3.x | 19.3.1 | 19.3.2 |
| 18.1.6 – 19.2.x | 19.2.3 | 19.2.4 |
Actualizar el AI Gateway en Docker.
# Detener y eliminar el contenedor existente
docker stop gitlab-ai-gateway && docker rm gitlab-ai-gateway
# Descargar la imagen corregida (sustituya el tag por su rama)
docker pull registry.gitlab.com/gitlab-org/modelops/apigw/self-hosted-v19.4.1-ee
# o: self-hosted-v19.3.2-ee / self-hosted-v19.2.4-ee
# Reiniciar con las mismas variables de entorno
docker run -d --name gitlab-ai-gateway \
--restart unless-stopped \
[sus opciones habituales] \
registry.gitlab.com/gitlab-org/modelops/apigw/self-hosted-v19.4.1-ee
# Verificar la versión desplegada
docker ps --format '{{.Image}}' | grep ai-gatewayActualizar mediante Helm / Kubernetes. Actualice image.tag en sus values de Helm a la versión corregida, configure imagePullPolicy: Always y ejecute helm upgrade.
Tras la actualización, confirme que el contenedor usa la imagen parcheada: docker inspect <container> --format '{{.Config.Image}}'.
Deshabilitar el AI Gateway si no lo utiliza. Si su instancia de GitLab CE no usa las funciones Duo (completado de código con IA, chat), el AI Gateway puede simplemente detenerse y eliminarse de su docker-compose.yml. Un componente no desplegado no puede ser explotado. Verifique que ningún pipeline dependa del AI Gateway API antes de eliminarlo.
Optimizar la RAM: swap y workers de Puma
Reducir workers de Puma. En GITLAB_OMNIBUS_CONFIG:
puma['worker_processes'] = 2
puma['min_threads'] = 1
puma['max_threads'] = 4Cada worker Puma consume entre 200 y 300 MB. Dos workers son suficientes para menos de quince usuarios activos simultáneos.
Añadir swap. En un VPS sin swap preconfigurado:
fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstabRecomendaciones de RAM y CPU por tamaño de equipo
| Tamaño del equipo | RAM recomendada | vCPU | Notas |
|---|---|---|---|
| 1–5 desarrolladores | 4 GB | 2 | Runners en la misma máquina posibles con swap |
| 5–15 desarrolladores | 8 GB | 4 | Runner dedicado aconsejable a partir de 8+ pipelines/día |
| 15–30 desarrolladores | 16 GB | 8 | Registry Docker activo, Gitaly bajo carga |
| 30+ desarrolladores | 32 GB+ | 16+ | Separar Gitaly y Sidekiq en máquinas dedicadas |
GitLab CE, Forgejo o Gitea: cómo elegir
Desplace la tabla
| Criterio | GitLab CE | Forgejo / Gitea |
|---|---|---|
| Licencia | MIT / EE Core | MIT |
| RAM en reposo | 300–600 MB (Puma + Sidekiq) | 30–50 MB |
| CI/CD nativa | Sí (`.gitlab-ci.yml`, runners) | Forgejo: sí mediante Woodpecker CI; Gitea: no nativa |
| Registry Docker integrado | Sí | Forgejo: sí; Gitea: no nativo |
| Wikis por proyecto | Sí | Sí |
| LDAP / SAML | Sí (CE) | Forgejo: sí; Gitea: LDAP sí, SAML no |
| Curva de aprendizaje | Alta (interfaz rica) | De baja a media |
| Ideal para | Equipos de 5+ con CI, registry y RBAC | Equipos ligeros, forja simple, poca huella de RAM |
Cuándo elegir GitLab CE y cuándo elegir Forgejo
GitLab CE es la elección correcta si su equipo necesita una CI/CD robusta directamente integrada en la forja, un registry Docker privado en su dominio, pipelines de despliegue con environments y deploy tokens, o una gestión de proyecto con issues, milestones y kanban sin herramienta externa. Su huella de memoria (4–8 GB) es el precio de esa riqueza funcional. Forgejo o Gitea se imponen cuando la memoria es la restricción principal, cuando la forja es la única necesidad (sin CI integrada). En un VPS de 2 GB, GitLab CE no es viable; Forgejo funciona con 256 MB. En un VPS de 8 GB dedicado a un equipo de desarrolladores, GitLab CE ofrece un entorno completo equivalente al que GitHub pone en su plataforma SaaS — bajo su control y sin suscripción mensual por usuario.