guides/serveur/kirby-ci-cd-deploy.md
isUnknown d0a243509d Refonte complète des guides VPS et ajout guides Kirby/CI-CD
- Restructuration VPS : guides rapide et complet séparés
- Nouveau guide Vim essentiel pour administration serveur
- Guide déploiement Kirby (VirtualHost, multi-sites, permissions)
- Guide CI/CD Kirby (GitLab CI, Forgejo Actions, Docker)
- Anonymisation complète (sécurité pour publication publique)
- Priorité aux solutions libres (Forgejo, GitLab)
- README général et navigation améliorée

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 14:58:13 +01:00

23 KiB

CI/CD pour déployer Kirby

Automatiser le déploiement de Kirby CMS avec GitLab CI ou Forgejo Actions (alternatives libres).

🆓 Philosophie : Ce guide privilégie les solutions libres et open-source :

  • Forgejo : Alternative libre à GitHub, auto-hébergeable
  • GitLab : Solution mature avec version Community (libre)
  • Pas de dépendance aux services propriétaires

📋 Table des matières

  1. Concepts de base
  2. GitLab CI (SSH/rsync)
  3. Forgejo Actions (SSH/rsync)
  4. Forgejo Actions (FTP)
  5. Image Docker optimisée
  6. Configuration des secrets
  7. Troubleshooting

Concepts de base

Workflow typique

1. Push sur main/master
   ↓
2. CI/CD se déclenche
   ↓
3. Stage BUILD
   - Installer les dépendances (Composer)
   - Optimiser (autoloader, assets)
   ↓
4. Stage DEPLOY
   - Se connecter au VPS (SSH ou FTP)
   - Synchroniser les fichiers
   - Exclure les dossiers sensibles (cache, sessions, accounts)
   ↓
5. ✅ Site déployé

Prérequis VPS


GitLab CI (SSH/rsync)

Avantages

  • Rapide (rsync ne transfère que les modifications)
  • Sécurisé (SSH)
  • Contrôle total (permissions, exclusions)
  • Logs détaillés

Configuration complète

.gitlab-ci.yml à la racine du projet :

stages:
  - build
  - deploy

variables:
  COMPOSER_ALLOW_SUPERUSER: "1"

# ===== STAGE BUILD =====
build_prod:
  stage: build
  only:
    - main  # Déclencher uniquement sur la branche main
  image: composer:2  # Image officielle Composer
  script:
    # Installer les dépendances Kirby
    - composer install --no-dev --optimize-autoloader --ignore-platform-req=ext-gd
  artifacts:
    # Fichiers à passer au stage deploy
    paths:
      - vendor/
      - kirby/
      - site/
      - assets/
      - content/
      - media/
      - index.php
      - .htaccess
    expire_in: 1 hour  # Les artifacts expirent après 1h

# ===== STAGE DEPLOY =====
deploy_prod:
  stage: deploy
  image: alpine:latest
  only:
    - main
  dependencies:
    - build_prod  # Récupère les artifacts du build
  before_script:
    # Installer rsync et SSH
    - apk add --no-cache rsync openssh

    # Configurer la clé SSH
    - mkdir -p ~/.ssh
    - echo -e "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
    - chmod 600 ~/.ssh/id_ed25519

    # Configuration SSH (désactiver la vérification de l'host)
    - |
      cat > ~/.ssh/config <<EOF
      Host *
          StrictHostKeyChecking no
          UserKnownHostsFile /dev/null
          BatchMode yes
          IdentitiesOnly yes
      EOF
    - chmod 600 ~/.ssh/config

  script:
    - echo "🚀 Déploiement sur le serveur de production..."

    # Fonction rsync réutilisable
    - |
      rsync_deploy() {
        src="$1"
        dst="$2"
        shift 2
        cmd="rsync -az --delete --no-perms --no-owner --no-group"
        for excl in "$@"; do
          cmd="$cmd --exclude=$excl"
        done
        cmd="$cmd -e 'ssh -p ${SSH_PORT:-22} -i ~/.ssh/id_ed25519' $src $USERNAME@$HOST:$dst"
        echo "$cmd"
        eval $cmd
      }

    # Déployer les dossiers buildés
    - rsync_deploy site/ "$PROD_PATH/site/" "accounts/" "cache/" "sessions/"
    - rsync_deploy vendor/ "$PROD_PATH/vendor/"
    - rsync_deploy kirby/ "$PROD_PATH/kirby/"
    - rsync_deploy assets/ "$PROD_PATH/assets/"
    - rsync_deploy content/ "$PROD_PATH/content/"
    - rsync_deploy media/ "$PROD_PATH/media/"
    - rsync_deploy .htaccess "$PROD_PATH/"
    - rsync_deploy index.php "$PROD_PATH/"

    - echo "✅ Déploiement terminé !"

