Guide de déploiement

Stack IA self-hosted : Gitea, runners et Ollama sur VPS

Déployer sur un VPS Cloud →

Intelligence Artificielle9 min de lecture

Stack IA self-hosted : Gitea, runners et Ollama sur VPS

GitHub Actions facture le temps de calcul, les API LLM cloud transmettent votre code aux serveurs du fournisseur. Pour un développeur qui gère du code client confidentiel, ces deux dépendances externes ne sont pas une option. Ce guide explique comment assembler, sur un seul VPS, une forge Gitea avec son moteur d'actions, un runner en conteneur Docker et un serveur d'inférence Ollama — un pipeline complet où ni une ligne de code ni un prompt ne quitte votre infrastructure. Cette approche a rassemblé 119 points sur Hacker News le 21 août 2026 (discussion : https://news.ycombinator.com/item?id=49390463), signal que l'appétit pour les pipelines IA souverains ne faiblit pas.

Pourquoi assembler cette stack sur un VPS

GitHub Actions et les API LLM cloud partagent le même défaut : ils déportent le traitement. Dans le premier cas, c'est votre code source qui tourne sur des runners mutualisés ; dans le second, c'est le contexte de vos requêtes qui est envoyé à un tiers. Pour une agence ou un indépendant qui gère du code client sous NDA, l'un ne peut pas aller sans corriger l'autre.

Gitea intègre nativement un moteur d'actions compatible GitHub Actions depuis sa version 1.19. Ollama expose une API REST locale sur le réseau Docker interne. Un runner act_runner en conteneur lit vos workflows .yml comme GitHub les lirait — et appelle Ollama à la place d'une API cloud. La stack entière fonctionne avec docker compose up -d et ne génère aucun trafic sortant vers les API de grands modèles.

Bénéfices concrets de cette architecture

  • Confidentialité totale du code : les runners s'exécutent sur votre VPS, le code cloné ne quitte jamais votre réseau Docker interne.
  • Zéro token envoyé à l'extérieur : Ollama sert l'inférence localement ; aucune requête ne part vers openai.com ou anthropic.com.
  • Coût fixe et prévisible : pas de facturation à l'exécution de workflow, pas de facturation au token — une ligne de budget mensuelle, quelle que soit la charge.
  • Workflows GitHub Actions réutilisables : Gitea Actions est compatible avec la syntaxe .github/workflows/ ; vos pipelines migrent sans réécriture.
  • Modèles librement échangeables : Qwen2.5-Coder, DeepSeek-Coder, Llama 3.1 ou Mistral — une commande ollama pull pour changer de modèle, sans toucher au pipeline.
  • Audit et traçabilité complets : logs de runners, historique des modèles chargés et journaux nginx restent sur votre infrastructure et vous appartiennent.

Prérequis matériels et logiciels

La contrainte dimensionnante est la mémoire vive : le modèle doit tenir entièrement en RAM pour que l'inférence reste réactive. Un modèle 7B quantisé au format Q4_K_M demande environ 5 à 6 Go de RAM ; en ajoutant Gitea (moins de 100 Mo au repos) et le runner, comptez 8 Go minimum pour un modèle 7B et 16 Go recommandés pour un modèle 13B.

Pour le CPU, deux vCPU suffisent pour Gitea et les runners ; l'inférence CPU-only sur un modèle 7B prend quelques secondes par réponse, ce qui est acceptable pour une revue de code automatisée. Un GPU dédié réduit ce délai à moins d'une seconde, mais n'est pas requis pour un usage pipeline CI.

Logiciels requis sur le VPS : Docker Engine et Docker Compose v2, un nom de domaine pointant vers votre serveur (pour les certificats TLS de Gitea), et les ports 3000 (Gitea) et 11434 (Ollama, réseau interne uniquement) disponibles.

Modèles recommandés selon la RAM disponible

  • Qwen2.5-Coder:7B (format Q4_K_M, ~5 Go de RAM) — rapport qualité/ressources équilibré pour les revues de code en 2026 ; compréhension du contexte étendu et des conventions de style.
  • DeepSeek-Coder:6.7B (format Q4, ~4,5 Go de RAM) — alternative compacte si la RAM est serrée ; précis sur le Python, le JavaScript et les diffs de moins de 200 lignes.
  • Llama 3.1:8B (format Q4_K_M, ~5,5 Go de RAM) — généraliste multilingue, utile si vos projets mélangent le code et de la documentation en plusieurs langues.
  • Mistral:7B (format Q4_K_M, ~4,5 Go de RAM) — réponses courtes et directes, idéal pour un résumé de diff plutôt qu'une analyse détaillée.
  • Passage à un modèle 13B — sur un VPS de 16 Go ou plus, codellama:13b ou qwen2.5-coder:14b améliorent les revues sur des diffs larges ; le temps d'inférence CPU passe de ~5 s à ~15 s par appel.

Déployer la stack Gitea + Ollama + runner

01

Créer la structure de fichiers

Créez un répertoire de projet et un fichier docker-compose.yml qui déclare trois services : gitea, ollama et runner. Placez tous les volumes dans un sous-dossier data/ pour faciliter les sauvegardes.

mkdir -p ~/gitea-stack/data/{gitea,ollama,runner}
cd ~/gitea-stack
02

Écrire le docker-compose.yml

Le fichier déclare un réseau interne ai-net sur lequel les trois services communiquent. Ollama n'est pas exposé sur l'hôte : seul le runner peut l'atteindre via le réseau Docker.

cat > docker-compose.yml << 'EOF'
version: "3.8"

networks:
  ai-net:
    driver: bridge

volumes:
  gitea-data:
  ollama-data:
  runner-data:

services:
  gitea:
    image: gitea/gitea:latest
    restart: unless-stopped
    networks: [ai-net]
    ports:
      - "3000:3000"
      - "2222:22"
    volumes:
      - gitea-data:/data
    environment:
      - GITEA__server__DOMAIN=git.votredomaine.com
      - GITEA__server__ROOT_URL=https://git.votredomaine.com
      - GITEA__actions__ENABLED=true

  ollama:
    image: ollama/ollama:latest
    restart: unless-stopped
    networks: [ai-net]
    volumes:
      - ollama-data:/root/.ollama

  runner:
    image: gitea/act_runner:latest
    restart: unless-stopped
    networks: [ai-net]
    volumes:
      - runner-data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - GITEA_INSTANCE_URL=http://gitea:3000
      - GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_TOKEN}
      - GITEA_RUNNER_NAME=local-runner
      - OLLAMA_URL=http://ollama:11434
