Guide de déploiement

Déployer Symfony sur VPS : guide complet 2024

Déployer sur un VPS Cloud →

Tutoriel

Développement

Déployer Symfony sur VPS : guide complet 2024

Un VPS vous donne le contrôle total que Symfony mérite : PHP 8.3, extensions natives, workers asynchrones et accès root complet. Ce guide couvre l'installation de PHP 8.3, le déploiement automatisé avec Deployer PHP, la configuration nginx et Supervisor pour les queues, ainsi que le durcissement de sécurité. En fin d'article, découvrez comment le Marketplace ServOrbit automatise tout ce socle en quelques clics.

9 min de lecture16 étapes
Sommaire· Pourquoi Symfony sur VPS plutôt que sur mutualisé ?1/10
  1. 01Pourquoi Symfony sur VPS plutôt que sur mutualisé ?
  2. 02Ce que le VPS débloque pour Symfony
  3. 03Prérequis chiffrés — ressources et logiciels
  4. 04Installer PHP 8.3 et Symfony CLI sur VPS Ubuntu
  5. 05Déployer une app Symfony en production avec Deployer PHP
  6. 06Configuration nginx pour Symfony
  7. 07Variables d'environnement et sécurité
  8. 08Queues asynchrones avec Symfony Messenger et Supervisor
  9. 09Dépannage — les 4 erreurs les plus fréquentes
  10. 10Déployer depuis le Marketplace ServOrbit

Pourquoi Symfony sur VPS plutôt que sur mutualisé ?

Un hébergement mutualisé impose la version PHP du serveur, désactive les extensions jugées « dangereuses » et interdit l'installation de processus persistants. Symfony 7.4 LTS requiert PHP 8.2 au minimum et Symfony 8 PHP 8.4 — des versions que de nombreux mutualisés ne proposent pas encore. Sur VPS, vous choisissez PHP 8.3 dès le premier jour, activez OPcache avec une configuration fine, et installez ext-redis, ext-intl ou ext-amqp selon vos besoins.

Les workers asynchrones (Symfony Messenger) exigent un processus qui tourne en fond, géré par Supervisor ou systemd. Sur mutualisé, ce processus est tout simplement impossible. Sur VPS, il démarre en une commande et redémarre automatiquement après un crash. Pour une application Symfony qui traite des emails transactionnels, des notifications push ou de l'import de fichiers, c'est une différence non négociable.

Ce que le VPS débloque pour Symfony

  • Accès root complet — installez n'importe quelle extension PHP, configurez OPcache à la ligne près et modifiez php.ini sans ticket support
  • PHP 8.3 natif — profitez des typed class constants, et de l'attribut #[Override] sans attendre la migration du serveur partagé
  • Supervisor et workers persistants — faites tourner messenger:consume en continu, avec redémarrage automatique et rotation des logs, pour traiter vos queues sans cron hacké
  • Redis en local — installez Redis sur le même VPS pour les sessions, le cache Doctrine et les transports Messenger, éliminant ainsi la latence réseau vers un service externe
  • Multi-version PHP via ondrej/php — maintenez PHP 8.1 pour une app legacy et PHP 8.3 pour la nouvelle, sur le même hôte, avec des pools FPM séparés
  • Xdebug et profiling en staging — activez Blackfire ou Xdebug sur l'environnement de recette sans impacter la prod, dans le même datacenter
  • Déploiements atomiques avec Deployer — zéro downtime grâce aux symlinks, rollback en une commande, hooks dep deploy intégrés dans votre pipeline CI/CD

Prérequis chiffrés — ressources et logiciels

Système d'exploitation : Ubuntu 24.04 LTS ou Debian 12. Ces deux distributions bénéficient du dépôt PHP d'Ondřej Surý (PPA ondrej/php sur Ubuntu, packages.sury.org sur Debian) et d'un support long terme compatible avec les cycles de maintenance de Symfony.

PHP : version 8.3 recommandée pour Symfony 7.4 LTS (minimum 8.2). Symfony 8 exige PHP 8.4 : remplacez alors 8.3 par 8.4 dans les noms de paquets. Extensions requises : php8.3-cli, php8.3-fpm, php8.3-mbstring, php8.3-xml, php8.3-curl, php8.3-zip, php8.3-intl, php8.3-opcache. Ajoutez php8.3-redis si vous utilisez Redis, php8.3-pgsql pour PostgreSQL.

Composer : version 2.x obligatoire. Packagist.org ne sert plus les métadonnées de paquets à Composer 1.x.

Base de données : MySQL 8.0+ ou PostgreSQL 16+. Pour une app Symfony standard, 1 Go de RAM suffit généralement pour MySQL comme pour PostgreSQL.

Nginx : 1.24+ depuis les dépôts officiels Ubuntu ou nginx.org.

RAM minimale :
- 1 Go : développement ou app légère sans queues
- 2 Go : production avec Symfony Messenger et un transport Redis
- 4 Go : workers multiples, Redis, sessions haute fréquence

