Pipeline GitLab CI/CD — déploiement Docker sur VPS via SSH¶
Objectif : déployer une application conteneurisée sur un VPS OVH via GitLab CI/CD, avec staging automatique et production manuelle. Quand l'utiliser : à chaque nouveau projet à déployer sur un VPS depuis GitLab. Template associé :
gitlab-ci-template.yml(à copier en.gitlab-ci.ymlet adapter).
Architecture¶
Push develop ──► lint ─► test ─► build (image :staging) ─► scan ─► deploy staging (AUTO)
Push main ──► lint ─► test ─► build (image :prod) ─► scan ─► deploy prod (MANUEL)
- Les images sont construites par le runner, poussées sur le GitLab Container Registry, taggées par branche + SHA (jamais de mélange staging/prod).
- Le déploiement se fait par SSH vers le VPS : on transfère seulement les fichiers de conf (
docker-compose,nginx) et un script de déploiement, puis on l'exécute côté serveur. Le serveur tire les images depuis le registry — pas de Git sur le VPS.
Prérequis¶
- VPS durci (voir
runbooks/vps/durcissement-vps-ovh.md), port SSH custom, avec : - un utilisateur de déploiement dédié (ex.
deploy), - Docker + docker compose installés,
- un dossier cible (ex.
/opt/<projet>-staging/et/opt/<projet>/), - un script de déploiement (
deploy-staging.sh/deploy-prod.sh) qui fait ledocker login,docker compose pull,up -d, migrations… - Un Deploy Token GitLab (ou les creds du registry) pour que le VPS puisse
docker pull. - Sous-domaines Cloudflare
staging.<domaine>et<domaine>pointant sur le VPS.
Variables CI/CD à configurer¶
GitLab → Settings → CI/CD → Variables :
| Variable | Rôle | Notes |
|---|---|---|
VPS_HOST |
IP / hostname du VPS | |
VPS_PORT |
Port SSH custom | ex. 49155 |
VPS_USER |
Utilisateur de déploiement | ex. deploy |
VPS_SSH_KEY |
Clé privée SSH en base64 | masked — voir piège n°1 |
POSTGRES_PASSWORD / _STAGING |
Secrets DB | prod et staging différents |
SECRET_KEY / _STAGING |
JWT / secrets app | prod et staging différents |
| (autres) | selon l'app | ex. GOOGLE_CLIENT_ID, URLs publiques |
Étapes de mise en place¶
- Copie le template
gitlab-ci-template.yml→.gitlab-ci.ymlà la racine du projet. - Remplace les
<PLACEHOLDER>(domaines, chemins/opt/<projet>, noms d'images) et adapte les jobslint/test/buildà ta stack. - Génère la clé de déploiement et autorise-la côté VPS :
ssh-keygen -t ed25519 -f ~/.ssh/<projet>_deploy -C "gitlab-ci" ssh-copy-id -i ~/.ssh/<projet>_deploy.pub -p <PORT> deploy@<VPS_HOST> base64 -w 0 ~/.ssh/<projet>_deploy > cle_base64.txt # → colle dans VPS_SSH_KEY - Crée les variables CI/CD (tableau ci-dessus).
- Prépare le VPS : dossiers
/opt/<projet>[-staging]/, scriptdeploy-*.shen place, Docker prêt. - Push sur
develop→ la pipeline doit dérouler jusqu'au deploy staging automatiquement. - Merge sur
main→ build/scan auto, puis lance le deploy prod manuellement (bouton play).
Pièges connus (chèrement appris — à ne pas refaire)¶
- Clé SSH en base64, une seule ligne. Coller une clé privée multi-lignes dans une variable GitLab depuis Windows introduit des CRLF → erreur
error in libcrypto. On stockebase64 -w 0de la clé et on la décode dans le job. - Script fichier plutôt que heredoc inline. Un
ssh ... <<SSHnon quoté fait expanser$i,$?,$(...)par le shell local du runner avant l'envoi. Transférer un.shet l'exécuter viassh ... bash script.sh= interprétation une seule fois, côté VPS. - Pas de pipelines
merge_request_event. Ils créent un état transitoire imprévisible (MR encore "ouverte" juste après merge → pipeline push bloqué). On déclenche uniquement sur pushdevelop/mainviaworkflow.rules. - Pas de Git sur le serveur. Seuls conf + script de déploiement sont transférés ; les images viennent du registry. Plus léger, plus sûr.
- Tags d'image par branche.
:$CI_COMMIT_REF_SLUG-$SHAévite qu'un build staging n'écrase une image prod. rsync --exclude nginx/certsen prod pour ne pas écraser les certificats présents sur le serveur.- Secrets staging ≠ prod. Toujours des valeurs distinctes (
_STAGING).
Vérification¶
- [ ] Push
develop→ pipeline verte jusqu'àdeploy:staging, site staging à jour - [ ]
deploy:productionreste manuel (n'a pas démarré tout seul) - [ ] Aucune clé/secret en clair dans le
.gitlab-ci.yml(tout en variables CI/CD) - [ ] Le VPS pull bien les images (Deploy Token valide)
Dépannage¶
error in libcrypto→VPS_SSH_KEYmal encodée : refairebase64 -w 0.Host key verification failed→ssh-keyscan -p $VPS_PORT $VPS_HOSTéchoue (port/IP) ou VPS injoignable.- Variables vides côté serveur / erreurs
$bizarres → tu es repassé sur un heredoc inline (piège n°2). deploy:stagingskippé → vérifieneeds(build/scan) et la règledevelop.
Ressources¶
- Runbook durcissement VPS :
../vps/durcissement-vps-ovh.md - GitLab CI/CD variables : https://docs.gitlab.com/ee/ci/variables/
- GitLab Container Registry : https://docs.gitlab.com/ee/user/packages/container_registry/
- Trivy (scan images) : https://trivy.dev/
Dernière mise à jour : 2026-07-02