Con Forgejo Docker Compose levantas tu propio servidor Git completo en menos de diez minutos: repositorios privados sin límite, revisiones de código, registro de paquetes y CI/CD integrado. Todo en tu hardware, sin cuotas por colaborador y sin que nadie analice tu código para entrenar modelos.
Consume unos 200 MB de RAM en reposo. Un mini PC reciclado o una Raspberry Pi 4 le sobran para dar servicio a un equipo pequeño.
Al terminar este artículo tendrás
- Un servidor Git funcionando con PostgreSQL y datos persistentes.
- Acceso por HTTPS y por SSH correctamente configurado.
- El registro de contenedores y Forgejo Actions listos para usar.
- Una estrategia de copias de seguridad que de verdad puedas restaurar.
Qué es Forgejo y por qué no es simplemente Gitea
Forgejo es una forja de software libre: un servidor Git con interfaz web, incidencias, revisiones de código, wiki, registro de paquetes y CI/CD. La pregunta obvia si conoces el ecosistema es en qué se diferencia de Gitea, y la respuesta tiene más de gobernanza que de tecnología.
El proyecto nació en octubre de 2022, cuando los dominios y la marca de Gitea pasaron a una empresa con ánimo de lucro. Codeberg, que era uno de los grandes usuarios de Gitea, impulsó la bifurcación. Empezó como fork blando, se convirtió en hard fork en marzo de 2024 y en agosto de ese año cambió su licencia a GPLv3+, una licencia copyleft. Hoy la marca y los dominios están en manos de Codeberg e.V., una asociación sin ánimo de lucro.
En la práctica, si vienes de nuestro tutorial de Gitea con Docker Compose te encontrarás en casa: la interfaz y los conceptos son reconocibles. Las diferencias se acumulan release a release, y el propio proyecto mantiene una comparación honesta con Gitea que merece la pena leer antes de decidir.
Qué incluye de serie
Conviene saber qué entra en la caja antes de montarlo, porque cubre bastante más terreno del que la gente espera de un servidor Git:
- Repositorios ilimitados, públicos y privados, sin coste por colaborador.
- Incidencias, hitos y tableros de proyecto para llevar el seguimiento del trabajo.
- Pull requests con revisión de código, comentarios en línea y reglas de protección de ramas.
- Registro de paquetes multiformato: OCI, npm, PyPI, Maven, NuGet, Cargo y más.
- CI/CD integrado con sintaxis compatible con la de GitHub Actions.
- Wiki, releases y federación mediante ActivityPub, todavía en desarrollo.
Esa combinación es la que justifica el esfuerzo: no sustituyes solo a GitHub, también al registro de contenedores y al servidor de CI que quizá tengas por separado.
Requisitos previos para Forgejo Docker Compose
| Recurso | Mínimo | Recomendado |
|---|---|---|
| RAM | 512 MB | 2 GB |
| CPU | 1 núcleo | 2 núcleos |
| Disco | 10 GB | 50 GB o más |
| Docker Engine | 24.x | Última estable |
Forgejo Docker Compose es exigente sobre todo con el disco, así que sé generoso: los repositorios crecen, pero lo que dispara el consumo de verdad son el registro de paquetes y los artefactos de CI. Con 10 GB haces pruebas; para uso real, empieza en 50 GB.
Configuración de Forgejo Docker Compose paso a paso
Vamos directamente a la configuración de Forgejo Docker Compose con PostgreSQL. Se puede usar SQLite y funciona, pero en cuanto tengas varios repositorios activos y CI en marcha, notarás bloqueos de escritura. Migrar después es un engorro; empezar bien no cuesta nada. Usaremos la imagen oficial de PostgreSQL en su variante Alpine, más ligera.
Crea el directorio de trabajo y el fichero de variables. Nunca dejes las credenciales dentro del compose.
mkdir -p ~/forgejo && cd ~/forgejo
mkdir -p ./data ./postgres
cat > .env <<'EOF'
POSTGRES_USER=forgejo
POSTGRES_PASSWORD=cambia-esta-clave-larga-y-aleatoria
POSTGRES_DB=forgejo
FORGEJO_DOMAIN=git.midominio.es
EOF
chmod 600 .env
Ahora el docker-compose.yml. Está basado en el ejemplo oficial, con las variables extraídas al fichero .env y algunos ajustes de red y de dominio.
networks:
forgejo:
external: false
services:
server:
image: codeberg.org/forgejo/forgejo:16
container_name: forgejo
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__database__DB_TYPE=postgres
- FORGEJO__database__HOST=db:5432
- FORGEJO__database__NAME=${POSTGRES_DB}
- FORGEJO__database__USER=${POSTGRES_USER}
- FORGEJO__database__PASSWD=${POSTGRES_PASSWORD}
- FORGEJO__server__DOMAIN=${FORGEJO_DOMAIN}
- FORGEJO__server__ROOT_URL=https://${FORGEJO_DOMAIN}/
- FORGEJO__server__SSH_DOMAIN=${FORGEJO_DOMAIN}
- FORGEJO__server__SSH_PORT=222
- FORGEJO__service__DISABLE_REGISTRATION=true
networks:
- forgejo
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
depends_on:
- db
db:
image: postgres:16-alpine
container_name: forgejo-db
restart: always
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
networks:
- forgejo
volumes:
- ./postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
Levanta la pila de Forgejo Docker Compose y comprueba que ambos contenedores quedan en estado saludable. Si la base de datos tarda en arrancar la primera vez, es normal: está inicializando el clúster.
docker compose up -d
docker compose ps
docker compose logs -f server
Entra en http://IP-DEL-SERVIDOR:3000 y verás el asistente de instalación. Los datos de base de datos vienen ya rellenos desde las variables de entorno; lo importante aquí es crear la cuenta de administrador antes de exponer nada al exterior.
Variables de entorno que conviene conocer
| Variable | Para qué sirve |
|---|---|
USER_UID / USER_GID | Alinean el usuario del contenedor con el del host y evitan problemas de permisos en ./data. |
FORGEJO__server__ROOT_URL | URL pública. Si está mal, los enlaces de clonado y los correos saldrán rotos. |
FORGEJO__server__SSH_PORT | Puerto anunciado en la interfaz, no el interno. Debe coincidir con el publicado. |
FORGEJO__service__DISABLE_REGISTRATION | Cierra el registro público. Imprescindible si el servidor es accesible desde Internet. |
Cualquier opción del fichero app.ini se puede fijar con este patrón de doble guion bajo. El manual de configuración recoge el listado completo, que es enorme.
Dónde viven los datos
Este despliegue de Forgejo Docker Compose usa montajes de directorio en lugar de volúmenes con nombre, y es una decisión deliberada: los ficheros quedan a la vista, se respaldan con un tar normal y no dependes de docker volume para recuperarlos.
| Ruta en el host | Contenido |
|---|---|
./data/git/repositories | Los repositorios Git en bruto. |
./data/gitea/conf/app.ini | Configuración efectiva del servidor. |
./data/gitea/packages | Registro de paquetes y contenedores. |
./data/gitea/actions_artifacts | Artefactos generados por el CI. |
./postgres | Datos de PostgreSQL. |
Sí, varias rutas internas conservan el nombre gitea: es herencia del origen común y no supone ningún problema. Anota estas ubicaciones porque son exactamente las que necesitarás cuando toque restaurar.
Publicar Forgejo Docker Compose con HTTPS
Servir esto en claro por el puerto 3000 no es una opción si va a salir de tu red local. Lo habitual es ponerle delante un proxy inverso que gestione los certificados. Si ya tienes montado Traefik como proxy inverso con SSL automático, basta con añadir etiquetas al servicio.
labels:
- "traefik.enable=true"
- "traefik.http.routers.forgejo.rule=Host(`git.midominio.es`)"
- "traefik.http.routers.forgejo.entrypoints=websecure"
- "traefik.http.routers.forgejo.tls.certresolver=letsencrypt"
- "traefik.http.services.forgejo.loadbalancer.server.port=3000"
Advertencia: el SSH no pasa por el proxy
Un proxy HTTP no enruta Git por SSH. El puerto 222 tiene que llegar directamente al contenedor, así que ábrelo en el cortafuegos de forma explícita y restringido a los orígenes que necesites. Si prefieres no exponerlo, trabaja solo con HTTPS y tokens de acceso personal.
Comprueba que el acceso por SSH funciona antes de dar por buena la instalación. La respuesta correcta es un saludo del servidor seguido del cierre de la conexión.
ssh -T -p 222 git@git.midominio.es
Registro de paquetes y CI con Forgejo Docker Compose
Aquí es donde Forgejo Docker Compose deja de ser «solo Git». El registro de paquetes viene activado de serie y admite contenedores OCI, npm, PyPI, Maven, NuGet y bastantes más, sin configuración adicional.
docker login git.midominio.es
docker tag mi-app:1.0 git.midominio.es/miusuario/mi-app:1.0
docker push git.midominio.es/miusuario/mi-app:1.0
La parte de CI se llama Forgejo Actions y usa una sintaxis muy parecida a la de GitHub Actions, con los ficheros en .forgejo/workflows/. Necesita un componente aparte, el runner, que es quien ejecuta los trabajos en contenedores.
runner:
image: code.forgejo.org/forgejo/runner:6
container_name: forgejo-runner
restart: always
depends_on:
- server
networks:
- forgejo
volumes:
- ./runner:/data
- /var/run/docker.sock:/var/run/docker.sock
command: forgejo-runner daemon
Antes de arrancarlo hay que registrarlo con el token que genera la interfaz de administración. Ten presente que montar el socket de Docker da al runner control efectivo sobre el host: hazlo solo si confías en quien puede escribir workflows en tus repositorios.
Endurecer Forgejo Docker Compose antes de exponerlo
Un servidor Git accesible desde Internet acumula intentos de registro automatizado en cuestión de horas. Estas cuatro medidas son el mínimo razonable y se aplican en diez minutos.
- Registro cerrado. Ya lo llevas puesto con
DISABLE_REGISTRATION=true. Crea tú las cuentas o habilita el registro solo por invitación. - Doble factor obligatorio. En la administración puedes exigir 2FA a todas las cuentas; con acceso de escritura al código, no es opcional.
- Repositorios privados por defecto. Evita el clásico despiste de publicar un repositorio con credenciales dentro.
- Correo saliente configurado. Sin SMTP no hay recuperación de contraseña ni avisos de seguridad.
Si además quieres centralizar la identidad, Forgejo Docker Compose admite OAuth2 y OIDC contra un proveedor propio, de modo que puedes delegar el inicio de sesión en el mismo sistema que uses para el resto de servicios del homelab.
Copias de seguridad de Forgejo Docker Compose
Un servidor Git propio sin copias es una bomba de relojería. En Forgejo Docker Compose hay dos piezas que respaldar: el volumen de datos y la base de datos. Y tienen que ser consistentes entre sí.
#!/bin/bash
set -euo pipefail
DESTINO=/mnt/backups/forgejo/$(date +%F)
mkdir -p "$DESTINO"
docker compose exec -T db pg_dump -U forgejo forgejo \
| gzip > "$DESTINO/forgejo-db.sql.gz"
tar czf "$DESTINO/forgejo-data.tar.gz" ./data
find /mnt/backups/forgejo -maxdepth 1 -type d -mtime +30 -exec rm -rf {} +
Progámalo en cron y, sobre todo, prueba una restauración completa en otra máquina al menos una vez. Una copia que nunca has restaurado no es una copia, es una intención. Si prefieres algo con interfaz y cifrado, Duplicati para backups cifrados automáticos encaja bien apuntando a ese mismo directorio. Y para la base de datos conviene revisar las buenas prácticas de respaldo de PostgreSQL.
Mantenimiento y problemas frecuentes
Actualizar Forgejo Docker Compose es cambiar la etiqueta de la imagen y volver a levantar, pero con una precaución importante: haz siempre copia antes, porque las migraciones de base de datos no se revierten. Puedes automatizar las actualizaciones menores con Watchtower, aunque en un servidor Git yo prefiero hacerlas a mano.
| Síntoma | Causa habitual | Solución |
|---|---|---|
Permission denied en ./data | UID del host distinto | Ajustar USER_UID y chown -R 1000:1000 ./data |
| Enlaces de clonado con la IP | ROOT_URL mal fijada | Corregir dominio y reiniciar el contenedor |
| SSH pide contraseña | Clave pública no cargada | Añadirla en el perfil de usuario |
| El runner no aparece | Registro sin completar | Volver a registrar con un token nuevo |
| Disco lleno de golpe | Artefactos de CI antiguos | Activar la caducidad de artefactos |
Conclusión sobre Forgejo Docker Compose
Migrar de una forja alojada a un Forgejo Docker Compose propio asusta menos de lo que parece, sobre todo porque Forgejo importa repositorios desde GitHub o GitLab con incidencias y pull requests incluidas. Lo que realmente decide el éxito no es la instalación, que es lo fácil, sino haber probado la restauración de la copia antes de necesitarla.
Si vienes de Gitea y estás cómodo, no hay urgencia por cambiar. Si empiezas de cero, la gobernanza sin ánimo de lucro y la licencia copyleft son argumentos sólidos para elegir este camino. Tienes el código en el repositorio oficial en Codeberg, la guía de despliegue en la documentación de instalación con Docker y más ideas para tu servidor en los tutoriales de Docker Compose del blog.
Preguntas frecuentes sobre Forgejo Docker Compose
¿Puedo migrar desde Gitea sin perder nada?
En versiones recientes ya no es un cambio de imagen directo, porque el hard fork separó los esquemas. La vía recomendada es exportar los repositorios y usar la herramienta de migración de la interfaz, que trae incidencias, etiquetas y pull requests.
¿Vale una Raspberry Pi?
Sí, hay imágenes para arm64 y una Pi 4 con 4 GB mueve sin problema un equipo pequeño. Usa SSD por USB en lugar de tarjeta microSD: los repositorios y la base de datos castigan mucho el almacenamiento.
¿Merece la pena la variante rootless?
Para exposición a Internet, sí. Cambia la ruta de datos a /var/lib/gitea, el puerto SSH interno a 2222 y exige fijar user: 1000:1000. Es algo más de trabajo inicial a cambio de reducir bastante la superficie de ataque.
¿Puedo usar SQLite en lugar de PostgreSQL?
Funciona para uso personal con pocos repositorios. En cuanto entran varios usuarios concurrentes o CI activo, aparecen bloqueos de escritura. Como la migración posterior es incómoda, compensa empezar directamente con PostgreSQL.
¿Cuánto ocupa realmente con el tiempo?
Los repositorios de código crecen despacio. Lo que dispara el disco son las imágenes del registro de contenedores y los artefactos de CI, así que configura políticas de caducidad desde el primer día y vigila el crecimiento el primer mes.