Ports à ouvrir : 80 (HTTP), 443 (HTTPS), 22 (SSH). Fermez tout le reste.

Installer PHP 8.3 et Symfony CLI sur VPS Ubuntu

  1. Mettre à jour le système et installer les dépendances préalables

    apt update && apt upgrade -y && apt install -y curl git unzip software-properties-common

  2. Ajouter le dépôt ondrej/php (Ubuntu 24.04 fournit déjà PHP 8.3 ; le dépôt est nécessaire pour PHP 8.4 ou plusieurs versions)

    add-apt-repository ppa:ondrej/php && apt update

  3. Installer PHP 8.3-FPM et les extensions requises par Symfony

    apt install -y php8.3-fpm php8.3-cli php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-intl php8.3-opcache php8.3-redis php8.3-mysql

  4. Vérifier la version installée et l'état du service FPM

    php --version && systemctl status php8.3-fpm

  5. Installer Composer 2.x globalement

    curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && composer --version

  6. Installer la Symfony CLI pour créer des projets et vérifier les prérequis

    curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | bash && apt install -y symfony-cli

  7. Vérifier que tous les prérequis Symfony sont satisfaits

    symfony check:requirements

  8. Installer nginx et Redis

    apt install -y nginx redis-server && systemctl enable --now nginx redis-server

Déployer une app Symfony en production avec Deployer PHP

  1. Dans votre projet Symfony local, ajouter Deployer comme dépendance de développement

    composer require deployer/deployer --dev

  2. Initialiser la configuration Deployer (choisir le recette `symfony` à la question)

    ./vendor/bin/dep init

  3. Éditer `deploy.php` à la racine du projet — adapter host, chemin de déploiement et branches

    require 'recipe/symfony.php';
    set('repository', '[email protected]:votre-org/votre-app.git');
    host('votre-vps.example.com')
    ->set('remote_user', 'deploy')
    ->set('deploy_path', '/var/www/symfony-app');

  4. Sur le VPS, créer l'utilisateur de déploiement et lui donner accès à nginx

    useradd -m -s /bin/bash deploy && usermod -aG www-data deploy

  5. Créer le fichier `.env.local` sur le VPS dans `{{deploy_path}}/shared/` — il sera partagé entre toutes les releases

    APP_ENV=prod
    APP_DEBUG=false
    APP_SECRET=<secret-aleatoire-32-chars>
    DATABASE_URL=mysql://user:[email protected]:3306/symfony_db

  6. Lancer le premier déploiement depuis votre machine locale

    ./vendor/bin/dep deploy votre-vps.example.com

  7. Vérifier que le symlink `current/` pointe bien vers la dernière release

    ls -la /var/www/symfony-app/

  8. En cas de problème, effectuer un rollback immédiat vers la release précédente

    ./vendor/bin/dep rollback votre-vps.example.com

Configuration nginx pour Symfony

Créez le fichier /etc/nginx/sites-available/symfony-app avec ce bloc server minimal, puis activez-le avec un symlink vers sites-enabled/. Seul index.php est transmis à PHP-FPM : un fichier .php déposé ailleurs dans public/ répond 404 au lieu d'être exécuté.

server {
    listen 80;
    server_name votre-domaine.com;
    root /var/www/symfony-app/current/public;
    index index.php;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        internal;
    }

    # tout autre fichier .php de public/ : 404, jamais exécuté
    location ~ \.php$ {
        return 404;
    }

    location ~ /\.ht {
        deny all;
    }
}

Activez le site et testez la configuration avant de recharger nginx :
ln -s /etc/nginx/sites-available/symfony-app /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx

Pour HTTPS, utilisez Certbot : certbot --nginx -d votre-domaine.com. Certbot modifie automatiquement le bloc server pour rediriger HTTP vers HTTPS et configurer les certificats Let's Encrypt.

Variables d'environnement et sécurité

Symfony lit .env, puis .env.local, puis .env.prod.local. En production, toute valeur sensible doit être dans .env.local — jamais commitée dans le dépôt git.

Variables essentielles en production :

APP_ENV=prod
APP_DEBUG=false
APP_SECRET=<64-chars-hex-aléatoire>
DATABASE_URL="mysql://dbuser:[email protected]:3306/symfony_prod"
MAILER_DSN=smtp://user:[email protected]:587
MESSENGER_TRANSPORT_DSN=redis://127.0.0.1:6379/messages

APP_SECRET doit être généré aléatoirement : openssl rand -hex 32. Ne jamais réutiliser la valeur par défaut du projet.

APP_DEBUG=false est critique : en mode debug, Symfony expose les traces de pile complètes, les variables de template et les requêtes SQL dans le navigateur — une fuite d'information sévère en production.