EOF
03

Démarrer Gitea et récupérer le token runner

Démarrez uniquement Gitea en premier pour terminer l'installation initiale et générer le token d'enregistrement du runner.

docker compose up -d gitea

Ouvrez http://<ip-du-vps>:3000 dans votre navigateur, terminez l'assistant d'installation, puis allez dans Paramètres du site → Actions → Runners pour créer un token d'enregistrement. Notez ce token — vous en aurez besoin à l'étape suivante.

04

Démarrer Ollama et télécharger un modèle

Démarrez Ollama et téléchargez votre modèle. Pour un pipeline de revue de code, qwen2.5-coder:7b ou deepseek-coder:6.7b offrent un bon rapport qualité/ressources.

docker compose up -d ollama
# télécharger un modèle de code
docker exec gitea-stack-ollama-1 ollama pull qwen2.5-coder:7b
# vérifier que l'API répond sur le réseau interne
docker run --rm --network gitea-stack_ai-net curlimages/curl \
  http://ollama:11434/api/tags

La réponse JSON liste les modèles disponibles — confirmation que l'API Ollama est accessible depuis le réseau Docker interne.

05

Enregistrer le runner et démarrer la stack complète

Créez un fichier .env avec le token récupéré à l'étape 3, puis démarrez le runner.

echo "RUNNER_TOKEN=votre_token_ici" > .env
docker compose up -d runner
# vérifier que le runner est bien enregistré
docker compose logs runner | tail -20

Vérifiez dans Paramètres du site → Actions → Runners que votre runner apparaît avec le statut Actif.

06

Créer un workflow qui appelle Ollama

Dans un dépôt Gitea, créez .gitea/workflows/review.yml. Le workflow clone le code, appelle l'endpoint /api/chat d'Ollama via curl et poste le résultat en commentaire de la pull request.

