Complete rewrite of DEPLOYMENT_HOMELAB.md covering: - IONOS DNS setup (with @ record workaround) - NPM proxy host config with exact nginx custom config - Docker server setup with setup-env.sh wizard - All 7 translation providers (Google, DeepL, OpenAI, DeepSeek, Minimax, OpenRouter, Ollama) - Full Stripe integration guide (account, products, webhooks, test cards, live mode) - NAS backup setup - Prometheus + Grafana monitoring - Gitea Actions CI/CD runner setup - Complete checklist Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
15 KiB
Guide de Deploiement - Wordly.art (Homelab)
NPM + IONOS + Docker + Stripe + API Keys + NAS Backup + Monitoring
Architecture
Internet
|
| DNS IONOS: wordly.art -> IP fixe
|
[Routeur/Box] (port forwarding 80+443 -> machine NPM)
|
+-- Machine 1: Nginx Proxy Manager (NPM)
| - SSL Let's Encrypt auto
| - Reverse proxy
|
+-- Machine 2/3: Docker (192.168.1.151)
|
+-- wordly-backend (FastAPI :8000)
+-- wordly-frontend (Next.js :3000)
+-- wordly-postgres (PostgreSQL :5432)
+-- wordly-redis (Redis :6379)
+-- wordly-prometheus (:9090 interne)
+-- wordly-grafana (:3001)
+-- wordly-node-exporter + cadvisor
|
+-- Backup quotidien -> NAS
Etape 1 : DNS IONOS
- Se connecter sur ionos.fr > Domaines & SSL > wordly.art > DNS
- Modifier les enregistrements A existants :
| Type | Nom (hote) | Valeur | Attention |
|---|---|---|---|
| A | (vide ou @) | Ton IP fixe | Pas dans "Sous-domaines", dans DNS principal |
| A | www |
Ton IP fixe | Dans Sous-domaines, taper juste www |
| A | monitoring |
Ton IP fixe | Dans Sous-domaines, taper juste monitoring |
Ton IP fixe : ouvre https://api.ipify.org depuis ton reseau
Ne pas toucher aux lignes Mail (MX, SPF, DKIM, autodiscover).
Verifier apres 10 min :
nslookup wordly.art
Etape 2 : Port forwarding + NPM
2.1 Routeur : ouvrir les ports
Sur ton routeur/box (souvent 192.168.1.1) :
| Port externe | Port interne | IP destination |
|---|---|---|
| 80 | 80 | IP de la machine NPM |
| 443 | 443 | IP de la machine NPM |
2.2 NPM : creer les Proxy Hosts
Interface NPM : http://IP_NPM:81
Proxy Host 1 : wordly.art
Details :
- Domain Names :
wordly.art - Scheme :
http - Forward Hostname/IP :
192.168.1.151(IP du serveur Wordly) - Forward Port :
3000 - Cocher Block Common Exploits + Websockets
SSL :
- Request a new SSL Certificate
- Cocher Force SSL + HTTP/2 + HSTS
- Email :
admin@wordly.art
Advanced - coller cette config nginx :
location /api/ {
proxy_pass http://192.168.1.151: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://192.168.1.151: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://192.168.1.151:8000;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location /docs { proxy_pass http://192.168.1.151:8000; proxy_set_header Host $host; }
location /redoc { proxy_pass http://192.168.1.151:8000; proxy_set_header Host $host; }
location /openapi.json { proxy_pass http://192.168.1.151:8000; proxy_set_header Host $host; }
location /admin { proxy_pass http://192.168.1.151: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://192.168.1.151:3000; add_header Cache-Control "public, max-age=31536000, immutable"; }
Si NPM et Wordly sont sur la meme machine Docker, remplace
192.168.1.151:8000parwordly-backend:8000etc.
Proxy Host 2 : www.wordly.art
Meme config, meme certificat SSL. Dans Advanced, ajoute :
return 301 https://wordly.art$request_uri;
Proxy Host 3 : monitoring.wordly.art
- Forward :
192.168.1.151:3001 - SSL Let's Encrypt
Etape 3 : Serveur Docker (192.168.1.151)
3.1 Installer Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
3.2 Cloner le projet
git clone -b production-deployment https://gitea.parsanet.org/sepehr/office_translator.git /opt/wordly
cd /opt/wordly
3.3 Configurer le .env avec le wizard
bash scripts/setup-env.sh
Le wizard demande le domaine, le mot de passe admin, le service de traduction, et genere tous les secrets automatiquement.
Ou configurer manuellement :
cp .env.production .env
nano .env
3.4 Lancer l'application
docker compose up -d --build
docker compose ps # Verifier que tout est "Up (healthy)"
curl http://localhost:8000/health
Etape 4 : Cles API - Services de traduction
L'application supporte 7 providers de traduction. Tu n'es pas oblige de tous les configurer - commence avec 1 ou 2.
Gerer les cles
cd /opt/wordly
bash scripts/manage-keys.sh
Menu interactif pour ajouter/modifier/supprimer des cles API.
Ou trouver les cles
| Provider | URL d'inscription | Cle a recuperer | Cout |
|---|---|---|---|
| Aucune inscription | Aucune cle (gratuit via deep_translator) | Gratuit | |
| DeepL | https://www.deepl.com/pro-api | Clé API DeepL (format: xxx-xxx-...) |
Gratuit 500k car/mois, puis 5.49EUR/mois |
| OpenAI | https://platform.openai.com/api-keys | sk-... |
~0.15$/1000 traductions |
| DeepSeek | https://platform.deepseek.com/api_keys | sk-... |
~0.14$/M tokens (tres bon rapport Q/P) |
| Minimax | https://platform.minimaxi.com/ | Cle API | Variable |
| OpenRouter | https://openrouter.ai/keys | sk-or-... |
Multi-modeles, pay-per-use |
Activer un provider dans le .env
Exemple pour DeepSeek :
# Dans le .env
DEEPSEEK_ENABLED=true
DEEPSEEK_API_KEY=sk-votre-cle-ici
DEEPSEEK_MODEL=deepseek-chat
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
Puis docker compose restart backend.
Recommandation pour demarrer
- Google (active par defaut, gratuit, aucune cle)
- DeepSeek (ajouter quand tu veux meilleure qualite, tres peu cher)
Etape 5 : Stripe - Systeme de paiement
Stripe gere les abonnements (Starter, Pro, Business). Si tu ne configures pas Stripe, l'app fonctionne en mode gratuit sans limitation d'abonnement.
5.1 Creer un compte Stripe
- Aller sur https://dashboard.stripe.com/register
- Creer un compte avec ton email
- Activer le compte (verifie identite + banque pour recevoir les paiements)
5.2 Recuperer les cles Stripe
Dans le dashboard Stripe : Developers > API keys
| Cle | Nom dans .env | Description |
|---|---|---|
sk_test_... ou sk_live_... |
STRIPE_SECRET_KEY |
Cle secrete (attention: NE PAS utiliser la cle publique pk_...) |
whsec_... |
STRIPE_WEBHOOK_SECRET |
Cle du webhook (voir section 5.4) |
Test vs Live : commence en mode test (
sk_test_...), passe en live quand tout fonctionne.
5.3 Creer les produits et forfaits
Dans Stripe : Products > Add product
Cree 3 produits :
Produit 1 : Starter
- Name :
Starter - Price :
9.00 EUR/ Monthly (recurring) - Une fois cree, clique sur le prix et copie le Price ID (
price_...)
Produit 2 : Pro
- Name :
Pro - Price :
19.00 EUR/ Monthly
Produit 3 : Business
- Name :
Business - Price :
49.00 EUR/ Monthly
Copie les 3 Price IDs.
5.4 Configurer le Webhook Stripe
Le webhook permet a Stripe de notifier ton app quand un paiement reussit, un abonnement est annule, etc.
Dans Stripe : Developers > Webhooks > Add endpoint
- Endpoint URL :
https://wordly.art/api/v1/stripe/webhook - Events a ecouter (cliquer "Select events") :
checkout.session.completedcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed
Une fois le webhook cree, clique dessus et copie le Signing secret (whsec_...).
5.5 Ajouter les cles Stripe dans le .env
cd /opt/wordly
bash scripts/manage-keys.sh
# Choisir option 4 (Stripe)
Ou manuellement dans .env :
STRIPE_SECRET_KEY=sk_live_votre_cle_secrete
STRIPE_WEBHOOK_SECRET=whsec_votre_signing_secret
STRIPE_STARTER_PRICE_ID=price_votre_price_id_starter
STRIPE_PRO_PRICE_ID=price_votre_price_id_pro
STRIPE_BUSINESS_PRICE_ID=price_votre_price_id_business
Puis docker compose restart backend.
5.6 Tester Stripe en mode test
- Utilise
sk_test_...comme STRIPE_SECRET_KEY - Cree des produits en mode test (meme process)
- Pour tester un paiement, Stripe fournit des cartes test :
- Succes :
4242 4242 4242 4242 - Echec :
4000 0000 0000 0002 - Exp : n'importe quelle date future
- CVC : n'importe quel nombre
- Succes :
- Verifie dans Stripe Dashboard > Payments que les paiements apparaissent
5.7 Passer en production
Quand tout fonctionne en test :
- Dans Stripe Dashboard, clique "Activate your account" en haut a gauche
- Remplie les infos business (identite, banque)
- Change dans le .env :
sk_test_...->sk_live_... - Refais les webhooks et produits en mode live
docker compose restart backend
5.8 Checklist Stripe
- Compte Stripe cree et verifie
- 3 produits crees (Starter 9EUR, Pro 19EUR, Business 49EUR)
- 3 Price IDs copies
- STRIPE_SECRET_KEY configure
- Webhook configure avec les 5 events
- STRIPE_WEBHOOK_SECRET configure
- 3 STRIPE_*_PRICE_ID configures
- Backend redemarre
- Test avec carte 4242 reussi
Etape 6 : Backup automatique vers NAS
6.1 Monter le NAS
sudo apt install cifs-utils
sudo mkdir -p /mnt/nas-backups/wordly
sudo tee /etc/nas-credentials <<EOF
username=wordly-backup
password=MOT_DE_PASSE_NAS
domain=WORKGROUP
EOF
sudo chmod 600 /etc/nas-credentials
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
6.2 Tester et programmer
cd /opt/wordly
bash scripts/backup-to-nas.sh --full
ls -lh /mnt/nas-backups/wordly/daily/
# Programmer le cron quotidien a 3h
crontab -e
# Ajouter :
0 3 * * * /opt/wordly/scripts/backup-to-nas.sh >> /var/log/wordly-backup.log 2>&1
Etape 7 : Monitoring (Prometheus + Grafana)
7.1 Lancer
cd /opt/wordly
docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d
7.2 Acceder
- URL :
https://monitoring.wordly.art - Login :
admin/ mot de passe defini dans le .env
7.3 Dashboards
| Dashboard | Contenu |
|---|---|
| Wordly - Application | Traductions, latence, providers, taux d'erreur |
| Wordly - Infrastructure | CPU, RAM, disque, reseau, status containers |
Alertes pre-configures : backend down, erreur > 10%, RAM > 90%, disque < 15%.
Etape 8 : Gitea Actions - Deploiement automatique
8.1 Installer le runner sur 192.168.1.151
mkdir -p /opt/gitea-runner && cd /opt/gitea-runner
wget https://gitea.com/gitea/act_runner/releases/latest/download/act_runner-linux-amd64
chmod +x act_runner-linux-amd64 && mv act_runner-linux-amd64 act_runner
./act_runner generate-config > config.yaml
8.2 Enregistrer le runner
- Gitea > office_translator > Settings > Actions > Runners > Create new Runner
- Copier le token
cd /opt/gitea-runner
./act_runner register \
--instance https://gitea.parsanet.org \
--token LE_TOKEN \
--name homelab-runner \
--labels self-hosted
8.3 Service systemd
sudo tee /etc/systemd/system/gitea-runner.service <<EOF
[Unit]
Description=Gitea Act Runner
After=docker.service
Requires=docker.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/gitea-runner
ExecStart=/opt/gitea-runner/act_runner daemon --config config.yaml
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now gitea-runner
sudo systemctl status gitea-runner
Desormais, chaque git push sur production-deployment deploye automatiquement.
Etape 9 : Operations courantes
| Action | Commande |
|---|---|
| Voir les logs | docker compose logs -f --tail=100 |
| Redemarrer backend | docker compose restart backend |
| Mettre a jour | cd /opt/wordly && git pull && docker compose up -d --build |
| Restaurer backup | bash scripts/backup-to-nas.sh --restore |
| Gerer les cles API | bash scripts/manage-keys.sh |
| Changer mot de passe admin | bash scripts/manage-keys.sh > option 5 |
| Espace disque | df -h && docker system df |
| SSL | NPM gere le renouvellement auto |
Checklist complete
DNS IONOS
- A record
@-> IP fixe - A record
www-> IP fixe - A record
monitoring-> IP fixe - Propagation verifiee
Routeur + NPM
- Ports 80+443 ouverts vers machine NPM
- Proxy Host wordly.art avec custom config
- SSL Let's Encrypt obtenu
- Proxy Host monitoring.wordly.art
Serveur Docker
- Docker installe
- Code clone dans /opt/wordly
bash scripts/setup-env.shexecuteddocker compose up -d --buildreussi- Tous les containers healthy
Cles API (minimum 1 provider)
- Provider de traduction configure et teste
bash scripts/manage-keys.shpour verifier
Stripe (optionnel)
- Compte Stripe cree et active
- 3 produits + Price IDs crees
- Webhook configure (5 events)
- Cles Stripe dans le .env
- Test avec carte 4242 reussi
Backup NAS
- NAS monte sur /mnt/nas-backups/wordly
- Backup manuel OK
- Cron programme a 3h
Monitoring
docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d- Grafana accessible sur monitoring.wordly.art
CI/CD
- Gitea runner installe et enregistre
- Service systemd active
Verification finale
- https://wordly.art accessible depuis Internet
- HTTPS OK (cadenas vert)
- Inscription utilisateur OK
- Upload + traduction OK
- Page admin /admin OK
- Monitoring OK