La stratégie
Deux approches pour déployer depuis GitHub Actions vers votre VPS : le runner hébergé par GitHub qui pousse en SSH (couvert ici, le plus universel), ou un runner self-hosted installé sur le VPS (évoqué en fin de guide). Dans les deux cas, l'objectif est un déploiement sans interruption grâce à des releases symlinkées.
Prérequis
Votre application tourne déjà une première fois sur le VPS (voir notre guide « Déployer une application Node.js en production »), servie par un utilisateur deploy non-root. Le dépôt est sur GitHub.
Étape 1 — Une clé SSH dédiée au déploiement
Sur votre poste, générez une paire de clés utilisée uniquement par la CI :
ssh-keygen -t ed25519 -f ~/.ssh/github-deploy -C "github-actions"Ajoutez la clé publique sur le VPS :
cat ~/.ssh/github-deploy.pub | ssh deploy@IP-du-VPS "cat >> ~/.ssh/authorized_keys"Dans le dépôt GitHub : Settings → Secrets and variables → Actions → New repository secret. Créez VPS_HOST (l'IP), VPS_USER (deploy), VPS_PORT (22 ou votre port custom) et VPS_SSH_KEY (le contenu complet de la clé privée, lignes BEGIN/END comprises).
Étape 2 — La structure zero-downtime sur le VPS
sudo mkdir -p /var/www/monapp/releases /var/www/monapp/shared
sudo chown -R deploy:deploy /var/www/monappChaque déploiement crée un dossier horodaté dans releases/. Le dossier shared/ contient ce qui survit d'une version à l'autre : le .env, le stockage uploadé. Un symlink current pointe vers la release active — Nginx ou PM2 ne voient jamais de coupure. Placez votre .env dans shared/ dès maintenant.
Étape 3 — Le script de déploiement
Créez /home/deploy/deploy.sh sur le VPS :
#!/usr/bin/env bash
set -euo pipefail
APP_DIR=/var/www/monapp
RELEASE=$(date +%Y%m%d%H%M%S)
mkdir -p "$APP_DIR/releases/$RELEASE"
tar -xzf /tmp/release.tar.gz -C "$APP_DIR/releases/$RELEASE"
ln -sfn "$APP_DIR/shared/.env" "$APP_DIR/releases/$RELEASE/.env"
cd "$APP_DIR/releases/$RELEASE"
npm ci --omit=dev
npm run build
ln -sfn "$APP_DIR/releases/$RELEASE" "$APP_DIR/current"
pm2 reload monapp
cd "$APP_DIR/releases"
ls -1dt */ | tail -n +6 | xargs -r rm -rfLe basculement est atomique : ln -sfn remplace le symlink en une opération. Les cinq dernières releases sont conservées, les autres supprimées. Rendez le script exécutable : chmod +x /home/deploy/deploy.sh.
Étape 4 — Le workflow GitHub Actions
Créez .github/workflows/deploy.yml dans votre dépôt :
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: |
npm ci
npm run build
tar -czf release.tar.gz dist package.json package-lock.json
- name: Upload to VPS
uses: appleboy/[email protected]
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT }}
source: release.tar.gz
target: /tmp/
- name: Deploy
uses: appleboy/[email protected]
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT }}
script: /home/deploy/deploy.shChaque push sur main build l'archive, la transfère et exécute le script. Premier déploiement : surveillez l'onglet Actions, chaque étape est loguée.
Étape 5 — Rollback en dix secondes
Le symlink rend le retour arrière trivial — listez les releases et repointez :
ls /var/www/monapp/releases
ln -sfn /var/www/monapp/releases/20241015120000 /var/www/monapp/current
pm2 reload monappVariante — runner self-hosted
Installez le runner sur le VPS (Settings → Actions → Runners → New self-hosted runner) et le workflow devient un simple git pull && npm ci && pm2 reload exécuté localement : plus rapide, aucun transfert réseau. Réservez cette option aux dépôts privés dont vous êtes le seul contributeur — un runner self-hosted sur un dépôt public peut exécuter du code arbitraire de tiers sur votre serveur.
Sécurité
- La clé de déploiement n'est utilisée nulle part ailleurs ; la révoquer revient à retirer une ligne d'
authorized_keys. - Le
.envvit dansshared/, jamais dans l'archive ni dans le dépôt. - Surveillez les logs du workflow : les secrets GitHub sont masqués, mais un
set -xmal placé peut fuiter des chemins.
Dépannage
- Permission denied (publickey) : le secret
VPS_SSH_KEYdoit contenir la clé privée complète. Testez la clé à la main :ssh -i ~/.ssh/github-deploy deploy@IP-du-VPS. - `pm2: command not found` dans le script : le PATH d'une session SSH non-interactive est minimal — utilisez le chemin complet
$(which pm2)ou exportez PATH en tête de script. - Le site sert encore l'ancienne version : vérifiez
ls -l /var/www/monapp/currentet rechargez l'application, pas seulement Nginx.