Pourquoi self-héberger Gitea sur un VPS
Gitea consomme à peine 200 à 300 Mo de RAM au repos, ce qui en fait l'une des forges Git les plus efficaces à auto-héberger. Sur un VPS, vous gardez la maîtrise totale de votre code source : aucune donnée n'est analysée par un tiers, aucune limite arbitraire sur le nombre de dépôts privés ou de collaborateurs, et aucun coût qui grimpe avec votre équipe. Pour une agence ou un développeur indépendant, un VPS unique peut héberger l'ensemble des projets clients avec des permissions cloisonnées par organisation. Gitea intègre nativement un CI/CD compatible avec les workflows GitHub Actions (Gitea Actions), un registre de paquets et un éditeur web, ce qui couvre la quasi-totalité d'un cycle de développement sans dépendance externe.
Bénéfices concrets d'un Gitea auto-hébergé
- Empreinte mémoire minime : tourne confortablement sur un VPS 2 Go de RAM même avec plusieurs dizaines d'utilisateurs.
- Dépôts privés sans facturation par siège ni limite imposée, contrairement aux offres SaaS.
- Gitea Actions intégré pour exécuter vos pipelines CI/CD sans service externe.
- Registre de paquets (Docker, npm, Composer, Maven) hébergé sur la même instance.
- Authentification fine : LDAP, OAuth2, jetons d'accès personnels et clés SSH par utilisateur.
- Sauvegarde triviale : tout tient dans un volume de données et une base de données.
Prérequis matériels et logiciels
Gitea est très frugal. Pour une petite équipe (jusqu'à 20 utilisateurs), un VPS avec 2 vCPU et 2 Go de RAM suffit largement ; comptez 4 Go si vous activez Gitea Actions avec des runners locaux qui compilent du code. Prévoyez au moins 20 à 40 Go de stockage SSD selon la taille de vos dépôts. Côté logiciel : Docker Engine et le plugin Docker Compose installés, un nom de domaine (ou sous-domaine type git.votredomaine.com) pointant vers l'IP du VPS via un enregistrement A, et le port 22 réservé au SSH du serveur — Gitea exposera son propre SSH sur un autre port pour éviter le conflit.
Déployer Gitea avec Docker et SSL
Préparer le VPS et Docker
Connectez-vous en SSH, mettez à jour le système puis installez Docker et le plugin Compose. Créez un répertoire dédié :
mkdir -p /opt/gitea && cd /opt/gitea. Créez aussi un volume de données qui survivra aux mises à jour du conteneur.Rédiger le docker-compose.yml
Définissez deux services :
gitea(imagegitea/gitea:1.25) et une basepostgres:16. Montez./gitea:/datapour la persistance, fixezUSER_UID/USER_GIDà 1000, et mappez le SSH de Gitea sur le port hôte 2222 :"2222:22". Laissez le port HTTP 3000 interne, il sera servi par le reverse proxy.Lancer les conteneurs
Exécutez
docker compose up -dpuisdocker compose logs -f giteapour suivre l'initialisation. Vérifiez que la connexion à PostgreSQL réussit avant de continuer.Configurer le reverse proxy
Avec Caddy, une seule ligne suffit :
git.votredomaine.com { reverse_proxy localhost:3000 }. Caddy obtient et renouvelle automatiquement le certificat Let's Encrypt. Avec Nginx, créez unserverqui proxifie vershttp://127.0.0.1:3000et utilisezcertbot --nginxpour le SSL.Finaliser l'installation web
Ouvrez
https://git.votredomaine.com, complétez l'assistant en renseignant l'URL de base HTTPS et l'hôte de la base de données (db:5432). Définissez leROOT_URLcorrectement, sinon les liens de clonage seront erronés. Dépliez « Paramètres optionnels > Paramètres du compte administrateur », créez-y votre compte admin puis validez.Sécuriser le SSH des dépôts
Configurez vos clients pour cloner via
ssh://[email protected]:2222/..., ou ajoutez un blocHostdans~/.ssh/configpour masquer le port. Désactivez l'inscription publique dans l'admin si l'instance est privée.
Gitea Actions : CI/CD auto-hébergé sur votre VPS
Gitea Actions est le moteur de CI/CD natif de Gitea, compatible avec la syntaxe des workflows GitHub Actions. Il permet d'exécuter des pipelines directement sur votre infrastructure, sans dépendance à un service externe comme GitHub Actions ou GitLab CI.
Pour l'activer, ajoutez dans votre app.ini :
[actions]
ENABLED = trueEnregistrez ensuite un runner act_runner dans un conteneur séparé sur le même VPS. Limitez sa RAM via mem_limit pour qu'un build gourmand n'étouffe pas Gitea lui-même. Pour des pipelines lourds (compilation, tests d'intégration), dédiez plutôt un second VPS au runner.
Un workflow minimal pour un projet Node.js se place dans .gitea/workflows/ci.yml :
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm testLe runner exécute ce workflow à chaque push. Vous pouvez combiner plusieurs jobs, utiliser des matrices de versions, et partager des artefacts entre étapes — exactement comme avec GitHub Actions, mais sur votre propre serveur.
Limitez la RAM du runner act_runner à 2 Go via mem_limit: 2g dans le docker-compose.yml si votre VPS a 4 Go au total. Ainsi, même un build qui consomme tout ce qui lui est alloué ne laisse que 2 Go à Gitea — suffisant pour continuer à servir les push et les pull requests pendant la compilation. Pour les builds qui produisent des artefacts volumineux (images Docker, binaires), ajoutez un volume séparé monté dans /home/runner/work afin de ne pas saturer le volume principal de Gitea.
Gitea ou Forgejo en 2026 : lequel choisir ?
Faites défiler le tableau
| Critère | Gitea 1.25 | Forgejo v16 |
|---|---|---|
| Gouvernance | Société commerciale (Gitea Ltd) | Association à but non lucratif (Codeberg e.V.) |
| Licence | MIT | MIT — 100 % logiciel libre, sans édition entreprise |
| Recommandé par Awesome-Selfhosted | Non (retiré en 2022) | Oui — recommandation par défaut depuis 2024 |
| Fédération ActivityPub | Non prévue | En déploiement progressif depuis Forgejo v7 (avril 2024), GA dans v16 |
| Compatibilité API Gitea | Référence | Compatible : même API, mêmes webhooks, même format de données |
| Sécurité — audits publics | Rares | Audits de code publiés par la communauté Codeberg |
| Roadmap communautaire | Pilotée par Gitea Ltd | Gouvernance ouverte, RFC publiques, vote des contributeurs |
| Migration depuis Gitea | N/A | Sans perte de données : même schéma de base et même format de volume |
Migrer de Gitea vers Forgejo
Sauvegarder l'instance Gitea
Avant toute opération, faites un dump complet :
docker exec -u git gitea gitea admin dump -c /data/gitea/conf/app.ini. Récupérez l'archive générée hors du conteneur et vérifiez que le volume./giteaest inclus dans votre snapshot VPS.Vérifier la compatibilité de version
Forgejo v16 supporte la migration depuis Gitea 1.20 et ultérieur. Si votre instance tourne sur une version antérieure, montez d'abord à Gitea 1.21 ou 1.22 via
docker compose pull && docker compose up -d, puis vérifiez l'absence d'erreurs dans les logs avant de continuer.Remplacer l'image Docker
Dans votre
docker-compose.yml, remplacezimage: gitea/gitea:latestparimage: codeberg.org/forgejo/forgejo:latest. Le volume de données (./gitea:/data) et la base PostgreSQL restent inchangés — Forgejo lit le même schéma et le mêmeapp.ini.Redémarrer et laisser migrer
Exécutez
docker compose pull && docker compose up -d. Forgejo applique automatiquement les migrations de schéma nécessaires au démarrage. Suivezdocker compose logs -f forgejojusqu'au message indiquant que le serveur HTTP écoute sur le port 3000.Régénérer les clés SSH
Comme pour toute mise à jour qui change le binaire référencé dans
authorized_keys, régénérez les entrées :docker exec -u git forgejo forgejo admin regenerate keys. Vérifiez ensuite qu'ungit cloneSSH réussit depuis un poste client.Tester et valider
Ouvrez l'interface web, vérifiez les dépôts, les webhooks et les runners Forgejo Actions. Les runners
act_runnerenregistrés sous Gitea sont compatibles — reconnectez-les au nouveau token d'enregistrement si la clé a changé.
Forgejo v16 : les nouveautés clés (juillet 2026)
Forgejo v16.0.0 introduit la fédération ActivityPub en disponibilité générale pour les issues et les pull requests : une issue ouverte sur une instance Forgejo peut recevoir des commentaires d'utilisateurs d'une autre instance, sans compte partagé. La version intègre aussi un gestionnaire de secrets d'instance (chiffrement AES-256 au repos), un nouveau moteur de recherche de code basé sur l'index Bleve v2 avec support des expressions régulières, et des améliorations significatives du planificateur de tâches de fond. Sur le plan de la sécurité, les tokens d'API sont désormais hachés en base (bcrypt) et non stockés en clair — une migration automatique au démarrage convertit les tokens existants.
Administration quotidienne : webhooks, dépôts et purge des orphelins
Une fois Gitea en production, quelques opérations d'entretien reviennent régulièrement.
Gérer les webhooks. Gitea enregistre l'historique de chaque déclenchement dans « Paramètres du dépôt → Webhooks → Dernières livraisons ». Un webhook en échec (timeout, erreur HTTP 5xx) peut être rejoué manuellement depuis cet historique. Si vos runners tournent sur le même réseau privé que Gitea, ajoutez leurs plages IP dans ALLOWED_HOST_LIST de la section [webhook] du app.ini — sinon les appels vers localhost sont bloqués par défaut depuis Gitea 1.20.
Purger les dépôts orphelins. Un dépôt supprimé via l'interface laisse ses données Git (objects/) sur disque jusqu'à la prochaine exécution des tâches de fond. Forcez le passage immédiat depuis l'admin : « Administration du site → Tâches de fond → Exécuter : Purger les repos supprimés ». L'espace libéré n'est visible qu'après docker exec -u git gitea gitea admin storage --remove-unlisted.
Quota de stockage par organisation. Pour les instances multi-tenants, fixez un quota de dépôts par organisation dans app.ini sous [repository] : MAX_CREATION_LIMIT = 50. Cela évite qu'un compte crée des centaines de dépôts sans surveillance et sature le volume de données.
Dépannage approfondi : SSH et clonage HTTPS
Permission denied (publickey) après mise à jour. Le binaire référencé dans authorized_keys a changé de chemin (vérifié en 1.27). Le service Gitea répond normalement via HTTPS, ce qui masque le problème. Corrigez avec : docker exec -u git gitea gitea admin regenerate keys. Vérifiez ensuite qu'un git clone SSH réussit avant de déclarer la mise à jour terminée.
Timeout ou erreur SSL lors d'un clone HTTPS. Trois causes courantes : le ROOT_URL dans app.ini ne correspond pas au domaine HTTPS réel (lien cassé + redirection infinie possible), le reverse proxy ne transmet pas l'en-tête X-Forwarded-Proto: https (Gitea génère alors des URLs en HTTP), ou le certificat Let's Encrypt a expiré (Caddy et certbot le renouvellent automatiquement, mais seulement si le DNS pointe bien vers le VPS). Vérifiez ROOT_URL en premier : c'est la cause la plus fréquente après une migration de domaine.
Clone SSH lent ou qui se bloque. L'upload-pack SSH utilise le même processus Git que les opérations web. Si votre runner act_runner mobilise tous les cœurs disponibles pendant un build, les opérations Git peuvent se retrouver en attente de CPU. Mettez un cpus: '1.5' sur le runner dans le compose pour lui réserver des cœurs sans le priver complètement.
Database migration failed: column already exists. La base a reçu une migration partielle (arrêt pendant la montée de version). Restaurez le dump réalisé avant la mise à jour, repartez d'un volume propre, puis relancez.
Passer à Gitea 1.27 sans perdre les accès SSH
La version 1.27 modifie le chemin interne du binaire gitea référencé dans authorized_keys. Lors de la mise à jour, les entrées existantes pointent vers l'ancien chemin : toute tentative de clone ou de push via SSH échoue silencieusement avec Permission denied (publickey), sans message d'erreur côté serveur qui pointe vers la mise à jour. Le service Gitea répond normalement via HTTPS, ce qui masque le problème. Après chaque mise à jour vers 1.27 (ou vers toute version qui change ce chemin), exécutez dans le conteneur : docker exec -u git gitea gitea admin regenerate keys. La commande réécrit toutes les entrées authorized_keys avec le bon chemin du binaire. Vérifiez ensuite qu'un git clone SSH réussit avant de déclarer la mise à jour terminée.
JWT_SECRET_URI et JWT_SECRET ne doivent pas coexister dans app.ini. Si les deux clés sont présentes, Gitea ou Forgejo charge l'une ou l'autre selon l'ordre de lecture, ce qui invalide en silence tous les tokens OAuth2 et Actions émis avant la mise à jour. Les utilisateurs voient des erreurs d'authentification intermittentes, sans message qui désigne app.ini comme cause. Choisissez un seul mécanisme : JWT_SECRET (valeur en clair) ou JWT_SECRET_URI (chemin vers un fichier secret), puis supprimez l'autre. Redémarrez le conteneur après la modification.
Pour aller plus loin : Forgejo et la CI/CD avancée
Si vous souhaitez pousser plus loin l'automatisation autour de votre forge Git, l'article Héberger Forgejo sur votre VPS détaille la configuration d'une instance Forgejo avec des runners Actions dédiés, la mise en place du registre OCI intégré et le durcissement sécurité (fail2ban, restriction des plages IP). Pour les équipes qui veulent une chaîne de déploiement continu complète, c'est le complément naturel de ce guide.
La documentation officielle
Pour la configuration avancée et les options propres à l'outil, référez-vous à la documentation officielle de Gitea ou à la documentation Forgejo. Ce guide couvre la mise en ligne sur VPS et la migration ; les docs éditeurs restent la référence pour les réglages fins et les cas d'usage spécifiques.