En 2026, GitHub Actions est le pipeline CI/CD utilisé par plus de 60% des développeurs professionnels. Selon les derniers chiffres de GitHub, son marketplace liste désormais plus de 22 000 actions réutilisables, et les entreprises qui ont migré vers GitHub Actions rapportent une réduction moyenne de 32% de leur temps de cycle de déploiement. Ce guide complet vous montre comment construire, tester et déployer automatiquement une application Dockerisée sur un VPS Linux — zéro intervention manuelle, zéro stress le vendredi soir.

Ce tutoriel est conçu pour être suivi de A à Z en 45 minutes. À la fin, vous aurez un pipeline fonctionnel qui se déclenche à chaque push sur main, construit votre image Docker, la pousse sur GitHub Container Registry, puis déploie automatiquement sur votre serveur via SSH.

Prérequis et architecture de la pipeline

Avant de commencer, voici ce dont vous avez besoin :

  • Un dépôt GitHub avec votre application (Node.js, Python, PHP — peu importe)
  • Un VPS Linux avec Docker et Docker Compose installés (Ubuntu 24.04 recommandé)
  • Accès SSH à votre serveur avec une clé SSH Ed25519
  • Un Dockerfile à la racine de votre projet

L’architecture que nous allons mettre en place suit trois étapes distinctes :

  1. CI (Intégration Continue) : à chaque push, les tests automatiques s’exécutent
  2. Build : si les tests passent, l’image Docker est construite et poussée sur GHCR
  3. CD (Déploiement Continu) : le serveur pull la nouvelle image et redémarre le container

Structure des fichiers GitHub Actions

GitHub Actions lit les workflows depuis le dossier .github/workflows/. Nous allons créer deux fichiers distincts pour séparer les responsabilités : un fichier CI pour les tests, un fichier CD pour le déploiement.

# Structure de votre dépôt
mon-projet/
├── .github/
│   └── workflows/
│       ├── ci.yml          # Tests + build image
│       └── deploy.yml      # Déploiement sur VPS
├── Dockerfile
├── docker-compose.yml
└── src/
    └── ...

# Pour créer les dossiers si nécessaires :
mkdir -p .github/workflows

Workflow CI : tests automatiques et build Docker

Le fichier .github/workflows/ci.yml se déclenche à chaque pull request et à chaque push sur main. Il exécute vos tests, puis construit et pousse l’image Docker sur GitHub Container Registry (GHCR) — gratuit pour les dépôts publics.

name: CI — Tests et Build Docker

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout du code
        uses: actions/checkout@v4

      - name: Configurer Node.js 22
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Installer les dépendances
        run: npm ci

      - name: Lancer les tests
        run: npm test

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'

    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout du code
        uses: actions/checkout@v4

      - name: Se connecter à GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extraire les métadonnées Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=sha-
            type=raw,value=latest

      - name: Activer le cache Docker (Buildx)
        uses: docker/setup-buildx-action@v3

      - name: Construire et pousser l'image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Notez l’utilisation du cache GitHub Actions (type=gha) : il réduit de 40 à 70% le temps de build après le premier run en réutilisant les couches Docker déjà calculées.

Configurer les secrets GitHub

Avant d’aller plus loin, vous devez configurer les secrets de votre dépôt. Allez dans Settings → Secrets and variables → Actions → New repository secret et créez les variables suivantes :

# Générer une clé SSH dédiée au déploiement (sur votre machine locale)
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key -N ""

# Afficher la clé PRIVÉE (à copier dans GitHub Secrets)
cat ~/.ssh/deploy_key
# → Copier dans : SSH_PRIVATE_KEY

# Afficher la clé PUBLIQUE (à ajouter sur votre serveur)
cat ~/.ssh/deploy_key.pub
# → Ajouter dans : /home/deploy/.ssh/authorized_keys sur le VPS

# Les autres secrets à créer dans GitHub :
# SERVER_HOST     → IP ou domaine de votre VPS (ex: 185.12.34.56)
# SERVER_USER     → utilisateur SSH (ex: deploy)
# SERVER_PORT     → port SSH (ex: 22 ou 2222 si personnalisé)

Bonne pratique : créez un utilisateur dédié deploy sur votre serveur avec des droits minimaux (accès Docker uniquement), plutôt que d’utiliser root.

Workflow CD : déploiement automatique sur VPS

Le fichier .github/workflows/deploy.yml se déclenche après un build réussi sur main. Il se connecte à votre VPS via SSH et lance la mise à jour du container.

name: CD — Déploiement sur VPS