cat > .gitea/workflows/review.yml << 'EOF'
name: AI Code Review
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Revue de code via Ollama
        run: |
          DIFF=$(git diff HEAD~1 --unified=5 | head -200)
          curl -s ${OLLAMA_URL:-http://ollama:11434}/api/chat \
            -H 'Content-Type: application/json' \
            -d "{\"model\": \"qwen2.5-coder:7b\", \"stream\": false,
                 \"messages\": [{\"role\": \"user\",
                 \"content\": \"Revue ce diff Git en français : $DIFF\"}]}" \
            | jq -r '.message.content'
EOF

Poussez ce fichier sur votre forge Gitea — le runner le détecte et lance le job sur le réseau interne, sans aucun accès à Internet.

07

Mettre Gitea derrière un reverse proxy HTTPS

Placez Gitea derrière nginx ou Caddy avec un certificat Let's Encrypt pour exposer la forge sur git.votredomaine.com. Le port 3000 ne doit plus être accessible directement depuis l'extérieur.

# exemple nginx minimal
cat > /etc/nginx/sites-available/gitea.conf << 'EOF'
server {
    listen 443 ssl;
    server_name git.votredomaine.com;
    ssl_certificate /etc/letsencrypt/live/git.votredomaine.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/git.votredomaine.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
EOF
nginx -t && systemctl reload nginx

Ollama reste uniquement sur le réseau Docker interne et n'est jamais exposé sur Internet.

Vérifier qu'aucun token ne part vers l'extérieur

Pour confirmer que les appels LLM restent bien sur votre VPS, capturez le trafic réseau sortant pendant l'exécution d'un workflow : tcpdump -i eth0 -n 'dst port 443 and dst host openai.com'. Zéro paquet capturé valide l'isolation. Vous pouvez aussi inspecter les logs Ollama (docker compose logs ollama) : chaque requête d'inférence y est tracée avec l'adresse source — elle doit toujours être l'IP du réseau Docker interne, jamais une IP externe.

Coût comparé : VPS self-hosted vs API cloud

Un modèle 7B traite environ 30 000 à 50 000 tokens par minute sur deux vCPU. Une revue de diff de 150 lignes consomme environ 800 tokens (prompt + réponse). Sur 1 000 revues mensuelles — un volume réaliste pour une équipe de cinq développeurs — cela représente 800 000 tokens.

Côté cloud, gpt-4o-mini facture 0,15 $ pour 1 million de tokens en entrée et 0,60 $ en sortie (tarifs OpenAI de septembre 2026). Pour 800 000 tokens : environ 0,70 $ par mois. Claude Haiku est dans la même fourchette. L'argument financier est donc faible pour des volumes modérés.

L'argument qui tient est la confidentialité : les diffs de code client, les clés d'API présentes dans les messages d'erreur, les noms de variables métier — tout cela sort du réseau avec chaque appel à une API externe. Sur un VPS ServOrbit à 8 Go de RAM (autour de 15 € par mois), l'inférence locale ne coûte rien de plus et aucun token ne part vers l'extérieur.

Aller plus loin : observer et enrichir le pipeline

Une fois la stack de base opérationnelle, deux extensions sont naturelles. La première est d'ajouter Langfuse en cinquième service : il trace chaque appel LLM (modèle, durée, tokens consommés, résultat), ce qui permet de mesurer la qualité des revues, de détecter les régressions de modèle et de comparer les résultats sur votre corpus réel avant de changer de version. La seconde est de paralléliser les runners : un second act_runner avec un label différent (par exemple gpu) peut cibler un nœud avec accès GPU pour les jobs d'inférence lourds, tandis que les jobs légers (lint, tests unitaires) continuent sur le runner CPU de base.

Pour la sauvegarde, les volumes gitea-data et ollama-data contiennent respectivement les dépôts et les modèles téléchargés. Un snapshot quotidien de ces deux volumes suffit à restaurer la stack complète en moins de dix minutes. Les modèles Ollama pouvant être retéléchargés à la demande depuis le registre public, seul gitea-data est réellement critique pour la continuité de service et la préservation de l'historique Git.

Votre VPS pour cette stack IA

Un VPS ServOrbit avec accès root, Docker préinstallé et IPv4 dédiée est la base sur laquelle Gitea, les runners et Ollama tournent sans partage de ressources ni trafic sortant imposé.

Besoin d'aide ?

Parcourez notre centre d'aide et notre FAQ, ou contactez notre équipe — rappel, WhatsApp ou e-mail. Support en français, anglais et arabe.

Écrire sur WhatsApps'ouvre dans un nouvel onglet