Gitea te da tu propio GitHub en casa: alojas repositorios, revisas código, gestionas incidencias y lanzas CI, todo en un servidor que controlas y con una huella de recursos diminuta. En esta guía instalas Gitea con Docker mediante un docker-compose.yml que lo empareja con PostgreSQL, entiendes por qué expone dos puertos (el 3000 para la web y el 22 para Git por SSH), completas la configuración inicial creando el primer usuario administrador, cierras el registro abierto y activas Gitea Actions para tener integración continua sin salir de tu servidor. La misma explicación está disponible en inglés.

Puntos clave

  • Gitea es un servicio Git autoalojado, de código abierto (licencia MIT) y escrito en Go; nació como una bifurcación de Gogs en noviembre de 2016 y hoy es la alternativa ligera a GitHub y GitLab de referencia para servidores modestos.
  • Es un único binario sin dependencias: la documentación oficial lo define como "un servicio de desarrollo de software autoalojado, todo en uno e indoloro", y funciona con soltura en 1 GB de RAM, incluso en una Raspberry Pi 3.
  • La versión estable más reciente es 1.27.0 (13 de julio de 2026), pero arrastra una regresión en las páginas de código, así que en esta guía se fija la 1.26.4 (21 de junio de 2026), que además parchea un fallo de seguridad importante.
  • Publica la interfaz web en el puerto 3000 y el acceso Git por SSH en el puerto 22 del contenedor; la configuración se inyecta con variables de entorno con el prefijo GITEA__.
  • Gitea Actions, disponible desde la versión 1.19, ofrece CI compatible con la sintaxis de GitHub Actions y se ejecuta con el Act Runner de Gitea; los webhooks disparan despliegues y notificaciones hacia otros servicios.

¿Qué es Gitea y para qué sirve?

Gitea es una plataforma de alojamiento Git que puedes ejecutar en tu propio servidor: cumple el mismo papel que GitHub o GitLab (repositorios, ramas, pull requests, incidencias, wiki, releases y un registro de paquetes), pero los datos y el control son tuyos. Está escrita en Go y se distribuye como un solo binario estático sin dependencias externas, lo que explica su rasgo más citado: apenas consume recursos. Donde GitLab pide varios gigabytes de RAM, Gitea se mueve con holgura en 1 GB y arranca en un par de segundos, así que cabe en un servidor doméstico, una máquina virtual pequeña o incluso una Raspberry Pi 3.

El proyecto nació en noviembre de 2016 como una bifurcación comunitaria de Gogs, con la idea de acelerar el desarrollo y abrirlo a más gente. Desde entonces casi todo el código se ha reescrito y hoy es un proyecto maduro con publicaciones frecuentes. La documentación oficial lo resume como "un servicio de desarrollo de software autoalojado, todo en uno e indoloro", y esa palabra, indoloro, marca la diferencia frente a GitLab: menos piezas que mantener, menos memoria y una instalación que en Docker cabe en un archivo.

En un homelab, Gitea encaja como el corazón de tu flujo de trabajo: guardas ahí el código de tus proyectos y tus archivos de Docker Compose, disparas despliegues con Actions o webhooks y, si además montas un registro privado de imágenes Docker, tienes un ciclo completo de código a contenedor sin depender de servicios externos.

¿Cómo es el docker-compose.yml de Gitea con PostgreSQL?

Gitea puede usar SQLite para pruebas, pero para un uso serio conviene una base de datos de verdad. Este docker-compose.yml levanta dos servicios: Gitea y un PostgreSQL con su volumen persistente. Gitea espera a que la base de datos esté sana antes de arrancar y toda la configuración se pasa por variables de entorno con el prefijo GITEA__seccion__CLAVE, que mapean a las secciones del archivo app.ini:

services:
  gitea:
    image: gitea/gitea:1.26.4
    container_name: gitea
    restart: unless-stopped
    environment:
      USER_UID: "1000"
      USER_GID: "1000"
      GITEA__database__DB_TYPE: "postgres"
      GITEA__database__HOST: "db:5432"
      GITEA__database__NAME: "gitea"
      GITEA__database__USER: "gitea"
      GITEA__database__PASSWD: "cambia-esta-contrasena-db"
      GITEA__server__DOMAIN: "git.midominio.es"
      GITEA__server__ROOT_URL: "https://git.midominio.es/"
      GITEA__server__SSH_PORT: "2222"
      GITEA__service__DISABLE_REGISTRATION: "true"
      GITEA__actions__ENABLED: "true"
    volumes:
      - gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:22"
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:3000/api/healthz"]
      interval: 30s
      timeout: 5s
      retries: 5

  db:
    image: postgres:17.5
    container_name: gitea-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: "gitea"
      POSTGRES_PASSWORD: "cambia-esta-contrasena-db"
      POSTGRES_DB: "gitea"
    volumes:
      - gitea_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U gitea"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  gitea_data:
  gitea_db:

