Guide complet pour héberger l'application Tarot sur un VPS OVH avec Debian, Nginx, PHP-FPM et MariaDB.
- Architecture de production
- Prérequis
- 1. Préparation du serveur
- 2. Installation des dépendances système
- 3. Configuration de MariaDB
- 4. Configuration de PHP-FPM
- 5. Déploiement de l'application
- 6. Configuration de Nginx
- 7. HTTPS avec Let's Encrypt
- 8. Permissions et sécurité
- 9. Mise à jour de l'application
- 10. Supervision et maintenance
- Dépannage
┌─────────────┐
Navigateur ─────▶ │ Nginx │
│ (port 443) │
└──────┬──────┘
│
┌────────────┼────────────┐
▼ ▼
/api/* → PHP-FPM /* → fichiers statiques
(Symfony API) (React build)
│
▼
MariaDB
En production, Nginx sert deux rôles :
- Fichiers statiques : le build React (HTML, JS, CSS) servi directement
- Proxy PHP : les requêtes
/api/*sont transmises à PHP-FPM (Symfony)
Le frontend et le backend partagent le même domaine, ce qui élimine les problèmes de CORS.
- VPS OVH : Debian 12 (Bookworm) recommandé — minimum 1 vCPU, 2 Go RAM
- Nom de domaine pointant vers l'IP du VPS (enregistrement DNS A)
- Accès SSH en root ou avec un utilisateur sudo
ssh root@VOTRE_IP
# Mise à jour du système
apt update && apt upgrade -yadduser deploy
usermod -aG sudo deploy
# Se reconnecter en tant que deploy
su - deploysudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enableÉditer /etc/ssh/sshd_config :
PermitRootLogin no
PasswordAuthentication no # après avoir configuré une clé SSH
sudo systemctl restart sshd# Ajouter le dépôt Sury pour PHP 8.3
sudo apt install -y ca-certificates apt-transport-https software-properties-common lsb-release curl
curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
sudo dpkg -i /tmp/debsuryorg-archive-keyring.deb
sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-archive-keyring.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/sury-php.list'
sudo apt update
# Installer PHP et les extensions requises
sudo apt install -y \
php8.3-fpm \
php8.3-cli \
php8.3-common \
php8.3-mysql \
php8.3-xml \
php8.3-mbstring \
php8.3-intl \
php8.3-opcache \
php8.3-apcu \
php8.3-curl \
php8.3-zip \
unzipcurl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composercurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejssudo apt install -y mariadb-server
sudo mysql_secure_installationPendant mysql_secure_installation :
- Définir un mot de passe root
- Supprimer les utilisateurs anonymes : Oui
- Désactiver la connexion root à distance : Oui
- Supprimer la base de test : Oui
sudo apt install -y nginxsudo apt install -y gitsudo mysql -u root -pCREATE DATABASE tarot CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'tarot'@'localhost' IDENTIFIED BY 'VOTRE_MOT_DE_PASSE_SECURISE';
GRANT ALL PRIVILEGES ON tarot.* TO 'tarot'@'localhost';
FLUSH PRIVILEGES;
EXIT;Important : remplacez
VOTRE_MOT_DE_PASSE_SECURISEpar un mot de passe fort. Ne réutilisez pas le mot de passe root.
Éditer /etc/php/8.3/fpm/php.ini :
; Performance
memory_limit = 256M
max_execution_time = 30
upload_max_filesize = 10M
post_max_size = 10M
; OPcache (crucial pour Symfony en production)
opcache.enable = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
; ⚠️ validate_timestamps = 0 signifie qu'il faut redémarrer PHP-FPM après chaque déploiement
; Sécurité
expose_php = OffÉditer /etc/php/8.3/fpm/pool.d/www.conf :
[www]
user = deploy
group = deploy
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
; Ajuster selon la RAM du VPS
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5sudo systemctl restart php8.3-fpmsudo mkdir -p /var/www/tarot
sudo chown deploy:deploy /var/www/tarotcd /var/www/tarot
git clone git@github.com:Soviann/tarot.git .Note : configurer une clé SSH de déploiement (lecture seule) sur le dépôt GitHub, ou utiliser un token HTTPS.
cd /var/www/tarot/backend
# Installer les dépendances (sans les dev)
composer install --no-dev --optimize-autoloader
# Créer le fichier d'environnement de production
cp .env .env.localÉditer /var/www/tarot/backend/.env.local :
APP_ENV=prod
APP_SECRET=GENEREZ_UNE_CLE_SECRETE_DE_32_CARACTERES
DATABASE_URL="mysql://tarot:VOTRE_MOT_DE_PASSE_SECURISE@127.0.0.1:3306/tarot?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
CORS_ALLOW_ORIGIN='^https://VOTRE_DOMAINE\.fr$'Pour générer
APP_SECRET:php -r "echo bin2hex(random_bytes(16));"
# Compiler les fichiers .env pour la production (plus rapide que parser à chaque requête)
composer dump-env prod
# Exécuter les migrations
php bin/console doctrine:migrations:migrate --no-interaction
# Vider et réchauffer le cache
php bin/console cache:clear
php bin/console cache:warmupcd /var/www/tarot/frontend
# Installer les dépendances
npm ci
# Builder pour la production
# VITE_API_URL n'est PAS nécessaire si le frontend est servi depuis le même domaine
# (les requêtes iront vers /api par défaut)
npm run buildLe build produit les fichiers dans frontend/dist/.
Créer le fichier de configuration : /etc/nginx/sites-available/tarot
server {
listen 80;
server_name VOTRE_DOMAINE.fr;
# Redirection HTTP → HTTPS (activé après Certbot, étape 7)
# return 301 https://$server_name$request_uri;
# --- Configuration temporaire avant HTTPS ---
# (sera remplacée par Certbot)
# Frontend : fichiers statiques React (SPA)
root /var/www/tarot/frontend/dist;
index index.html;
# Logs
access_log /var/log/nginx/tarot-access.log;
error_log /var/log/nginx/tarot-error.log;
# Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
gzip_min_length 1000;
# ──────────────────────────────────────────────
# Backend API : requêtes /api/* → PHP-FPM
# ──────────────────────────────────────────────
location /api {
alias /var/www/tarot/backend/public;
try_files $uri /api/index.php$is_args$args;
location ~ \.php$ {
# Résoudre le vrai chemin du script
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# IMPORTANT : le SCRIPT_FILENAME doit pointer vers le vrai fichier
fastcgi_param SCRIPT_FILENAME /var/www/tarot/backend/public/index.php;
fastcgi_param DOCUMENT_ROOT /var/www/tarot/backend/public;
# Optimisations
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
internal;
}
}
# ──────────────────────────────────────────────
# PWA : Service Worker et manifeste
# ──────────────────────────────────────────────
location /sw.js {
add_header Cache-Control "no-cache";
try_files $uri =404;
}
location /manifest.webmanifest {
add_header Content-Type "application/manifest+json";
try_files $uri =404;
}
# ──────────────────────────────────────────────
# Assets statiques : cache long (fichiers hashés par Vite)
# ──────────────────────────────────────────────
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# ──────────────────────────────────────────────
# SPA fallback : toutes les autres routes → index.html
# ──────────────────────────────────────────────
location / {
try_files $uri $uri/ /index.html;
}
# Sécurité : bloquer les fichiers cachés
location ~ /\. {
deny all;
}
}sudo ln -s /etc/nginx/sites-available/tarot /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default # Supprimer le site par défaut
sudo nginx -t # Tester la configuration
sudo systemctl reload nginxConfiguration Nginx
alias+/api: cette configuration utilisealiaspour servir les requêtes/api/*depuis le répertoirebackend/public/. L'ensemble passe par leindex.phpde Symfony. Selon le routage Symfony, les URLs sont de la forme/api/players,/api/sessions, etc. — elles fonctionnent directement.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d VOTRE_DOMAINE.frCertbot va :
- Obtenir un certificat SSL gratuit
- Modifier automatiquement la config Nginx pour HTTPS
- Ajouter la redirection HTTP → HTTPS
- Configurer le renouvellement automatique
Vérifier le renouvellement automatique :
sudo certbot renew --dry-runcd /var/www/tarot
# L'utilisateur deploy est propriétaire de tout
chown -R deploy:deploy .
# Backend : Symfony a besoin d'écrire dans var/ (cache, logs)
chmod -R 775 backend/varS'assurer que ces fichiers ne sont jamais accessibles publiquement :
# .env.local.php contient les secrets compilés
chmod 640 /var/www/tarot/backend/.env.local.php
# Le répertoire .git ne doit pas être exposé (déjà bloqué par Nginx)Script de déploiement à placer dans /var/www/tarot/deploy.sh :
#!/bin/bash
set -euo pipefail
APP_DIR="/var/www/tarot"
echo "=== Déploiement Tarot ==="
cd "$APP_DIR"
# 1. Récupérer les dernières modifications
echo "[1/7] Pull des sources..."
git pull origin main
# 2. Backend
echo "[2/7] Installation des dépendances backend..."
cd "$APP_DIR/backend"
composer install --no-dev --optimize-autoloader
echo "[3/7] Compilation de l'environnement..."
composer dump-env prod
echo "[4/7] Migrations de la base de données..."
php bin/console doctrine:migrations:migrate --no-interaction
echo "[5/7] Nettoyage du cache Symfony..."
php bin/console cache:clear
php bin/console cache:warmup
# 3. Frontend
echo "[6/7] Build du frontend..."
cd "$APP_DIR/frontend"
npm ci
npm run build
# 4. Redémarrer PHP-FPM (nécessaire car opcache.validate_timestamps = 0)
echo "[7/7] Redémarrage de PHP-FPM..."
sudo systemctl restart php8.3-fpm
echo "=== Déploiement terminé ==="chmod +x /var/www/tarot/deploy.shPour déployer :
/var/www/tarot/deploy.shSudo sans mot de passe pour PHP-FPM : pour que le script puisse redémarrer PHP-FPM sans mot de passe, ajouter dans
/etc/sudoers.d/deploy:deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart php8.3-fpm
# Logs Nginx
tail -f /var/log/nginx/tarot-access.log
tail -f /var/log/nginx/tarot-error.log
# Logs Symfony (prod)
tail -f /var/www/tarot/backend/var/log/prod.log
# Logs PHP-FPM
journalctl -u php8.3-fpm -f
# Logs MariaDB
journalctl -u mariadb -fCréer un script de sauvegarde /var/www/tarot/backup-db.sh :
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/var/backups/tarot"
DATE=$(date +%Y-%m-%d_%H%M)
mkdir -p "$BACKUP_DIR"
mysqldump -u tarot -pVOTRE_MOT_DE_PASSE_SECURISE tarot > "$BACKUP_DIR/tarot_$DATE.sql"
# Garder les 30 dernières sauvegardes
ls -t "$BACKUP_DIR"/tarot_*.sql | tail -n +31 | xargs -r rm
echo "Sauvegarde créée : $BACKUP_DIR/tarot_$DATE.sql"Automatiser via cron (tous les jours à 3h) :
crontab -e
# Ajouter :
0 3 * * * /var/www/tarot/backup-db.sh >> /var/log/tarot-backup.log 2>&1# Tester que l'API répond
curl -s https://VOTRE_DOMAINE.fr/api/players | head -c 200
# Tester que le frontend est servi
curl -s -o /dev/null -w "%{http_code}" https://VOTRE_DOMAINE.fr/# Mises à jour de sécurité (à faire régulièrement)
sudo apt update && sudo apt upgrade -y
# Renouvellement certificat SSL (automatique, mais vérifiable)
sudo certbot renew --dry-runPHP-FPM ne tourne pas ou le socket est incorrect :
sudo systemctl status php8.3-fpm
sudo systemctl restart php8.3-fpm
ls -la /run/php/php8.3-fpm.sockProblème de permissions :
# Vérifier que Nginx peut lire les fichiers
namei -l /var/www/tarot/frontend/dist/index.htmlConsulter les logs Symfony :
tail -50 /var/www/tarot/backend/var/log/prod.logCauses fréquentes :
APP_SECRETnon défini → vérifier.env.local.phpDATABASE_URLincorrect → tester la connexion MySQL- Cache corrompu →
php bin/console cache:clear
La configuration location /api ne route pas correctement vers PHP-FPM. Vérifier :
sudo nginx -t
sudo nginx -T | grep -A 20 "location /api"Le Service Worker nécessite HTTPS. Vérifier :
- Le certificat SSL est valide :
curl -vI https://VOTRE_DOMAINE.fr 2>&1 | grep "SSL certificate" - Le fichier
sw.jsest accessible :curl -I https://VOTRE_DOMAINE.fr/sw.js - Le header
Cache-Control: no-cacheest présent sursw.js
# Vérifier qu'OPcache est activé
php -i | grep opcache.enable
# Vérifier que le cache Symfony est réchauffé
ls -la /var/www/tarot/backend/var/cache/prod/
# Vérifier les slow queries MariaDB
sudo mysql -e "SHOW VARIABLES LIKE 'slow_query_log';"| Service | Port | Usage |
|---|---|---|
| Nginx | 80, 443 | HTTP/HTTPS (frontend + API) |
| PHP-FPM | socket Unix | Traitement PHP (API Symfony) |
| MariaDB | 3306 (local) | Base de données (non exposé) |
| SSH | 22 | Administration |
| Élément | Chemin |
|---|---|
| Racine du projet | /var/www/tarot/ |
| Backend Symfony | /var/www/tarot/backend/ |
| Frontend build | /var/www/tarot/frontend/dist/ |
| Config Nginx | /etc/nginx/sites-available/tarot |
| Config PHP-FPM | /etc/php/8.3/fpm/pool.d/www.conf |
| Logs Nginx | /var/log/nginx/tarot-*.log |
| Logs Symfony | /var/www/tarot/backend/var/log/prod.log |
| Sauvegardes BDD | /var/backups/tarot/ |
| Script de déploiement | /var/www/tarot/deploy.sh |
| Variable | Fichier | Exemple |
|---|---|---|
APP_ENV |
.env.local |
prod |
APP_SECRET |
.env.local |
a1b2c3... (32 caractères hex) |
DATABASE_URL |
.env.local |
mysql://tarot:pass@127.0.0.1:3306/tarot?serverVersion=10.11.2-MariaDB&charset=utf8mb4 |
CORS_ALLOW_ORIGIN |
.env.local |
^https://tarot\.mondomaine\.fr$ |