- Restructured docker-compose for Nginx Proxy Manager (no custom nginx) - Added domain wordly.art configuration - Added Prometheus + Grafana monitoring stack with pre-configured dashboards - Added PostgreSQL backup script to NAS (daily/weekly/monthly rotation) - Added alert rules for backend, system, and Docker metrics - Updated deployment guide for NPM + IONOS DNS homelab setup - Added marketing plan document - PDF translator and watermark support - Enhanced middleware, routes, and translator modules Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
15 KiB
Guide de Deploiement - Wordly.art (Homelab)
Nginx Proxy Manager + IONOS DNS + Docker + NAS Backup + Monitoring
Architecture
Internet
|
| DNS IONOS: wordly.art -> ton IP fixe
|
[Routeur/Box] (port forwarding 80+443 -> machine NPM)
|
+-- Machine 1: Nginx Proxy Manager (NPM)
| - SSL Let's Encrypt automatique
| - Reverse proxy vers les services
|
+-- Machine 2 ou 3: Docker (Wordly app)
|
+-- wordly-backend (FastAPI :8000)
+-- wordly-frontend (Next.js :3000)
+-- wordly-postgres (PostgreSQL :5432)
+-- wordly-redis (Redis :6379)
+-- wordly-prometheus (interne :9090)
+-- wordly-grafana (:3001)
+-- wordly-node-exporter
+-- wordly-cadvisor
|
+-- Backup quotidien -> NAS (SMB/NFS)
Routing NPM
| Sous-domaine | Cible Docker | Port |
|---|---|---|
wordly.art |
wordly-frontend |
3000 |
wordly.art/api/* |
wordly-backend |
8000 |
wordly.art/translate |
wordly-backend |
8000 |
monitoring.wordly.art |
wordly-grafana |
3000 |
Etape 1 : Configuration DNS chez IONOS
1.1 Connexion a IONOS
- Se connecter sur ionos.fr / ionos.com
- Aller dans Domaines & SSL
- Cliquer sur wordly.art
1.2 Creer les enregistrements DNS
Cliquer sur DNS > Gerer les enregistrements et ajouter :
| Type | Nom | Valeur | TTL |
|---|---|---|---|
| A | @ |
TON_IP_FIXE |
3600 |
| A | www |
TON_IP_FIXE |
3600 |
| A | monitoring |
TON_IP_FIXE |
3600 |
Remplace
TON_IP_FIXEpar ton IP fixe publique. Pour la trouver : https://whatismyip.com
1.3 Verifier la propagation DNS
# Attendre 5-30 minutes puis verifier
nslookup wordly.art
nslookup monitoring.wordly.art
Etape 2 : Nginx Proxy Manager (NPM)
2.1 Verifier que NPM tourne
NPM est deja installe sur une de tes machines. Verifier :
# Sur la machine qui heberge NPM
docker ps | grep npm
# ou
docker ps | grep nginx-proxy-manager
L'interface admin NPM est accessible sur http://IP_NPM:81
2.2 Connecter NPM au reseau Docker de Wordly
Pour que NPM puisse atteindre les containers Wordly par leur nom, il doit etre sur le meme reseau Docker.
# Sur la machine qui heberge NPM, trouver le nom du container NPM
docker ps | grep -i npm
# Trouver le reseau du container NPM
docker inspect <NPM_CONTAINER> | grep -A5 Networks
# Sur la machine Wordly, on va connecter NPM au reseau wordly-network
# (uniquement si NPM et Wordly sont sur la MEME machine Docker)
docker network connect wordly-network <NPM_CONTAINER>
Si NPM et Wordly sont sur des machines differentes (recommande avec 3 machines) :
- Pas besoin de reseau Docker partage
- NPM utilisera l'IP de la machine Wordly au lieu du nom de container
- Voir section 2.4 pour la configuration
2.3 Creer les Proxy Hosts dans NPM
Se connecter a http://IP_NPM:81 puis :
Proxy Host 1 : wordly.art (Frontend + Backend)
Aller dans Proxy Hosts > Add Proxy Host :
Onglet Details :
- Domain Names :
wordly.art - Scheme :
http - Forward Hostname/IP :
wordly-frontend(si meme machine) OUIP_MACHINE_WORDLY(si machines differentes) - Forward Port :
3000 - Cocher Block Common Exploits
- Cocher Websockets Support
Onglet SSL :
- SSL Certificate :
Request a new SSL Certificate - Cocher Force SSL
- Cocher HTTP/2 Support
- Cocher HSTS Enabled
- Email :
admin@wordly.art - Cocher I Agree to the...
Cliquer Save. NPM va automatiquement obtenir le certificat Let's Encrypt.
Proxy Host 2 : www.wordly.art -> redirect
Onglet Details :
- Domain Names :
www.wordly.art - Scheme :
http - Forward Hostname/IP :
wordly-frontend - Forward Port :
3000
Onglet SSL :
- Meme certificat que wordly.art (selectionner le certificat deja cree)
Onglet Advanced : Ajouter dans le champ Custom Nginx Configuration :
# Rediriger toutes les requetes API et translate vers le backend
location /api/ {
proxy_pass http://wordly-backend:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
add_header Access-Control-Allow-Origin https://wordly.art always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, X-API-Key" always;
add_header Access-Control-Allow-Credentials "true" always;
if ($request_method = 'OPTIONS') {
return 204;
}
}
location /translate {
proxy_pass http://wordly-backend:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
client_max_body_size 100M;
}
location /health {
proxy_pass http://wordly-backend:8000;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location /docs {
proxy_pass http://wordly-backend:8000;
proxy_set_header Host $host;
}
location /redoc {
proxy_pass http://wordly-backend:8000;
proxy_set_header Host $host;
}
location /openapi.json {
proxy_pass http://wordly-backend:8000;
proxy_set_header Host $host;
}
location /admin {
proxy_pass http://wordly-frontend:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /_next/static/ {
proxy_pass http://wordly-frontend:3000;
add_header Cache-Control "public, max-age=31536000, immutable";
}
Important
: Si NPM et Wordly sont sur des machines differentes, remplacer
wordly-backend:8000parIP_MACHINE_WORDLY:8000etwordly-frontend:3000parIP_MACHINE_WORDLY:3000
Proxy Host 3 : monitoring.wordly.art (Grafana)
Onglet Details :
- Domain Names :
monitoring.wordly.art - Scheme :
http - Forward Hostname/IP :
wordly-grafana(si meme machine) OUIP_MACHINE_WORDLY(si machines differentes) - Forward Port :
3000 - Cocher Block Common Exploits
Onglet SSL :
- SSL Certificate :
Request a new SSL Certificate - Cocher Force SSL
- Cocher HSTS Enabled
- Email :
admin@wordly.art
2.4 Cas : NPM et Wordly sur machines differentes
Si NPM tourne sur Machine A et Wordly sur Machine B :
-
Ouvrir les ports sur la Machine B pour que NPM puisse atteindre les services :
# Sur Machine B, ouvrir dans le pare-feu local sudo ufw allow from IP_MACHINE_A to any port 3000 # Frontend sudo ufw allow from IP_MACHINE_A to any port 8000 # Backend sudo ufw allow from IP_MACHINE_A to any port 3001 # Grafana -
Exposer les ports dans docker-compose.yml en ajoutant la section
ports:backend: # ... config existante ... ports: - "IP_MACHINE_B:8000:8000" # Bind sur IP locale seulement frontend: # ... config existante ... ports: - "IP_MACHINE_B:3000:3000" grafana: # ... config existante ... ports: - "IP_MACHINE_B:3000:3000" -
Dans NPM, utiliser
IP_MACHINE_Bcomme Forward Hostname
Etape 3 : Deploiement de l'application
3.1 Preparer le serveur
# Installer Docker (si pas deja fait)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
# Verifier
docker --version
docker compose version
3.2 Transferer le code
# Cloner le repo
git clone <ton-repo-git> /opt/wordly
cd /opt/wordly
git checkout production-deployment
3.3 Configurer les secrets
cd /opt/wordly
# Copier le fichier env
cp .env.production .env
# Generer le hash bcrypt du mot de passe admin
docker run --rm python:3.12-slim bash -c "
pip install 'passlib[bcrypt]' bcrypt > /dev/null 2>&1 &&
python -c \"from passlib.context import CryptContext; print(CryptContext(schemes=['bcrypt']).hash('TON_MOT_DE_PASSE_ADMIN'))\"
"
Copier le hash affiche et le coller dans .env pour ADMIN_PASSWORD_HASH.
# Editer le fichier .env et verifier tous les champs
nano .env
Verifications cles dans .env :
DOMAIN=wordly.artNEXT_PUBLIC_API_URL=https://wordly.artJWT_SECRET_KEY=(deja rempli)ADMIN_TOKEN_SECRET=(deja rempli)ADMIN_PASSWORD_HASH=(coller le hash genere)CORS_ORIGINS=https://wordly.artPOSTGRES_PASSWORD=(deja rempli)
3.4 Lancer l'application
# Build et demarrage
docker compose up -d --build
# Suivre les logs pendant le premier demarrage
docker compose logs -f
3.5 Verifier
# Sur le serveur, tester directement
curl http://localhost:8000/health
# Verifier tous les containers
docker compose ps
Resultat attendu :
NAME STATUS PORTS
wordly-postgres Up (healthy)
wordly-redis Up (healthy)
wordly-backend Up (healthy)
wordly-frontend Up (healthy)
Etape 4 : Verification complete
4.1 Tester depuis l'exterieur
# Depuis un autre ordinateur ou telephone
curl -I https://wordly.art
# Doit retourner:
# HTTP/2 200
# server: nginx
# strict-transport-security: ...
4.2 Tester dans le navigateur
- Ouvrir https://wordly.art -> doit afficher le frontend
- Ouvrir https://wordly.art/health -> doit retourner
{"status": "ok"} - Ouvrir https://wordly.art/admin -> doit afficher le login admin
- Ouvrir https://monitoring.wordly.art -> doit afficher Grafana
4.3 Tester le SSL
Le cadenas vert doit etre present sur wordly.art. NPM a automatiquement obtenu le certificat Let's Encrypt.
Etape 5 : Backup automatique vers NAS
5.1 Monter le NAS
Option A : SMB/CIFS (Synology / QNAP)
sudo apt install cifs-utils
sudo mkdir -p /mnt/nas-backups/wordly
# Fichier credentials
sudo tee /etc/nas-credentials <<EOF
username=wordly-backup
password=MOT_DE_PASSE_NAS
domain=WORKGROUP
EOF
sudo chmod 600 /etc/nas-credentials
# Montage auto au demarrage
echo "//IP_DU_NAS/wordly-backups /mnt/nas-backups/wordly cifs credentials=/etc/nas-credentials,uid=$(id -u),gid=$(id -g),iocharset=utf8,vers=3.0,noperm 0 0" | sudo tee -a /etc/fstab
sudo mount /mnt/nas-backups/wordly
Option B : NFS
sudo apt install nfs-common
sudo mkdir -p /mnt/nas-backups/wordly
echo "IP_DU_NAS:/volume1/wordly-backups /mnt/nas-backups/wordly nfs rw,hard,intr 0 0" | sudo tee -a /etc/fstab
sudo mount /mnt/nas-backups/wordly
5.2 Tester le backup
chmod +x /opt/wordly/scripts/backup-to-nas.sh
/opt/wordly/scripts/backup-to-nas.sh --full
# Verifier
ls -lh /mnt/nas-backups/wordly/daily/
5.3 Programmer le cron
crontab -e
# Backup quotidien a 3h du matin
0 3 * * * /opt/wordly/scripts/backup-to-nas.sh >> /var/log/wordly-backup.log 2>&1
Etape 6 : Monitoring (Prometheus + Grafana)
6.1 Lancer le stack monitoring
cd /opt/wordly
# Creer le reseau externe si necessaire
docker network create wordly-network 2>/dev/null || true
# Lancer application + monitoring
docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d
6.2 Acceder a Grafana
- URL :
https://monitoring.wordly.art(via NPM) - Login :
admin - Mot de passe :
WordlyGrafana2026! - Changer le mot de passe a la premiere connexion
6.3 Dashboards pre-configures
| Dashboard | Contenu |
|---|---|
| Wordly - Application | Traductions, latence, providers, taux d'erreur |
| Wordly - Infrastructure | CPU, RAM, disque, reseau, status containers |
Ils apparaissent automatiquement dans le dossier Wordly de Grafana.
6.4 Configurer les alertes (optionnel)
Dans Grafana : Alerting > Contact points > Add contact point :
- Type : Email ou Discord webhook
- Ajouter ton email ou l'URL du webhook
Les regles d'alerte suivantes sont pre-configures :
- Backend down
- Taux d'erreur > 10%
- RAM > 90%
- Disque < 15%
Etape 7 : Operations courantes
Logs
docker compose logs -f --tail=100 # Tous les services
docker compose logs -f backend # Backend seul
docker compose logs -f frontend # Frontend seul
docker compose logs -f postgres # Base de donnees
Redemarrer
docker compose restart backend
docker compose restart frontend
Mettre a jour
cd /opt/wordly
git pull origin production-deployment
docker compose up -d --build
Restaurer un backup
/opt/wordly/scripts/backup-to-nas.sh --restore
# Liste les backups disponibles
/opt/wordly/scripts/backup-to-nas.sh --restore wordly_db_20260510_030000.sql.gz
Verifier l'espace
df -h # Disque systeme
docker system df # Espace Docker
Renouveler le SSL
NPM renouvelle les certificats Let's Encrypt automatiquement. Pas d'action requise. Verifier dans NPM > SSL Certificates que le statut est OK.
Checklist de deploiement
DNS IONOS
- Enregistrement A :
@-> IP fixe - Enregistrement A :
www-> IP fixe - Enregistrement A :
monitoring-> IP fixe - Propagation DNS verifiee (nslookup)
Nginx Proxy Manager
- Proxy Host cree pour
wordly.art-> frontend:3000 - SSL Let's Encrypt obtenu pour
wordly.art - Custom config nginx ajoutee (routing API/backend)
- Proxy Host cree pour
monitoring.wordly.art-> grafana:3000 - SSL Let's Encrypt obtenu pour
monitoring.wordly.art
Serveur Docker
- Docker installe
- Code clone dans /opt/wordly
- Fichier .env rempli avec tous les secrets
- Hash bcrypt genere et colle
docker compose up -d --buildreussi- Tous les containers healthy
NAS
- Partage de backup cree sur le NAS
- NAS monte sur /mnt/nas-backups/wordly
- Backup manuel test OK
- Cron programme a 3h
Verification finale
- https://wordly.art accessible depuis Internet
- HTTPS OK (cadenas vert)
- Upload fichier OK
- Traduction test OK
- https://monitoring.wordly.art accessible
- Dashboards Grafana affichent des donnees
- Page admin accessible (/admin)