Avec le coffre de secrets de Symfony (config/secrets/prod/), les secrets chiffrés peuvent être committés sans risque ; seule la clé de déchiffrement (prod.decrypt.private.php, ou son contenu en base64 dans la variable d'environnement SYMFONY_DECRYPTION_SECRET) reste hors du dépôt, sur le serveur.

Queues asynchrones avec Symfony Messenger et Supervisor

Symfony Messenger permet de différer les tâches lourdes (envoi d'email, génération PDF, webhooks) dans une queue traitée de façon asynchrone. Sur VPS, Supervisor garantit que le worker tourne en permanence et redémarre après un crash.

Installez Supervisor :
apt install -y supervisor

Créez /etc/supervisor/conf.d/symfony-messenger.conf :

[program:symfony-messenger]
command=/var/www/symfony-app/current/bin/console messenger:consume async --time-limit=3600
user=deploy
autostart=true
autorestart=true
startretries=3
redirect_stderr=true
stdout_logfile=/var/log/supervisor/symfony-messenger.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5

L'option --time-limit=3600 force le worker à se relancer toutes les heures, libérant la mémoire accumulée. Supervisor s'occupe du redémarrage automatique.

Activez et démarrez le worker :
supervisorctl reread && supervisorctl update && supervisorctl start symfony-messenger

Pour surveiller l'état : supervisorctl status symfony-messenger

Côté Symfony, configurez le transport Redis dans config/packages/messenger.yaml :

framework:
    messenger:
        transports:
            async:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                retry_strategy:
                    max_retries: 3
                    delay: 1000

Hardening en 3 commandes. Bloquez l'accès à .git/ dans nginx (ajoutez location ~ /\.git { deny all; } à votre bloc server). Installez fail2ban pour bloquer les scans SSH et les tentatives de connexion répétées : apt install -y fail2ban. Restreignez les permissions du répertoire de déploiement : chmod 750 /var/www/symfony-app && chown -R deploy:www-data /var/www/symfony-app. Enfin, configurez le cache OPcache dans /etc/php/8.3/fpm/conf.d/10-opcache.ini : opcache.enable=1, opcache.memory_consumption=256, opcache.validate_timestamps=0 (désactivez la validation en prod pour les performances maximales — invalidez manuellement après chaque déploiement avec cachetool opcache:reset).

Dépannage — les 4 erreurs les plus fréquentes

1. The environment variable "APP_SECRET" is not set.
APP_SECRET est absent du fichier .env.local sur le serveur. Vérifiez que le fichier existe dans {{deploy_path}}/shared/ et qu'il contient bien cette variable. Deployer crée un symlink vers ce fichier partagé à chaque release — si le symlink est cassé, Symfony ne lit pas .env.local.

2. Erreur 500 silencieuse — logs vides
Avec APP_DEBUG=false, Symfony n'affiche rien dans le navigateur et écrit dans var/log/prod.log. Lisez ce fichier en temps réel : tail -f /var/www/symfony-app/current/var/log/prod.log. Une permission refusée sur var/cache/ ou var/log/ est la cause la plus fréquente — corrigez avec chown -R deploy:www-data var/ depuis le répertoire de la release courante.

3. Connection refused sur la base de données
DATABASE_URL pointe vers 127.0.0.1 mais MySQL écoute sur localhost (socket Unix) — ou l'inverse. Testez la connexion directement : mysql -u dbuser -p -h 127.0.0.1 symfony_db. Si MySQL n'écoute pas sur TCP, ajoutez bind-address = 127.0.0.1 dans /etc/mysql/mysql.conf.d/mysqld.cnf et redémarrez.

4. Unable to write in the "cache" directory
Ce message signifie que Symfony ne peut pas écrire dans var/cache/. Le processus PHP-FPM tourne sous www-data, mais le répertoire appartient à deploy sans permissions d'écriture pour le groupe. Solution : chmod -R 775 var/cache var/log && chown -R deploy:www-data var/.

Déployer depuis le Marketplace ServOrbit

Si vous préférez éviter les heures de configuration manuelle, le template symfony-stack du Marketplace ServOrbit installe automatiquement le socle logiciel de ce guide sur Ubuntu 24.04 : PHP 8.3-FPM et ses extensions, Composer, nginx, PostgreSQL 16, Redis et Supervisor. Il n'y a pas d'interface web : vous disposez d'un accès SSH root pour configurer le bloc server nginx, le worker Supervisor et les permissions comme décrit plus haut.

Le template installe PostgreSQL et non MySQL : écrivez une DATABASE_URL en postgresql://… dans .env.local, avec un APP_SECRET généré par openssl rand -hex 32, ou installez MySQL et php8.3-mysql si votre application en dépend.

Vous gardez un contrôle total : le VPS vous appartient, il n'y a aucun lock-in, et vous pouvez utiliser Deployer PHP exactement comme décrit ci-dessus dès la première connexion SSH.

Symfony opérationnel en 5 minutes

Commandez un VPS pré-configuré avec PHP 8.3, nginx, Redis et Supervisor — le stack Symfony complet installé et sécurisé, sans configuration manuelle.