Merece la pena entender las piezas clave. La imagen se fija a gitea/gitea:1.26.4 en lugar de a la etiqueta móvil :latest, para que una recreación del contenedor no te salte de versión sin avisar; en un servicio que guarda todo tu código eso es innegociable. USER_UID y USER_GID fijados a 1000 alinean el usuario interno con el propietario habitual de los archivos, aunque con un volumen con nombre Docker gestiona los permisos por ti. Las variables GITEA__database__* apuntan a PostgreSQL por el nombre de servicio db, que resuelve dentro de la red interna de Compose. DISABLE_REGISTRATION: "true" cierra el registro público desde el primer arranque, un ajuste importante si el servidor va a estar expuesto. El bloque depends_on con condition: service_healthy retrasa el arranque de Gitea hasta que pg_isready confirme que la base de datos acepta conexiones, y el healthcheck de Gitea consulta su propio endpoint /api/healthz. El único volumen de Gitea, /data, guarda los repositorios, la configuración app.ini, las claves SSH y la base de datos de sesiones. La contraseña se repite en ambos servicios: si prefieres no exponerla en el archivo, muévela a un .env o a un secreto de Docker.

Levanta la pila y sigue el arranque:

docker compose up -d
docker compose ps
docker compose logs -f gitea

Si necesitas repasar cómo se prepara PostgreSQL en contenedor (usuario, volumen y copias con pg_dump), tienes la guía dedicada a PostgreSQL con Docker, que complementa a esta.

¿Por qué Gitea expone dos puertos (HTTP y SSH)?

Un servidor Git necesita dos caminos, y por eso el contenedor publica dos puertos. El puerto 3000 sirve la interfaz web y la API HTTP: es la que abres en el navegador y por la que puedes clonar con https://. El puerto 22 es el servidor SSH integrado de Gitea, que atiende los git clone, git push y git pull por SSH usando las claves públicas que cada usuario sube a su perfil. Ese servidor SSH vive dentro del contenedor y no tiene nada que ver con el SSH del sistema anfitrión.

Ahí surge el detalle práctico: el anfitrión suele tener su propio sshd ocupando el puerto 22, así que no puedes mapear 22:22 sin chocar con él. La solución más limpia es publicar el SSH de Gitea en otro puerto del anfitrión, por ejemplo 2222:22, y avisar a Gitea de ello con GITEA__server__SSH_PORT: "2222". Esa variable no cambia el puerto en el que escucha el contenedor (sigue siendo el 22 interno), solo hace que las URL de clonado que muestra la interfaz incluyan el puerto correcto, de forma que un git clone ssh://git@git.midominio.es:2222/usuario/repo.git funcione a la primera. Con esta configuración, tus repositorios quedan accesibles por web en el 3000 y por SSH en el 2222.

Para no exponer el 3000 en claro a Internet, lo habitual es poner Gitea detrás de un proxy inverso como Traefik que le añada un dominio y un certificado HTTPS de Let’s Encrypt. Recuerda que el proxy inverso solo enruta el tráfico HTTP del puerto 3000; el puerto SSH tiene que seguir publicado directamente en la máquina, porque SSH no pasa por el proxy HTTP.

¿Cómo se hace la configuración inicial y el primer usuario?

Con la pila en marcha, abre http://IP-DEL-SERVIDOR:3000 en el navegador. La primera vez, Gitea muestra la pantalla de instalación con la configuración ya rellenada a partir de las variables de entorno: el tipo de base de datos, el host y las credenciales aparecen fijados, así que no tienes que tocarlos. Baja hasta la sección de configuración opcional, despliega los ajustes de la cuenta de administrador y crea ahí el primer usuario: ese usuario será administrador del sistema. Es la forma recomendada, porque si no defines un administrador en la instalación, el primer usuario que se registre lo será por defecto, y eso es justo lo que quieres evitar en un servidor accesible.

