Guides et tutoriels

CI/CD : déployer sur son VPS avec GitHub Actions

Publié le 15 octobre 2024 · 13 min de lecture

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/monapp

Chaque 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 -rf

Le 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.sh

Chaque 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 monapp

Variante — 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 .env vit dans shared/, jamais dans l'archive ni dans le dépôt.
  • Surveillez les logs du workflow : les secrets GitHub sont masqués, mais un set -x mal placé peut fuiter des chemins.

Dépannage

  • Permission denied (publickey) : le secret VPS_SSH_KEY doit 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/current et rechargez l'application, pas seulement Nginx.

Des tutoriels pas-à-pas rédigés par nos ingénieurs, testés sur notre infrastructure.

GLOBALCLOUDHOSTING →