Symfony12 min

Deployer une application Symfony : checklist 2026

Par Pierre-Arthur Demengel
SymfonyDeploiementProductionDevOps

Le deploiement est le moment de verite. Votre code Symfony fonctionne en local, les tests passent, le client a valide - mais tout cela ne vaut rien si la mise en production est approximative. Voici une checklist methodique pour deployer Symfony en 2026, que vous utilisiez Deployer PHP, Docker ou un pipeline CI/CD.

Pre-deploiement : la checklist avant de lancer

Avant toute mise en production, verifiez systematiquement ces points. J'ai vu des deploiements echouer pour chacun d'entre eux, souvent un vendredi apres-midi :

  • Variables d'environnement - toutes les variables de .env sont definies sur le serveur (APP_ENV=prod, APP_SECRET, DATABASE_URL, MAILER_DSN)
  • APP_DEBUG=0 - jamais de debug en production, meme "juste pour tester"
  • Dependances a jour - composer install --no-dev --optimize-autoloader
  • Migrations testees - executez les migrations sur un dump de la base de production d'abord
  • Cache warmup - php bin/console cache:clear --env=prod suivi de cache:warmup
  • Assets compiles - si vous utilisez Webpack Encore ou AssetMapper, les assets doivent etre builds
  • Tests verts - idealement automatises dans la CI, pas lances a la main
# Script de pre-deploiement - a automatiser
#!/bin/bash
set -e

echo "=== Pre-deployment checks ==="

# Verifier que les variables critiques sont definies
for var in APP_ENV APP_SECRET DATABASE_URL; do
    if [ -z "${!var}" ]; then
        echo "ERREUR: $var n'est pas defini"
        exit 1
    fi
done

# Verifier que APP_ENV est bien 'prod'
if [ "$APP_ENV" != "prod" ]; then
    echo "ATTENTION: APP_ENV=$APP_ENV (devrait etre 'prod')"
    exit 1
fi

echo "Variables d'environnement OK"

Deployer PHP : l'outil de reference

Deployer PHP est l'outil de deploiement le plus utilise dans l'ecosysteme Symfony. Il gere les releases atomiques, le zero-downtime et le rollback nativement. Voici une configuration complete :

// deploy.php
namespace Deployer;

require 'recipe/symfony.php';

set('application', 'mon-app-symfony');
set('repository', 'git@github.com:user/mon-app.git');
set('php_version', '8.3');

// Garder 5 releases pour le rollback
set('keep_releases', 5);

// Repertoires partages entre les releases
add('shared_dirs', ['var/log']);
add('shared_files', ['.env.local']);

// Repertoires avec permissions d'ecriture
add('writable_dirs', ['var/cache', 'var/log']);

host('production')
    ->set('remote_user', 'deploy')
    ->set('hostname', 'mon-serveur.example.com')
    ->set('deploy_path', '/var/www/mon-app');

// Taches personnalisees
task('deploy:build_assets', function () {
    cd('{{release_path}}');
    run('php bin/console asset-map:compile');
});

after('deploy:vendors', 'deploy:build_assets');

// Verifier la sante apres deploiement
task('deploy:healthcheck', function () {
    $response = run('curl -s -o /dev/null -w "%{http_code}" http://localhost/health');
    if ($response !== '200') {
        throw new \RuntimeException("Health check echoue: HTTP $response");
    }
    writeln('Health check OK');
});

after('deploy:symlink', 'deploy:healthcheck');

Deployer cree une structure de repertoires comme celle-ci :

/var/www/mon-app/
    current -> releases/3     # Lien symbolique vers la release active
    releases/
        1/                     # Release precedente
        2/                     # Release precedente
        3/                     # Release courante
    shared/
        var/log/               # Logs partages
        .env.local             # Configuration locale

Le current est un lien symbolique. Quand Deployer bascule ce lien de releases/2 a releases/3, c'est atomique du point de vue du systeme de fichiers. Les requetes en cours finissent sur l'ancienne release, les nouvelles arrivent sur la nouvelle.

Pipeline CI/CD avec GitHub Actions

Un pipeline complet qui teste, build et deploie automatiquement :

