Déploiement (Ubuntu + Apache)
Guide de déploiement pour un VPS OVH avec Ubuntu + Apache + Symfony.
Voir aussi :
guides/ssh-reference.md(aliases SSH â accĂšs poseidon et VPS OVH)guides/dsn-ovh.md(DNS OVH, wildcard, vhosts)guides/hsts.md(HSTS)
Prérequis
- Ubuntu 24.10+
- AccĂšs root ou sudo
- Nom de domaine configuré
Prérequis spécifiques (fonctionnalités à venir)
Ces prérequis concernent des fonctionnalités en cours de spécification (doc web
/docset IA). à appliquer au moment de leur déploiement, pas avant. Procédure IA détaillée : §11.
- Documentation web (
/docs) : le contenutelaria-docest inclus via un git submodule â exĂ©cutergit submodule update --init --recursiveau dĂ©ploiement / en CI. Voirspecs/docs-web.md. - Index vectoriel IA (
sqlite-vec) : extension SQLite non incluse par dĂ©faut â l'installer sur le serveur (binaire prĂ©-compilĂ© ou compilation) et vĂ©rifier que le build PHP autorise le chargement d'extensions ; Ă dĂ©faut, rĂ©aliser l'indexation via l'outillage (CLI/Python) et faire lire l'index par l'application. Voirspecs/ia-coeur.md. - Microservice d'embeddings (Python) : dĂ©ployable sĂ©parĂ© (FastAPI + sentence-transformers), Ă lancer en service (ex. systemd). Voir
tutos/ia/microservice-embeddings-python.md.
1) Sécuriser le serveur
Mise Ă jour :
sudo apt update && sudo apt upgrade -y
Firewall (UFW) :
sudo apt install ufw sudo ufw allow 'Apache Full' sudo ufw allow ssh sudo ufw allow 22/tcp # ou votre port SSH sudo ufw enable sudo ufw status verbose
Fail2ban (SSH) :
sudo apt install fail2ban sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local sudo nano /etc/fail2ban/jail.local
[sshd] enabled = true
sudo systemctl restart fail2ban
2) Installer la stack
Apache + PHP + MySQL :
sudo apt install -y apache2 php libapache2-mod-php mysql-server php-mysql sudo apt install -y php-pdo php-mysql php-zip php-gd php-mbstring \ php-curl php-xml php-pear php-bcmath php-imagick php-simplexml \ php-dom php-intl
Activer les modules Apache :
sudo a2enmod ssl sudo a2enmod headers sudo a2enmod rewrite sudo a2enmod http2 sudo a2enmod proxy_fcgi sudo a2enmod setenvif
PHP-FPM (recommandé) :
sudo a2dismod php8.4 sudo a2dismod mpm_prefork sudo a2enmod mpm_event sudo apt install php8.5-fpm sudo a2enconf php8.5-fpm sudo systemctl restart apache2 sudo systemctl restart php8.5-fpm
3) Apache : vhost HTTPS
Exemple minimal (adapter le domaine) :
<VirtualHost *:80> ServerName telaria.dev RewriteEngine On RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] </VirtualHost> <VirtualHost *:443> ServerName telaria.dev Protocols h2 http/1.1 DocumentRoot /var/www/codexia/public <Directory /var/www/codexia/public> AllowOverride None Require all granted FallbackResource /index.php </Directory> <FilesMatch \\.php$> SetHandler proxy:unix:/var/run/php/php8.5-fpm.sock|fcgi://telaria.dev </FilesMatch> SSLEngine on SSLCertificateFile /etc/letsencrypt/live/telaria.dev/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/telaria.dev/privkey.pem ErrorLog ${APACHE_LOG_DIR}/telaria.dev-error.log CustomLog ${APACHE_LOG_DIR}/telaria.dev-access.log combined </VirtualHost>
Activer :
sudo a2ensite telaria.dev.conf sudo systemctl reload apache2
4) Certificat SSL (OVH DNS-01)
Objectif : un certificat wildcard pour telaria.dev + *.telaria.dev.
Installer le plugin OVH :
sudo apt install python3-certbot-dns-ovh
Créer les credentials OVH :
# /etc/letsencrypt/ovh.ini dns_ovh_endpoint = ovh-eu # dns_ovh_application_key = ... # dns_ovh_application_secret = ... # dns_ovh_consumer_key = ...
sudo chmod 600 /etc/letsencrypt/ovh.ini
Demander le certificat :
sudo certbot certonly --dns-ovh --dns-ovh-credentials /etc/letsencrypt/ovh.ini \ -d telaria.dev -d "*.telaria.dev"
Renouvellement :
sudo certbot renew --dry-run
Note : la validation DNS-01 fonctionne sans port 80, mais garder le port 80 est recommandé pour les redirections HTTP -> HTTPS.
5) Déploiement de l'application
Permissions :
sudo chown -R ubuntu:www-data /var/www/codexia sudo find /var/www/codexia -type d -exec chmod 770 {} \; sudo find /var/www/codexia -type f -exec chmod 660 {} \; sudo find /var/www/codexia -type d -exec chmod g+s {} \;
Dépendances :
cd /var/www/codexia composer install --no-dev --optimize-autoloader
Configuration :
cp .env.dist .env.local nano .env.local
APP_ENV=prod APP_DEBUG=false APP_SECRET=your_generated_secret_here DATABASE_URL="mysql://codexia_user:your_password@127.0.0.1:3306/codexia_prod?serverVersion=8.4&charset=utf8mb4" MAILER_DSN=sendmail://default
Migrations + cache :
php bin/console doctrine:migrations:migrate --no-interaction
php bin/console cache:clear
php bin/console cache:warmup
â ïž Recharger PHP-FPM aprĂšs toute mise Ă jour de code/schĂ©ma â sinon l'OPcache sert l'ancien code :
sudo systemctl reload php8.5-fpmEn prod,
opcache.validate_timestamps=0(recommandĂ© pour la perf) impose ce rechargement. Sans lui, le CLI et les tests peuvent ĂȘtre verts alors que le web (FPM) plante â vĂ©cu rĂ©el :SQLSTATE[42S22] Unknown column 't0.username'aprĂšs le drop de la colonne. Ordre sĂ»r :git pullâmigrateâcache:clearâreload php8.5-fpm.
6) Diagnostics rapides
sudo apachectl -t -D DUMP_VHOSTS sudo apache2ctl -t curl -I --http2 https://telaria.dev
7) Sécurisation des Headers HTTP
Pour renforcer la sĂ©curitĂ© du serveur, appliquer une politique d'en-tĂȘtes HTTP standardisĂ©e cĂŽtĂ© Apache. Suivre le guide dĂ©diĂ© : Headers de SĂ©curitĂ© (Apache).
8) OCSP Stapling (optimisation TLS)
Objectif: réduire la latence cÎté client en fournissant la réponse OCSP depuis le serveur.
Configuration (dans le vhost 443 ou un fichier SSL global):
SSLUseStapling On
SSLStaplingCache shmcb:${APACHE_LOG_DIR}/stapling_cache(128000)
SSLStaplingReturnResponderErrors off
SSLStaplingResponderTimeout 5
Vérification:
openssl s_client -connect votre-domaine:443 -servername votre-domaine -status < /dev/null | grep -A3 "OCSP Response Status" # Attendu: OCSP Response Status: successful
9) Compression HTTP (Brotli + Gzip)
Activer Brotli (niveau 5) et conserver Gzip en fallback. Voir la procédure détaillée: Performances HTTP.
Rappel minimal de configuration globale (ex: conf-available/compression.conf):
<IfModule mod_brotli.c> AddOutputFilterByType BROTLI_COMPRESS \ text/html text/plain text/css application/javascript application/json image/svg+xml BrotliCompressionQuality 5 </IfModule> <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE \ text/html text/plain text/css application/javascript application/json image/svg+xml </IfModule>
Tests rapides:
curl -sI -H "Accept-Encoding: br" https://votre-domaine | grep -i content-encoding # br curl -sI -H "Accept-Encoding: gzip" https://votre-domaine | grep -i content-encoding # gzip
10) Cache navigateur (assets statiques)
Pour les assets versionnés (.css/.js/.svg), ajouter un cache long:
<IfModule mod_headers.c> <FilesMatch "\.(css|js|svg)$"> Header set Cache-Control "public, max-age=31536000, immutable" </FilesMatch> </IfModule>
Vérification 304:
ETAG=$(curl -sI https://votre-domaine/favicon.svg | awk -F': ' '/^etag:/ {print $2}') curl -I -H "If-None-Match: $ETAG" https://votre-domaine/favicon.svg # 304 si inchangé
11) DĂ©ploiement de la fonctionnalitĂ© IA (cĆur RAG)
Déploiement du bundle
telaria/rag-bundle(Telaria\Rag, consommĂ© partelariaen^0.1). Le cĆur fait du retrieval (pas de gĂ©nĂ©ration â aucune clĂ© Claude ici). Spec :specs/ia-coeur.md.
11.1 AccÚs Composer au dépÎt privé telaria-rag
- Deploy key GitHub (lecture) sur
<owner>/telaria-rag+ la clé SSH correspondante sur le VPS (~/.ssh), testée parssh -T git@github.com. - Dans
composer.json(repository VCS SSH), ajouter"no-api": trueâ sinon l'API GitHub renvoie 404 sans token (la clĂ© SSH du VPS suffit alors). - (vĂ©cu) Garder
bump-after-updatedĂ©sactivĂ© pour Ă©viter la divergencecomposer.jsonVPSâgit quand composer n'est pas lancĂ© en local. - Puis :
composer update telaria/rag-bundle.
11.2 Variables d'environnement (.env.local)
| Variable | RÎle | Défaut |
|---|---|---|
RAG_EMBEDDING_URL |
URL du microservice embeddings | http://127.0.0.1:8001 |
RAG_DB_PATH |
index SQLite | %kernel.project_dir%/var/rag/index.sqlite |
RAG_SQLITE_VEC_PATH |
chemin du .so sqlite-vec |
(sinon tente vec0) |
Les autres rĂ©glages (model, dimension, chunk, retrieval.k, source_rootâŠ) sont dans config/packages/telaria_rag.yaml. Aucune clĂ© Claude en L0.
11.3 Extension sqlite-vec
- Récupérer la release loadable (
github.com/asg017/sqlite-vec) :sqlite-vec-<ver>-loadable-linux-x86_64.tar.gzâvec0.so(~58 Ko), p. ex. dans/usr/local/lib/sqlite-vec/. - Pointer
RAG_SQLITE_VEC_PATHdessus. Le bundle charge viaPdo\Sqlite::loadExtension()(PHP 8.5) â validĂ© sur le VPS â . - â ïž Rappel (
ia-coeur.md§3.4) : la recherche kNN exige l'extension chargĂ©e en PHP. Si PHP ne pouvait pas la charger (open_basedir, build), il faudrait dĂ©porter la recherche hors PHP â le repli « indexation outillĂ©e » seul ne suffit pas.
11.4 â ïž Droits sur var/rag/ (le piĂšge)
app:rag:ingest (lancĂ© en ubuntu) crĂ©e l'index ; Apache/PHP-FPM lit en www-data. SQLite Ă©crit aussi -wal/-shm â le dossier doit ĂȘtre inscriptible par le lecteur. Deux stratĂ©gies :
- lancer l'ingest en
www-data:sudo -u www-data php bin/console app:rag:ingest; ou - recette VPS validée (groupe partagé + setgid pour héritage) :
sudo chgrp -R www-data var/rag sudo chmod -R g+rwX var/rag sudo chmod g+s var/rag # setgid : les nouveaux fichiers (-wal/-shm) héritent du groupe
11.5 Corpus (premier déploiement)
git clone <telaria-doc> docs/ Ă la racine (= rag.source_root = %kernel.project_dir%/docs). Le submodule pinnĂ© + treeview (specs/docs-web.md) est un chantier distinct â ne pas bloquer ici.
11.6 Microservice telaria-embeddings (Python, systemd)
Sans lui, app:rag:ingest et la recherche Ă©chouent. Mode « par dĂ©faut » (page /admin/rag dĂ©gradĂ©e, stats vides) â validable sans. Mode « nominal » â service up sur :8001, URL en RAG_EMBEDDING_URL.
ProcĂ©dure d'installation (tag courant : v0.1.2, Python 3.13.2 â versionnĂ© en .python-version) :
sudo apt install python3 python3-venv python3-pip sudo mkdir -p /opt/telaria-embeddings && sudo chown ubuntu: /opt/telaria-embeddings git clone git@github.com:<owner>/telaria-embeddings.git /opt/telaria-embeddings cd /opt/telaria-embeddings && git checkout v0.1.2 python3 -m venv .venv && .venv/bin/pip install -r requirements.txt # requirements.txt pin dĂ©jĂ torch==2.12.0+cpu via --extra-index-url â install propre CPU-only. # requirements.lock : Ă rĂ©gĂ©nĂ©rer SUR LA CIBLE si besoin (wheels Linux/3.13 spĂ©cifiques), avec # l'index CPU explicite pour rester propre : # .venv/bin/pip install --index-url https://download.pytorch.org/whl/cpu \ # --extra-index-url https://pypi.org/simple -r requirements.txt .venv/bin/pip freeze > requirements.lock sudo cp deploy/telaria-embeddings.service /etc/systemd/system/ sudo systemctl daemon-reload && sudo systemctl enable --now telaria-embeddings curl -s http://127.0.0.1:8001/health # {"status":"ok","model":...,"dim":768}
Unité systemd versionnée (deploy/telaria-embeddings.service, fournie par le dépÎt) :
- bind
127.0.0.1:8001(jamais exposĂ© directement â passe par Apache si besoin) HF_HOME=/var/lib/telaria-embeddings/hf(sur disque, pas/tmp)- durcissement :
ProtectSystem=strict,ReadWritePaths=/var/lib/telaria-embeddings,NoNewPrivileges=yes TimeoutStartSec=600â le 1er dĂ©marrage tĂ©lĂ©charge ~1,1 Go (modĂšle Hugging Face), prĂ©voir rĂ©seau + disque- empreinte runtime : ~440 Mo RAM une fois le modĂšle chargĂ©
Alternative conteneur (dev local, ou prod sur hÎte avec Docker) : image ghcr.io/<owner>/telaria-embeddings:v0.1.2 (ou :latest) publiée par CI GitHub Actions à chaque tag. Bind interne 0.0.0.0:8001, à publier sur 127.0.0.1:8001 cÎté hÎte. Cache HF dans /home/app/.cache/huggingface (user non-root app). Pour le VPS, le mode systemd reste recommandé (durcissement, simplicité, pas de runtime Docker requis).
Contrat strict /embed : type â {"query","passage"}, texts non vide â toute autre entrĂ©e â HTTP 422 (le bundle propage l'erreur).
11.7 Ordre sûr (réutilise le reload FPM du §5)
composer update telaria/rag-bundle php bin/console cache:clear && sudo systemctl reload php8.5-fpm # vĂ©rifier le mode dĂ©gradĂ© : /admin/rag â 200 + banniĂšre « index indisponible » # puis : installer vec0.so + RAG_SQLITE_VEC_PATH ; git clone <telaria-doc> docs/ php bin/console app:rag:stats # nominal (microservice up sur :8001) : php bin/console app:rag:ingest # --full = rĂ©indexation complĂšte php bin/console app:rag:search "comment rĂ©initialiser mon mot de passe ?"
Ordres de grandeur attendus aprĂšs ingest complet de telaria-doc : ~180 documents / ~2400 chunks, top-1 pertinent avec cosinus > 0.8 sur requĂȘtes en langage naturel.
11.8 PiĂšges connus
- BOM UTF-8 en tĂȘte d'un
.phpest fatal sous PHP 8.5 («strict_typesmust be the very first statement »). Enregistrer les.phpsans BOM. - OPcache (
opcache.validate_timestamps=0en prod) sert l'ancien code aprĂšsgit pullâ toujourscache:clearpuissudo systemctl reload php8.5-fpm(cf. §5). SymptĂŽme vĂ©cu :SQLSTATE[42S22] Unknown column 't0.username'cĂŽtĂ© web alors que CLI/tests verts. PDO::PARAM_INTsurvec0: bug d'intĂ©grationOnly integers are allowed for primary key valueslors des bindingsrowid/kâ corrigĂ© entelaria/rag-bundle v0.1.3. VĂ©rifier la contrainte composer (^0.1couvre v0.1.3+).requirements.lockPython : Ă gĂ©nĂ©rer sur le VPS (pip freeze), pas sur un poste Windows/macOS (wheels Linux-spĂ©cifiques pourtorchnotamment).bump-after-updateComposer : Ă laisser dĂ©sactivĂ© pour Ă©viter la divergencecomposer.jsonVPSâgit quand composer n'est pas lancĂ© en local.