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 requiert PHP 8.2 au minimum — une contrainte que de nombreux mutualisés ne satisfont 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, de la syntaxe
#[Override]et des performances du JIT amélioré sans attendre la migration du serveur partagé - Supervisor et workers persistants — faites tourner
messenger:consumeen 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 deployinté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 des dépôts ondrej/php et d'un support long terme compatible avec les cycles de maintenance de Symfony.
PHP : version 8.3 recommandée (minimum 8.2 pour Symfony 7). 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. Composer 1.x ne supporte pas les contraintes de packages modernes.
Base de données : MySQL 8.0+ ou PostgreSQL 16+. Pour une app Symfony standard, 1 Go de RAM suffit pour MySQL ; PostgreSQL recommande 2 Go pour les plans de requête optimaux.
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
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-commonAjouter le dépôt ondrej/php pour obtenir PHP 8.3 sur Ubuntu 24.04
add-apt-repository ppa:ondrej/php && apt updateInstaller 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-mysqlVérifier la version installée et l'état du service FPM
php --version && systemctl status php8.3-fpmInstaller Composer 2.x globalement
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && composer --versionInstaller 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-cliVérifier que tous les prérequis Symfony sont satisfaits
symfony check:requirementsInstaller 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
Dans votre projet Symfony local, ajouter Deployer comme dépendance de développement
composer require deployer/deployer --devInitialiser la configuration Deployer (choisir le recette `symfony` à la question)
./vendor/bin/dep initÉ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');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 deployLancer le premier déploiement depuis votre machine locale
./vendor/bin/dep deploy productionVérifier que le symlink `current/` pointe bien vers la dernière release
ls -la /var/www/symfony-app/En cas de problème, effectuer un rollback immédiat vers la release précédente
./vendor/bin/dep rollback production
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/.
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 ~ \.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;
}
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/messagesAPP_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.
Pour les projets utilisant Symfony Secrets Vault (.env.vault), les secrets chiffrés peuvent être committés sans risque ; seule la clé de déchiffrement (SYMFONY_DECRYPTION_SECRET) reste dans .env.local.
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=5L'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: 1000Hardening 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. There are no commands defined in the "cache" namespace
Ce message trompeur signifie souvent 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 et configure automatiquement tout le socle décrit dans ce guide : PHP 8.3-FPM, Composer, nginx optimisé pour Symfony, Redis, Supervisor et les permissions correctes. Le VPS est opérationnel en moins de 5 minutes, avec un accès SSH root immédiat pour personnaliser votre déploiement.
Le template configure également les variables d'environnement essentielles via un formulaire guidé au moment de la commande — APP_SECRET généré aléatoirement, DATABASE_URL pré-remplie avec les credentials MySQL créés pour votre instance.
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.