# .github/workflows/deploy.yml
name: Test & Deploy
on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_DB: app_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          extensions: intl, pdo_pgsql
          coverage: xdebug

      - run: composer install --prefer-dist --no-progress
      - run: php bin/phpunit

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4

      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'

      - run: composer install --prefer-dist --no-progress

      - name: Deploy via Deployer
        run: vendor/bin/dep deploy production -v
        env:
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}

Le job test s'execute en premier. Si les tests echouent, le deploiement ne se lance pas. Simple, efficace, pas de mauvaise surprise.

Zero-downtime : les pieges a eviter

Le zero-downtime deploy ne se limite pas a basculer un lien symbolique. Plusieurs elements peuvent casser la continuite :

  • Sessions en fichier - si les sessions sont stockees dans var/sessions, les utilisateurs perdent leur session a chaque deploiement. Utilisez Redis ou la base de donnees
  • Migrations destructives - un ALTER TABLE DROP COLUMN casse l'ancienne release qui tente encore de lire cette colonne. Deployez en deux etapes : d'abord le code qui n'utilise plus la colonne, puis la migration qui la supprime
  • Cache non partage - si le warmup genere un cache dans le repertoire de la release, l'ancienne release garde son ancien cache - c'est le comportement attendu
  • Cron jobs - si un cron tourne sur l'ancienne release pendant le deploiement, il peut echouer. Utilisez des chemins absolus vers current/

Migrations Doctrine en production

Les migrations meritent une attention particuliere. Voici les regles que j'applique :

# Generer la migration
php bin/console make:migration

# Verifier le SQL genere AVANT de l'executer
php bin/console doctrine:migrations:migrate --dry-run

# Executer en production (non-interactif)
php bin/console doctrine:migrations:migrate --no-interaction

# En cas de probleme : rollback
php bin/console doctrine:migrations:migrate prev

Ecrivez toujours la methode down() de vos migrations, meme si vous pensez ne jamais en avoir besoin. Le jour ou un deploiement echoue et que vous devez rollback, vous serez content de l'avoir fait.

Post-deploiement : les verifications

Apres chaque deploiement, verifiez systematiquement :

  • Health check - l'endpoint /health retourne 200
  • Logs - pas d'erreurs critiques dans var/log/prod.log
  • Fonctionnalites cles - testez les parcours critiques (inscription, commande, paiement)
  • Performances - le temps de reponse est-il dans les normes ?
  • Monitoring - les metriques Sentry, Datadog ou Blackfire ne montrent pas d'anomalies

Strategie de rollback

Le rollback doit etre plus rapide que la correction. Avec Deployer :

# Rollback immediat vers la release precedente
dep rollback production

# Lister les releases disponibles
dep releases production

En moins de 5 secondes, votre application est revenue a l'etat precedent. C'est pourquoi Deployer garde les anciennes releases (keep_releases: 5 par defaut).

Pour les migrations de base de donnees, le rollback est plus complexe. Si la migration est incompatible avec l'ancien code, vous etes bloque. La solution : ne jamais deployer de migrations destructives en meme temps que le code qui en depend.

Monitoring post-deploiement

Un deploiement reussi ne veut pas dire que tout va bien. Mettez en place un monitoring minimum :

  • Sentry - capture les exceptions PHP en temps reel via le bundle sentry/sentry-symfony
  • Uptime monitoring - un service externe (UptimeRobot, Pingdom) verifie que le site repond
  • Log aggregation - centralisez les logs avec Monolog + un handler Graylog ou Elasticsearch

Aller plus loin

Si vous conteneurisez votre application, consultez l'article Symfony dans Docker : Dockerfile production-ready pour la partie build d'image. Pour les commandes Symfony utiles en deploiement, la cheatsheet CLI Symfony est un bon aide-memoire. Et pour optimiser les performances apres deploiement, voyez le guide sur le cache Symfony PSR-6/PSR-16.

Vous planifiez un deploiement Symfony et souhaitez un accompagnement ? Decouvrez mes services et mes tarifs, puis contactez-moi pour discuter de votre projet. Je travaille en tant que freelance entre la France et la Belgique sur des projets Symfony de toutes tailles.

Questions fréquentes

13 projets livrésGrand-Est & BelgiqueLighthouse >90Disponible immédiatement

Un projet en tête ?

Discutons de votre site web. Réponse garantie sous 24h.

Ou appelez directement :06 95 41 30 25

WhatsApp
Appeler