Gabarits de déploiement sur le VPS Linux : manifeste deploiement-vps.yml + action réutilisable (construction, site, base, mise en service avec retour automatique).
  • Python 81.9%
  • Shell 18.1%
Find a file
Yannick Armand Tebi 9e9b38f300 Recuperation du code du projet : 4 essais (coupures reseau du VPS)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 13:43:40 -05:00
modeles Secrets generes sur le VPS, commentaire sur la demande de fusion, preproduction hors des moteurs de recherche 2026-11-03 11:58:14 -05:00
outils Image de base : recuperation reessayee (sinon non suivie, signale) ; README : redeploiement automatique de nuit 2026-11-03 12:23:21 -05:00
.gitattributes Gabarits de deploiement sur le VPS Linux : action, manifeste, outils 2026-11-02 16:04:15 -05:00
action.yml Recuperation du code du projet : 4 essais (coupures reseau du VPS) 2026-11-03 13:43:40 -05:00
README.md Image de base : recuperation reessayee (sinon non suivie, signale) ; README : redeploiement automatique de nuit 2026-11-03 12:23:21 -05:00

deploiement-vps : gabarits de déploiement sur le VPS Linux

Ce dépôt contient tout ce qu'il faut pour qu'un projet se déploie seul sur le VPS Linux à chaque arrivée sur main. Un projet n'a besoin que de :

  1. un Dockerfile par application (ou par site statique) ;
  2. un manifeste deploiement-vps.yml à la racine, qui décrit ses applications ;
  3. le workflow modeles/deployer.yml, copié tel quel dans .forgejo/workflows/deployer.yml.

Rien d'autre à préparer sur le serveur. Au premier déploiement, l'action fait tout le nécessaire :

  • elle construit les images ;
  • elle crée le site dans la console du VPS : domaines, relais ou site statique, puis certificat ;
  • elle crée la base PostgreSQL et son compte si le manifeste en demande une, et injecte la chaîne de connexion ;
  • elle crée les dossiers de données, avec le bon propriétaire ;
  • elle écrit les secrets déclarés et génère la composition Docker ;
  • elle met en service, contrôle la santé et revient automatiquement à la version précédente en cas d'échec.

Les déploiements suivants refont la même chose : le manifeste du dépôt fait foi.

Démarrer un nouveau projet

  1. Écrire le ou les Dockerfile.

    • Conteneur : l'application écoute un port HTTP en clair. Caddy fait le HTTPS devant, depuis l'adresse fixe 172.18.0.250.
    • Site statique : l'image finale contient le site compilé dans /site, index.html à la racine. Une étape finale FROM busybox:1.37 suffit.
  2. Copier modeles/deploiement-vps.yml à la racine du projet et l'adapter.

  3. Copier modeles/deployer.yml dans .forgejo/workflows/deployer.yml.

  4. Pour une clé ou un mot de passe propre à l'application (clé JWT, clé de chiffrement…), préférer secrets_generes : rien à créer. Pour un secret venu d'ailleurs (clé d'API d'un fournisseur), le déclarer dans secrets et le créer dans Forgejo : Paramètres du dépôt > Actions > Secrets, noms en majuscules.

  5. Fusionner dans main. L'onglet Actions du dépôt montre le déploiement.

  6. Le DNS se fait seul pour les noms des zones OVH du compte (orbiteigroup.com, sates-doc.com, sates-doc.fr, sates-doc.net) : un enregistrement A vers le VPS (169.58.249.205) est créé pour chaque nom qui n'en a aucun. Un nom qui pointe déjà ailleurs (ancien serveur) n'est jamais modifié : on le bascule depuis la console (« Faire pointer ici ») ou chez le registraire. Pour un nom hors de ces zones, faire pointer le DNS à la main.

    Tant que tous les noms ne pointent pas sur le VPS, le site est en mode essai (certificat local). La console vérifie toutes les 5 minutes et passe seule en certificat Let's Encrypt dès que c'est le cas.

Plusieurs adresses et changements d'adresses. Un site sert tous les noms de sa liste domaines (avec ou sans www., sous-domaines, autres zones). Ajouter un nom au manifeste puis déployer : il est servi, son DNS créé s'il manque, son certificat obtenu. Retirer un nom : il n'est plus servi, et son enregistrement DNS est retiré seulement si c'est la console qui l'a créé et qu'aucun autre site ne s'en sert.

Le manifeste

version: 1
applications:            # déployées DANS L'ORDRE ; un échec arrête la suite
  - nom: ...             # nom du conteneur, de l'image et du site (minuscules, chiffres, tirets)
    type: conteneur      # ou statique
    ...

Champs communs