Revisa que el dominio y la URL base (ROOT_URL) coincidan con cómo vas a acceder al servicio; si vas a usar HTTPS tras un proxy, deben apuntar a https://tu-dominio/. Pulsa el botón de instalar y Gitea escribe el archivo app.ini en el volumen /data, migra el esquema de la base de datos y te lleva a la pantalla de acceso. A partir de ese momento, entra con el usuario administrador que creaste. Como en el Compose ya pusiste DISABLE_REGISTRATION: "true", nadie más podrá crearse una cuenta: los nuevos usuarios los das de alta tú desde el panel de administración. Para gestionar contraseñas y tokens de acceso de forma segura, un gestor como Vaultwarden es un buen compañero de este montaje.

¿Cómo funcionan Gitea Actions y los webhooks?

Gitea Actions es el sistema de integración continua integrado en Gitea desde la versión 1.19. Su gran ventaja es que usa una sintaxis casi idéntica a la de GitHub Actions: los flujos de trabajo se definen en archivos YAML dentro de .gitea/workflows/ del repositorio, con los mismos on, jobs y steps, e incluso puedes reutilizar muchas actions del ecosistema de GitHub. En el Compose ya activaste el motor con GITEA__actions__ENABLED: "true", pero eso solo habilita la parte del servidor; falta quien ejecute los trabajos.

Esa pieza es el Act Runner de Gitea, un programa aparte, también escrito en Go, que es una bifurcación de nektos/act y que ejecuta cada trabajo dentro de un contenedor. Se despliega como un servicio más, se registra contra tu Gitea con un token que obtienes en la administración y, a partir de ahí, cada push que coincida con un flujo lanza su ejecución. Es el equivalente autoalojado a los runners de GitHub.

Los webhooks cubren el otro lado de la automatización: en la configuración de cada repositorio defines una URL a la que Gitea enviará una petición HTTP cuando ocurra un evento (un push, una nueva incidencia, una release). Con eso disparas un despliegue en tu servidor, una reconstrucción de imágenes o una notificación. La combinación de Actions para construir y webhooks para avisar convierte a Gitea en el centro de un flujo de trabajo completo sin depender de la nube.

Preguntas frecuentes

¿Por qué no debo usar la etiqueta latest de Gitea?

Porque gitea/gitea:latest apunta siempre a la última versión publicada, y una recreación del contenedor podría subirte a una versión mayor con cambios de esquema o de configuración sin que lo decidas tú. En un servicio que aloja todo tu código eso es un riesgo innecesario. Fija una etiqueta concreta como gitea/gitea:1.26.4, que además incorpora el parche de CVE-2026-20896 (las imágenes antiguas traían REVERSE_PROXY_TRUSTED_PROXIES con un comodín que permitía suplantar usuarios), lee las notas de versión y actualiza recreando el contenedor cuando te venga bien.

¿Qué base de datos elijo: SQLite, MySQL o PostgreSQL?

SQLite no necesita otro contenedor y va bien para probar o para un uso personal muy ligero, pero rinde peor con concurrencia y complica las copias en caliente. Para cualquier uso en equipo, PostgreSQL (o MariaDB) es la opción recomendada por su robustez y su rendimiento, y es la que usa esta guía. Si ya tienes un contenedor de base de datos, puedes reutilizarlo creando una base de datos y un usuario dedicados para Gitea.

¿Cómo hago copias de seguridad de Gitea?

Necesitas dos cosas: el volumen /data, que contiene los repositorios, el app.ini y las claves, y un volcado de la base de datos con pg_dump. Gitea incluye además el comando gitea dump, que empaqueta repositorios, configuración y base de datos en un único archivo comprimido; ejecútalo con docker exec -u git gitea gitea dump -c /data/gitea/conf/app.ini y guarda el resultado fuera del servidor. Restaurar es tan sencillo como recuperar el volumen y reimportar el volcado.

Conclusión

Con un solo docker-compose.yml tienes tu propio servicio Git self-hosted: Gitea sobre PostgreSQL, con la web en el puerto 3000, Git por SSH en el 2222, el registro abierto cerrado y Gitea Actions listo para CI. El camino completo es levantar la pila, crear el primer usuario administrador en la pantalla de instalación, ponerlo detrás de un proxy inverso con HTTPS y sumar un Act Runner cuando quieras construir. A partir de ahí controlas tu código de principio a fin, con una huella de recursos que cabe hasta en una Raspberry Pi. Para publicarlo con dominio y certificado, usa Traefik; y para cerrar el ciclo de código a contenedor, súmale un registro privado de imágenes Docker.

Fuentes

  1. Documentación oficial de instalación de Gitea con Docker
  2. Imagen oficial gitea/gitea en Docker Hub
  3. Documentación de Gitea Actions
  4. Notas de versión de Gitea 1.26.3 y 1.26.4 en el blog oficial

Ruta: Redes y acceso remoto seguro con Docker