---
title: Déploiement continu
description: Publier une image puis déployer une version par clé SSH.
---

Le workflow de livraison s'exécute après la CI, idéalement sur un tag de version. Il publie d'abord l'image, puis déclenche une opération étroite et contrôlée sur le serveur.

## Préparer une commande de déploiement côté serveur

Le serveur peut exposer un script `/usr/local/sbin/deploy-app`, appartenant à `root`, qui n'accepte qu'un tag SemVer :

```bash
#!/usr/bin/env bash
set -Eeuo pipefail

tag="${1:-}"
if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  echo "Tag invalide" >&2
  exit 2
fi

cd /srv/mon-app
printf 'IMAGE_TAG=%s\n' "$tag" > .env.release

docker compose --env-file .env.release -f docker-compose.prod.yml pull
docker compose --env-file .env.release -f docker-compose.prod.yml \
  up -d --no-build --remove-orphans
docker compose --env-file .env.release -f docker-compose.prod.yml ps
```

Configure ensuite, avec `visudo`, une règle limitée à ce script pour le compte technique. Le workflow n'a ainsi pas besoin d'un accès Docker général ni d'un mot de passe SSH.

## Publier dans GHCR

```yaml
name: Release

on:
  push:
    tags: ["v*.*.*"]

permissions:
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v6
      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/metadata-action@v5
        id: meta
        with:
          images: ghcr.io/${{ github.repository }}-backend
          tags: type=ref,event=tag

      - uses: docker/build-push-action@v6
        with:
          context: ./backend
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
```

GitHub recommande d'épingler les actions à un SHA de commit pour une chaîne d'approvisionnement plus stricte. Les tags majeurs ci-dessus gardent l'exemple lisible ; remplace-les par les SHA validés par ton équipe en production.

## Déployer avec une clé

Ajoute un second job au même workflow :

```yaml
  deploy:
    runs-on: ubuntu-latest
    needs: publish
    permissions:
      contents: read
    environment:
      name: production
      url: https://app.example.com
    concurrency:
      group: production
      cancel-in-progress: false

    steps:
      - name: Préparer SSH
        env:
          SSH_PRIVATE_KEY: ${{ secrets.PRODUCTION_SSH_PRIVATE_KEY }}
          SSH_KNOWN_HOSTS: ${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }}
        run: |
          install -m 700 -d ~/.ssh
          printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          printf '%s\n' "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts

      - name: Déployer la version
        env:
          DEPLOY_HOST: ${{ vars.PRODUCTION_HOST }}
          DEPLOY_USER: ${{ vars.PRODUCTION_USER }}
          IMAGE_TAG: ${{ github.ref_name }}
        run: |
          [[ "$IMAGE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
          ssh "$DEPLOY_USER@$DEPLOY_HOST" \
            "sudo /usr/local/sbin/deploy-app $IMAGE_TAG"
```

## Secrets et variables attendus

| Nom | Type | Contenu |
| --- | --- | --- |
| `PRODUCTION_SSH_PRIVATE_KEY` | secret d'environnement | clé privée dédiée au déploiement |
| `PRODUCTION_SSH_KNOWN_HOSTS` | secret d'environnement | empreinte SSH vérifiée du serveur |
| `PRODUCTION_HOST` | variable d'environnement | nom DNS ou adresse du serveur |
| `PRODUCTION_USER` | variable d'environnement | compte technique autorisé |

La clé doit être dédiée, révocable et limitée à la production. Stocke-la dans l'environnement GitHub `production`, qui peut aussi exiger une approbation et limiter les tags autorisés.

<Callout title="Pas de mot de passe SSH dans la CI" type="warn">
  Utilise une clé dédiée et vérifie l'empreinte de l'hôte. Ne remplace pas `known_hosts` par un `ssh-keyscan` aveugle au moment du déploiement.
</Callout>

Pour aller plus loin : [publier des images Docker](https://docs.github.com/en/actions/tutorials/publish-packages/publish-docker-images) et [protéger les environnements](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments).