Champ Rôle
nom identifiant unique sur le VPS (dossier /srv/apps/<nom>, conteneur, image <nom>:<commit>, site de la console)
type conteneur (relais Caddy vers le conteneur) ou statique (fichiers servis par Caddy)
libelle nom affiché sur la page « service indisponible »
dockerfile, contexte Dockerfile et contexte de construction, relatifs au dépôt (défaut : Dockerfile, .)
arguments_construction --build-arg (dictionnaire)
domaines noms servis ; le premier sert au contrôle de santé
www_vers_nu www.x redirigé vers x (301), pour chaque paire présente dans domaines
sante.chemin, sante.motif adresse vérifiée après mise en service (statut 200 à 399) et texte exigé dans la réponse
sante.tous_statuts accepter tout statut HTTP : seul le motif compte

Conteneur seulement

Champ Rôle
port port écouté dans le conteneur (obligatoire)
utilisateur uid de l'image. Les volumes et fichiers du serveur lui sont attribués avant chaque mise en service, après l'arrêt de l'ancien conteneur
volumes sous-dossier: /chemin/dans/le/conteneur. Données persistantes dans /srv/apps/<nom>/<sous-dossier>, sauvegardées chaque nuit. Tout VOLUME déclaré par l'image doit y figurer, sinon Docker y place un volume anonyme vide qui masque les données
fichiers_serveur chemin-sur-le-serveur: /chemin/dans/le/conteneur, monté en lecture seule (ou {cible: ..., ecriture: true}). Pour une configuration avec secrets déposée une fois à la main dans /srv/apps/<nom>/ et jamais écrasée. Absent, le déploiement s'arrête en le signalant
environnement variables non secrètes
secrets [NOM] ou {NOM_DU_SECRET: VARIABLE} : secrets Forgejo du dépôt, réécrits dans /srv/apps/<nom>/.env à chaque déploiement
secrets_generes [VARIABLE] ou {VARIABLE: {format, octets, copie_en_preproduction}} : valeur tirée au sort sur le VPS au premier déploiement, écrite dans .env, puis jamais régénérée ni affichée. Rien à créer dans Forgejo. format : texte (lettres et chiffres, défaut), hex ou base64 ; octets : 16 à 128 (défaut 32). En préproduction, chaque secret généré a sa propre valeur (un jeton signé en préproduction n'ouvre pas la production), sauf copie_en_preproduction: true, à mettre sur une clé qui chiffre des données copiées depuis la production. Une même variable ne peut pas être à la fois dans secrets et secrets_generes
postgres {base, utilisateur, variable, format} : base et compte créés sur le PostgreSQL commun s'ils n'existent pas, mot de passe généré et chaîne écrite dans .env (format Npgsql par défaut, format: url pour postgresql://…). Une variable déjà présente dans .env n'est jamais remplacée
reseaux réseaux Docker en plus de web (bases est ajouté seul avec postgres)
memoire, memoire_partagee mem_limit et shm_size. Sans memoire, la limite acceptée dans la console du VPS (proposée depuis le pic des 7 derniers jours) est reprise à chaque déploiement

Statique seulement

Champ Rôle
chemin dossier du site dans l'image (défaut /site)
spa application monopage : une adresse inconnue sert index.html, sauf /api/* et les fichiers du service worker ; en-têtes de cache comme sous IIS
proteger fichiers repris de la version en ligne (configuration propre au serveur, par exemple assets/app-config.json)

Tests avant déploiement (facultatif, à la racine du manifeste) :

tests:                     # lancés AVANT toute construction ; un échec arrête tout
  - nom: unitaires
    image: mcr.microsoft.com/dotnet/sdk:10.0   # image où tourne la commande
    commande: dotnet test MaSolution.sln -c Release
    docker: false          # true : Docker du VPS + réseau de l'hôte (Testcontainers)
    delai: 1800            # secondes (défaut 1800)
    environnement: {}      # variables en plus

Chaque test tourne dans un conteneur jetable, sur une copie du code du commit (répertoire /src). Les caches NuGet et npm sont gardés dans des volumes Docker (deploiement-cache-*). Ne pas terminer la commande par un tube (| tail…) : le code de sortie serait celui du dernier programme, et un test en échec passerait. En urgence, la variable SANS_TESTS=1 de l'exécuteur les saute, mais c'est déconseillé.

Documentation seule : le workflow modèle ignore les commits qui ne touchent que la documentation (*.md, docs/, Documentation/, .claude/, _bmad-output/, LICENSE*) : rien n'est redéployé pour eux. Adapter la liste paths-ignore du workflow si un projet range sa documentation ailleurs.

Variante : l'entrée seulement de l'action (noms séparés par des virgules) ne déploie que les applications indiquées.

Ce que l'action écrit sur le serveur

  • /srv/apps/<nom>/compose.yaml et deploiement.conf : générés à chaque déploiement depuis le manifeste ;
  • /srv/apps/<nom>/.env : IMAGE, les secrets Forgejo, les secrets générés et la connexion PostgreSQL. Les autres lignes, posées à la main, sont gardées ;
  • le site dans la console (/srv/console-vps/etat/sites.json, puis Caddy). Ce que le manifeste ne précise pas est conservé, par exemple un en-tête secret vers l'application ajouté dans la console ;
  • une ligne au journal de la console par mise en service.

Les fichiers_serveur ne sont jamais écrits : seulement montés.

Fonctionnement

  • action.yml : action composite. Elle clone le commit avec le jeton de la tâche, puis lance outils/deployer.py.
  • outils/deployer.py : lit le manifeste et enchaîne construction, préparation du serveur, site de la console (docker exec console-vps python3 /app/server.py site-assurer) et mise en service.
  • outils/vps-deployer.sh : la mise en service elle-même. Arrêt de l'ancien conteneur et chown (crochet AVANT), docker compose up, stabilité pendant 20 s, santé, et retour à l'image (ou à la version statique) précédente en cas d'échec. Il garde 3 images et 5 versions statiques.

Tout s'exécute sur l'exécuteur Forgejo du VPS (étiquette vps-linux), qui a la main sur le serveur. Il est réservé au compte yannicktebi : ne jamais l'ouvrir à d'autres comptes ni à toute l'instance. Une modification de ce dépôt s'applique dès le déploiement suivant de chaque projet, puisque tous référencent @main.

Projets qui l'utilisent

OrbiteGroupe (orbite-api, orbite-desk, orbite-public), MyCVBuilder (mycvbuilder), PiloteEquipe (piloteequipe), sates-gestiondocumentconfidentiel (sates). Leurs manifestes sont de bons exemples.

Image de base suivie

Pour une application conteneur, l'image du dernier FROM du Dockerfile (celle qui tourne en production, étapes nommées suivies, ARG résolus) est récupérée juste avant la construction. Elle est inscrite sur l'image construite : vps.base=<image>@<empreinte>, avec vps.depot=<adresse du dépôt>. La console du VPS compare chaque jour cette empreinte à celle du registre. Si l'éditeur a publié une nouvelle version de la même étiquette (correctifs de sécurité de .NET, Node, Debian…), elle signale qu'il faut redéployer l'application, avec un lien vers le dépôt. Rien n'est à écrire dans le manifeste.

Redéploiement automatique : si le manifeste a des tests, la console relance elle-même le pipeline la nuit (04:30 GMT), une seule fois par nouvelle version de la base. Tests, contrôle de santé et retour automatique s'appliquent comme pour un envoi sur main. Si la base est toujours périmée 20 minutes après, l'alerte le signale. Sans tests, ou avec mise_a_jour_auto: false à la racine du manifeste, rien n'est relancé : l'alerte demande un redéploiement à la main.

Préproduction (demandes de fusion vers main)

Une section preproduction: dans le manifeste suffit. La demande reçoit un commentaire avec l'adresse de chaque application en préproduction (ou l'échec, avec le lien du journal), mis à jour à chaque envoi. Les sites de préproduction sont hors des moteurs de recherche (en-tête X-Robots-Tag: noindex et robots.txt qui interdit tout). Une demande de fusion vers main se déploie alors en préproduction, se met à jour à chaque envoi sur sa branche, et tout est retiré à sa fermeture (fusion ou abandon). Sans cette section, une demande de fusion ne déploie rien.

preproduction:
  environnement:            # variables ajoutées ou remplacées en préproduction (toutes les applications)
    Email__Envoi: "false"   # exemple : couper l'envoi de courriels
  applications:             # réglages propres à une application
    mon-api:
      environnement: {Url__Front: https://dev-exemple.com}
  domaines:                 # facultatif : nom de production -> nom de préproduction
    exemple.com: essai.exemple.com
  remplacer:                # sites statiques : textes remplacés dans les fichiers (js, json, html, css...)
    api.exemple.com: dev-api.exemple.com
  copier_donnees: true      # copie des volumes de production au premier déploiement (défaut : oui)
  copier_env: true          # copie du .env de production, sans la chaîne de la base ni l'image (défaut : oui)
  memoire: 384m             # facultatif

Ce qui se passe :

  • chaque application devient <nom>-dev, sur dev-<nom de domaine> (ou dev.<domaine> pour un domaine nu). Les noms www. sont ignorés ;
  • au premier déploiement, la base PostgreSQL est copiée depuis la production (<base>_dev, compte <compte>_dev, sans couper la production). Les fichiers de configuration du serveur sont copiés, ainsi que les volumes et le .env si les options le permettent ;
  • la console du VPS crée l'enregistrement DNS chez OVH si le nom n'en a aucun, puis sort le site du mode essai dès que le nom pointe sur le VPS ;
  • les applications de préproduction n'ont pas de sonde Uptime Kuma ;
  • à la fermeture de la demande, sont supprimés : le conteneur, le site, l'enregistrement DNS créé par la console, la base et le compte _dev, les images, et les dossiers /srv/apps/<nom>-dev.

Attention : la préproduction utilise une copie des données réelles. Coupe tout effet externe (courriels, paiements, webhooks) par environnement, et vérifie les fichiers de configuration statiques (proteger) qui pointeraient vers la production.

Le workflow modèle (modeles/deployer.yml) contient le déclencheur pull_request ; les projets qui veulent la préproduction doivent reprendre cette version.