on:
  workflow_run:
    workflows: ["CI — Tests et Build Docker"]
    branches: [ main ]
    types:
      - completed

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}

    steps:
      - name: Déployer sur le VPS via SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          port: ${{ secrets.SERVER_PORT }}
          script: |
            # Se connecter à GHCR depuis le serveur
            echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io               -u ${{ github.actor }} --password-stdin

            # Aller dans le dossier du projet
            cd /home/deploy/mon-projet

            # Mettre à jour le docker-compose.yml si besoin
            # (optionnel si vous le gérez via Git sur le serveur)

            # Arrêter et supprimer l'ancien container
            docker compose down --remove-orphans

            # Tirer la nouvelle image
            docker compose pull

            # Démarrer avec la nouvelle image
            docker compose up -d --remove-orphans

            # Nettoyer les anciennes images inutilisées
            docker image prune -f

            echo "Déploiement terminé : $(date)"

Le Dockerfile optimisé pour la production

Un Dockerfile bien écrit est la fondation de tout ce pipeline. Voici un exemple optimisé pour une application Node.js, utilisant le multi-stage build pour minimiser la taille de l’image finale.

# Étape 1 : construction
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# Étape 2 : image finale (sans les outils de build)
FROM node:22-alpine AS production
WORKDIR /app

# Utilisateur non-root pour la sécurité
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

COPY --from=builder /app/node_modules ./node_modules
COPY --chown=appuser:appgroup . .

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --retries=3   CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "src/index.js"]

Ce Dockerfile utilise Alpine Linux (image ~5 MB vs ~200 MB pour Ubuntu) et un utilisateur non-root : deux bonnes pratiques incontournables en 2026 pour réduire la surface d’attaque et la taille de l’image.

Stratégie de rollback en cas d’échec

Tout pipeline de production digne de ce nom doit pouvoir revenir en arrière rapidement. Avec GitHub Container Registry, chaque image est taguée avec le SHA du commit (sha-abc1234), ce qui permet un rollback en une commande.

# Sur votre serveur, lister les images disponibles
docker images ghcr.io/votre-org/votre-projet

# Rollback vers un commit spécifique
# 1. Identifier le SHA du dernier commit stable (depuis GitHub Actions)
ROLLBACK_SHA="sha-abc1234"

# 2. Mettre à jour le docker-compose.yml pour pointer vers cette image
sed -i "s|image:.*|image: ghcr.io/votre-org/votre-projet:${ROLLBACK_SHA}|" docker-compose.yml

# 3. Redémarrer avec l'ancienne image
docker compose up -d --no-build

# 4. Vérifier
docker compose ps
docker compose logs --tail=50

Pour automatiser le rollback, vous pouvez ajouter un step dans le workflow CD qui exécute le rollback si le healthcheck échoue après le déploiement.

Optimisations avancées : matrices de tests et environnements parallèles

Si votre projet doit fonctionner sur plusieurs versions de Node.js ou Python, les matrices de tests GitHub Actions permettent d’exécuter les tests en parallèle sur toutes les versions simultanément.

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
      fail-fast: false  # Continuer même si une version échoue

    steps:
      - uses: actions/checkout@v4
      - name: Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci && npm test

Avec fail-fast: false, vous voyez quelles versions précises posent problème plutôt que d’obtenir un échec générique dès la première erreur.

Monitoring de la pipeline : alertes et métriques

Un bon pipeline est un pipeline observé. Voici comment configurer des notifications Slack ou Discord pour être alerté immédiatement en cas d’échec de déploiement.

# À ajouter à la fin de votre workflow deploy.yml
      - name: Notifier en cas d'échec
        if: failure()
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          text: |
            Déploiement ÉCHOUÉ sur ${{ github.repository }}
            Commit: ${{ github.sha }}
            Auteur: ${{ github.actor }}
            Voir les logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

      - name: Notifier le succès
        if: success()
        uses: 8398a7/action-slack@v3
        with:
          status: success
          text: "Déploiement réussi sur ${{ github.repository }} ✅"
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Résultats mesurables et retour sur investissement

Ce pipeline n’est pas qu’une curiosité technique — il a des impacts directs et mesurables sur votre productivité. Voici les gains moyens observés en 2026 selon les équipes ayant adopté ce setup :

  • -32% sur le temps de cycle de déploiement (source : GitHub State of the Octoverse 2026)
  • Zéro déploiement manuel une fois le pipeline en place — plus de « je déploie vendredi à 17h »
  • 3× moins de régressions grâce aux tests systématiques avant chaque merge
  • Rollback en 2 minutes vs 20-30 minutes avec des déploiements manuels
  • Cache Docker : -65% sur le temps de build après le premier run

La mise en place prend 45 minutes. Le retour est immédiat. Et une fois configuré, ce pipeline travaille pour vous 24h/24, week-ends inclus.

Sources :

W
WP Admin Lab

Architecte web full-stack. WordPress, performance, data et sécurité. Notes de terrain, tests reproductibles et retours d'expérience.