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.
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.