Variables CI/CD à configurer

GitLab → Settings → CI/CD → Variables :

Variable Valeur Protégé Masqué
SSH_PRIVATE_KEY Contenu de ~/.ssh/id_ed25519
HOST 203.0.113.10 (IP du VPS)
USERNAME debian
SSH_PORT 22 (ou autre si modifié)
PROD_PATH /var/www/monsite.com

Obtenir la clé SSH privée :

cat ~/.ssh/id_ed25519
# Copiez TOUT le contenu (-----BEGIN ... -----END)

Forgejo Actions (SSH/rsync)

Avantages

  • Libre : Alternative open-source à GitHub
  • Auto-hébergé : Vos données restent chez vous
  • Compatible : Syntaxe proche de GitHub Actions
  • Rapide : rsync incrémental

Configuration

.forgejo/workflows/deploy.yml :

name: Deploy to VPS

on:
  push:
    branches:
      - main

jobs:
  deploy:
    name: Build and Deploy
    runs-on: docker

    container:
      image: php:8.2-cli  # Image PHP avec Composer

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

      - name: Install Composer
        run: |
          curl -sS https://getcomposer.org/installer | php
          mv composer.phar /usr/local/bin/composer

      - name: Install dependencies
        run: composer install --no-dev --optimize-autoloader

      - name: Deploy to VPS
        env:
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
          HOST: ${{ secrets.HOST }}
          USERNAME: ${{ secrets.USERNAME }}
          PROD_PATH: ${{ secrets.PROD_PATH }}
        run: |
          # Installer rsync et SSH
          apt-get update -qq
          apt-get install -y -qq rsync openssh-client

          # Configurer SSH
          mkdir -p ~/.ssh
          echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519

          cat > ~/.ssh/config <<EOF
          Host *
              StrictHostKeyChecking no
              UserKnownHostsFile /dev/null
              BatchMode yes
              IdentitiesOnly yes
          EOF
          chmod 600 ~/.ssh/config

          # Déployer avec rsync
          echo "🚀 Déploiement..."

          rsync -avz --delete \
            --exclude 'site/accounts/' \
            --exclude 'site/cache/' \
            --exclude 'site/sessions/' \
            --exclude '.git' \
            --exclude '.github' \
            --exclude '.forgejo' \
            --exclude 'node_modules' \
            -e "ssh -i ~/.ssh/id_ed25519" \
            ./ $USERNAME@$HOST:$PROD_PATH/

          echo "✅ Déploiement terminé !"

Secrets à configurer

Dans Forgejo : Settings → Secrets → Actions

Secret Valeur
SSH_PRIVATE_KEY Clé privée SSH complète
HOST IP ou domaine du VPS
USERNAME Utilisateur SSH
PROD_PATH Chemin absolu sur le VPS

💡 Note : Si vous utilisez GitLab, la configuration des secrets est identique (Settings → CI/CD → Variables)


Forgejo Actions (FTP)

Quand utiliser FTP ?

  • Hébergement mutualisé (pas d'accès SSH)
  • Hébergeur impose FTP uniquement
  • Plus lent que SSH/rsync
  • Moins sécurisé (sauf FTPS/SFTP)

Configuration

.forgejo/workflows/deploy-ftp.yml :

name: Deploy via FTP

on:
  push:
    branches:
      - main

jobs:
  deploy:
    name: Deploy to FTP
    runs-on: docker
    container:
      image: forgejo-ci-php:latest  # Image custom (voir ci-dessous)

    steps:
      - name: Checkout code
        run: |
          git clone --depth 1 --branch main \
            https://forge.example.com/${{ github.repository }}.git .

      - name: Install dependencies
        run: composer install --no-dev --optimize-autoloader

      - name: Deploy via FTP
        env:
          FTP_USERNAME: ${{ secrets.FTP_USERNAME }}
          FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
          FTP_HOST: ${{ secrets.FTP_HOST }}
        run: |
          # Installer lftp (client FTP puissant)
          apt-get update -qq && apt-get install -y -qq lftp

          # Script lftp
          cat > /tmp/lftp-script.txt <<SCRIPT
          set ftp:ssl-allow no
          open -u $FTP_USERNAME,$FTP_PASSWORD $FTP_HOST

          # Synchroniser les dossiers (mirror --reverse = upload)
          mirror --reverse --verbose --ignore-time --parallel=10 \
            -x accounts/ -x cache/ -x sessions/ \
            site site

          mirror --reverse --verbose --ignore-time --parallel=10 \
            kirby kirby

          mirror --reverse --verbose --ignore-time --parallel=10 \
            vendor vendor

          mirror --reverse --verbose --ignore-time --parallel=10 \
            assets assets

          mirror --reverse --verbose --ignore-time --parallel=10 \
            content content

          # Upload des fichiers individuels
          put .htaccess
          put index.php

          quit
          SCRIPT

          # Exécuter
          lftp -f /tmp/lftp-script.txt

          echo "✅ Déploiement FTP terminé !"

Secrets FTP

Secret Valeur
FTP_HOST ftp.example.com
FTP_USERNAME Utilisateur FTP
FTP_PASSWORD Mot de passe FTP

Image Docker optimisée pour Kirby

Pourquoi créer une image custom ?

Sans image custom :

Chaque CI installe : Composer + dépendances + rsync/lftp
→ Temps : 2-5 minutes
→ Bande passante gaspillée

Avec image custom :

Image pré-configurée avec tout installé
→ Temps : 30 secondes - 1 minute
→ CI plus rapide et stable

Créer l'image Docker

Dockerfile :

FROM php:8.2-cli

# Métadonnées
LABEL maintainer="votre-email@example.com"
LABEL description="Image PHP optimisée pour CI Kirby CMS"

# Installation des extensions PHP nécessaires pour Kirby
RUN apt-get update && apt-get install -y \
    # Extensions PHP
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    # Outils de déploiement
    rsync \
    openssh-client \
    lftp \
    git \
    unzip \
    # Nettoyage
    && rm -rf /var/lib/apt/lists/*

# Configurer GD (images)
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd zip

# Installer Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
    --install-dir=/usr/local/bin --filename=composer

# Définir Composer en mode superuser (pour CI)
ENV COMPOSER_ALLOW_SUPERUSER=1

# Optimisations Composer
RUN composer global config --no-plugins allow-plugins.composer/installers true

# Version et vérification
RUN php -v && composer --version && rsync --version

WORKDIR /workspace

CMD ["/bin/bash"]

Builder et publier l'image

Option 1 : Forgejo Container Registry (recommandé - auto-hébergé)

# Builder l'image
docker build -t forge.example.com/votre-user/kirby-ci:latest .

# Tester localement
docker run --rm -it forge.example.com/votre-user/kirby-ci:latest bash
composer --version
rsync --version

# Login sur votre registry Forgejo
docker login forge.example.com

# Publier
docker push forge.example.com/votre-user/kirby-ci:latest

Option 2 : GitLab Container Registry

# Builder
docker build -t registry.gitlab.com/votre-user/kirby-ci:latest .

# Login GitLab
docker login registry.gitlab.com

# Publier
docker push registry.gitlab.com/votre-user/kirby-ci:latest

Option 3 : Docker Hub (public)

# Builder
docker build -t votre-user/kirby-ci:latest .

# Login Docker Hub
docker login

# Publier
docker push votre-user/kirby-ci:latest

Utiliser l'image dans la CI

GitLab CI :

build_prod:
  stage: build
  image: registry.gitlab.com/votre-user/kirby-ci:latest  # Votre image custom
  script:
    - composer install --no-dev --optimize-autoloader

Forgejo Actions :

jobs:
  deploy:
    runs-on: docker
    container:
      image: forge.example.com/votre-user/kirby-ci:latest
    steps:
      - name: Install dependencies
        run: composer install --no-dev --optimize-autoloader

Gain de temps :

  • Avant : ~3-5 min (install PHP + Composer + tools)
  • Après : ~30s - 1 min (juste composer install)

Configuration des secrets

Clé SSH pour la CI

1. Générer une clé dédiée à la CI (recommandé) :

# Sur votre machine locale
ssh-keygen -t ed25519 -C "ci-deploy-kirby" -f ~/.ssh/ci_deploy

# Deux fichiers créés :
# ~/.ssh/ci_deploy       (PRIVÉE - pour la CI)
# ~/.ssh/ci_deploy.pub   (PUBLIQUE - pour le VPS)

2. Ajouter la clé publique au VPS :

# Copier la clé publique
cat ~/.ssh/ci_deploy.pub

# Sur le VPS
ssh debian@VPS
vim ~/.ssh/authorized_keys
# Collez la clé publique sur une nouvelle ligne

3. Ajouter la clé privée dans les secrets CI :

# Afficher la clé PRIVÉE
cat ~/.ssh/ci_deploy

# Copiez TOUT (-----BEGIN ... -----END)
# Puis ajoutez dans les secrets CI sous le nom SSH_PRIVATE_KEY

Vérification

Tester la clé depuis votre machine :

ssh -i ~/.ssh/ci_deploy debian@VPS
# Doit fonctionner sans mot de passe

Déploiement multi-environnements

Staging + Production

.gitlab-ci.yml :

stages:
  - build
  - deploy-staging
  - deploy-production

build:
  stage: build
  image: votre-user/kirby-ci:latest
  script:
    - composer install --no-dev --optimize-autoloader
  artifacts:
    paths:
      - vendor/
      - kirby/
      - site/
      - assets/
      - .htaccess
      - index.php

# Déploiement automatique sur staging (toutes les branches)
deploy_staging:
  stage: deploy-staging
  image: alpine:latest
  only:
    - branches
  except:
    - main
  before_script:
    - apk add --no-cache rsync openssh
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
    - chmod 600 ~/.ssh/id_ed25519
  script:
    - rsync -avz --delete \
        --exclude 'site/accounts/' \
        --exclude 'site/cache/' \
        --exclude 'site/sessions/' \
        -e "ssh -i ~/.ssh/id_ed25519" \
        ./ $STAGING_USERNAME@$STAGING_HOST:$STAGING_PATH/

# Déploiement manuel sur production (branche main uniquement)
deploy_production:
  stage: deploy-production
  image: alpine:latest
  only:
    - main
  when: manual  # Nécessite une action manuelle
  before_script:
    - apk add --no-cache rsync openssh
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
    - chmod 600 ~/.ssh/id_ed25519
  script:
    - rsync -avz --delete \
        --exclude 'site/accounts/' \
        --exclude 'site/cache/' \
        --exclude 'site/sessions/' \
        -e "ssh -i ~/.ssh/id_ed25519" \
        ./ $PROD_USERNAME@$PROD_HOST:$PROD_PATH/

Variables supplémentaires :

Variable Staging Production
STAGING_HOST staging.example.com -
STAGING_USERNAME debian -
STAGING_PATH /var/www/staging.example.com -
PROD_HOST - example.com
PROD_USERNAME - debian
PROD_PATH - /var/www/example.com

Optimisations

Cache Composer

GitLab CI :

build_prod:
  stage: build
  image: composer:2
  cache:
    key: composer-cache
    paths:
      - vendor/
  script:
    - composer install --no-dev --optimize-autoloader

Forgejo Actions :

- name: Cache Composer
  uses: actions/cache@v4
  with:
    path: vendor
    key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}

- name: Install dependencies
  run: composer install --no-dev --optimize-autoloader

💡 Note : Forgejo Actions supporte la syntaxe GitHub Actions (compatibilité)

Déploiement incrémental (rsync optimisé)

# Ne synchroniser que les fichiers modifiés
rsync -avz --delete --checksum \
  --exclude 'site/accounts/' \
  --exclude 'site/cache/' \
  --exclude 'site/sessions/' \
  --exclude 'media/' \
  -e "ssh -i ~/.ssh/id_ed25519" \
  ./ $USERNAME@$HOST:$PROD_PATH/

Options rsync importantes :

Option Description
-a Mode archive (préserve tout)
-v Verbeux (affiche les fichiers)
-z Compression pendant le transfert
--delete Supprime les fichiers distants en trop
--checksum Compare par checksum (plus précis)
--dry-run Simulation (ne fait rien)

Rollback rapide

Script de rollback :

rollback:
  stage: deploy
  when: manual
  script:
    - ssh $USERNAME@$HOST "cd $PROD_PATH && git reset --hard HEAD~1"

Notifications

Slack

GitLab CI :

after_script:
  - |
    curl -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"✅ Déploiement de $CI_PROJECT_NAME réussi !\"}" \
    $SLACK_WEBHOOK_URL

Variables :

  • SLACK_WEBHOOK_URL : URL du webhook Slack

Discord

after_script:
  - |
    curl -X POST -H 'Content-Type: application/json' \
    -d "{\"content\":\"✅ **$CI_PROJECT_NAME** déployé sur production !\"}" \
    $DISCORD_WEBHOOK_URL

Troubleshooting

Erreur "Permission denied (publickey)"

Cause : La clé SSH n'est pas reconnue par le VPS.

Solutions :

  1. Vérifier que la clé publique est bien dans ~/.ssh/authorized_keys sur le VPS
  2. Vérifier les permissions :
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys
    
  3. Vérifier le format de la clé privée dans les secrets (doit inclure -----BEGIN et -----END)

Erreur "Host key verification failed"

Cause : Le serveur SSH demande une confirmation.

Solution : Désactiver la vérification dans la config SSH :

before_script:
  - |
    cat > ~/.ssh/config <<EOF
    Host *
        StrictHostKeyChecking no
        UserKnownHostsFile /dev/null
    EOF

Composer install échoue

Erreur : ext-gd is missing

Solution 1 : Ignorer les requirements de plateforme :

composer install --no-dev --optimize-autoloader --ignore-platform-req=ext-gd

Solution 2 : Utiliser une image avec GD pré-installé (image custom)

rsync trop lent

Optimisations :

# Paralléliser avec --partial-dir
rsync -avz --delete --partial-dir=.rsync-partial \
  --exclude 'media/' \
  ./ $USERNAME@$HOST:$PROD_PATH/

# Ou exclure les gros dossiers (uploads manuels)
rsync -avz --delete \
  --exclude 'media/' \
  --exclude 'assets/tiles/' \
  ./ $USERNAME@$HOST:$PROD_PATH/

FTP trop lent

Optimisation lftp :

mirror --reverse --verbose --parallel=20 \
  --exclude-glob cache/ \
  --exclude-glob sessions/ \
  site site

Augmenter --parallel (jusqu'à 20-30 connexions simultanées)


Comparaison des méthodes

Critère SSH/rsync FTP/lftp
Vitesse Très rapide Moyen
Sécurité SSH chiffré FTP non chiffré (FTPS mieux)
Fiabilité Très stable Dépend du serveur FTP
Compatibilité VPS/dédié Tous hébergements
Facilité Simple avec clés SSH Très simple
Bande passante Optimisée (delta) Retransfère tout

Recommandation :

  • VPS/Serveur dédié → SSH/rsync (meilleure option)
  • Hébergement mutualisé → FTP/lftp (seule option souvent)

Exemple complet GitLab CI (production-ready)

.gitlab-ci.yml :

stages:
  - build
  - test
  - deploy

variables:
  COMPOSER_ALLOW_SUPERUSER: "1"

# ===== BUILD =====
build:
  stage: build
  image: votre-user/kirby-ci:latest
  cache:
    key: composer-$CI_COMMIT_REF_SLUG
    paths:
      - vendor/
  script:
    - composer install --no-dev --optimize-autoloader
  artifacts:
    paths:
      - vendor/
      - kirby/
      - site/
      - assets/
      - content/
      - index.php
      - .htaccess
    expire_in: 1 hour

# ===== TEST =====
test:
  stage: test
  image: votre-user/kirby-ci:latest
  dependencies:
    - build
  script:
    - echo "🧪 Vérification de la structure Kirby..."
    - test -d kirby || exit 1
    - test -f index.php || exit 1
    - test -f .htaccess || exit 1
    - echo "✅ Structure OK"

# ===== DEPLOY PRODUCTION =====
deploy_production:
  stage: deploy
  image: alpine:latest
  only:
    - main
  when: manual  # Déploiement manuel sur prod
  dependencies:
    - build
  before_script:
    - apk add --no-cache rsync openssh
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
    - chmod 600 ~/.ssh/id_ed25519
    - |
      cat > ~/.ssh/config <<EOF
      Host *
          StrictHostKeyChecking no
          UserKnownHostsFile /dev/null
          BatchMode yes
          IdentitiesOnly yes
      EOF
    - chmod 600 ~/.ssh/config
  script:
    - echo "🚀 Déploiement sur $PROD_HOST..."
    - |
      rsync -avz --delete --checksum \
        --exclude 'site/accounts/' \
        --exclude 'site/cache/' \
        --exclude 'site/sessions/' \
        --exclude '.git' \
        --exclude '.gitlab-ci.yml' \
        -e "ssh -p ${SSH_PORT:-22} -i ~/.ssh/id_ed25519" \
        ./ $USERNAME@$PROD_HOST:$PROD_PATH/
    - echo "✅ Déploiement terminé !"
  after_script:
    - |
      curl -X POST -H 'Content-type: application/json' \
      --data "{\"text\":\"✅ $CI_PROJECT_NAME déployé en production\"}" \
      $SLACK_WEBHOOK_URL || true

Checklist déploiement CI/CD

Avant de mettre en place la CI/CD :

  • VPS configuré et accessible en SSH
  • Clé SSH dédiée générée pour la CI
  • Clé publique ajoutée sur le VPS (~/.ssh/authorized_keys)
  • Connexion SSH testée depuis votre machine
  • Secrets CI configurés (SSH_PRIVATE_KEY, HOST, USERNAME, PROD_PATH)
  • Fichier .gitlab-ci.yml ou .forgejo/workflows/deploy.yml créé
  • Test de déploiement sur branche de test
  • Vérification du site après déploiement
  • Notifications configurées (optionnel)
  • Documentation de rollback prête

Pour aller plus loin

Docker Compose pour tests locaux

docker-compose.yml :

version: '3.8'

services:
  kirby:
    build: .
    volumes:
      - ./:/workspace
    working_dir: /workspace
    command: php -S 0.0.0.0:8000
    ports:
      - "8000:8000"
# Tester localement
docker-compose up
# Ouvrir http://localhost:8000

Tests automatisés

test:
  stage: test
  image: votre-user/kirby-ci:latest
  script:
    - composer require --dev phpunit/phpunit
    - vendor/bin/phpunit tests/

Monitoring post-déploiement

after_script:
  # Vérifier que le site répond
  - |
    STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://example.com)
    if [ "$STATUS" != "200" ]; then
      echo "❌ Le site ne répond pas (HTTP $STATUS)"
      exit 1
    fi
    echo "✅ Site accessible (HTTP 200)"

Ressources

Docker :

CI/CD (solutions libres) :

Déploiement :


Temps de mise en place : 1-2h pour la première configuration, puis déploiements en 2-5 minutes ! 🚀