---
title: Deploiement
description: Guide de deploiement en production de libreDecision
---

# Deploiement

Ce guide couvre le deploiement complet de libreDecision en production avec Docker, Traefik, PostgreSQL et IPFS.

## Prerequis

| Composant | Version minimale | Description |
| --------- | ---------------- | ----------- |
| Docker | 24+ | Moteur de conteneurs |
| Docker Compose | 2.20+ | Orchestration multi-conteneurs |
| Nom de domaine | -- | Domaine pointe vers le serveur (ex: `decision.librodrome.org`) |
| Certificat TLS | -- | Genere automatiquement par Traefik via Let's Encrypt |
| Traefik | 2.10+ | Reverse proxy avec terminaison TLS (reseau externe `traefik`) |
| Serveur | 2 vCPU, 4 Go RAM, 40 Go SSD | Ressources recommandees |

### Reseau Traefik

Le deploiement suppose qu'un reseau Docker externe `traefik` existe deja avec une instance Traefik configuree. Si ce n'est pas le cas, creez-le :

```bash
docker network create traefik
```

Et deployez Traefik separement (voir section [Configuration Traefik](#configuration-traefik)).

## Configuration .env

Copiez le fichier `.env.example` et configurez chaque variable pour la production :

```bash
cp .env.example .env
```

### Variables d'environnement

| Variable | Description | Valeur par defaut | Production |
| -------- | ----------- | ----------------- | ---------- |
| `POSTGRES_DB` | Nom de la base de donnees | `libredecision` | `libredecision` |
| `POSTGRES_USER` | Utilisateur PostgreSQL | `libredecision` | `libredecision` |
| `POSTGRES_PASSWORD` | Mot de passe PostgreSQL | `change-me-in-production` | **Generer un mot de passe fort** (32+ caracteres) |
| `DATABASE_URL` | URL de connexion asyncpg | `postgresql+asyncpg://...@localhost:5432/...` | Construite automatiquement dans docker-compose |
| `SECRET_KEY` | Cle secrete pour les tokens de session | `change-me-in-production-with-a-real-secret-key` | **Generer une cle aleatoire** (`openssl rand -hex 64`) |
| `DEBUG` | Mode debug | `true` | **`false`** |
| `CORS_ORIGINS` | Origines CORS autorisees | `["http://localhost:3002"]` | `["https://decision.librodrome.org"]` |
| `DUNITER_RPC_URL` | URL du noeud Duniter V2 RPC | `wss://gdev.p2p.legal/ws` | URL du noeud de production |
| `IPFS_API_URL` | URL de l'API IPFS (kubo) | `http://localhost:5001` | `http://ipfs:5001` (interne Docker) |
| `IPFS_GATEWAY_URL` | URL de la passerelle IPFS | `http://localhost:8080` | `http://ipfs:8080` (interne Docker) |
| `NUXT_PUBLIC_API_BASE` | URL publique de l'API pour le frontend | `http://localhost:8002/api/v1` | `https://decision.librodrome.org/api/v1` |
| `DOMAIN` | Nom de domaine | `decision.librodrome.org` | Votre domaine |

### Generer les secrets

```bash
# Generer POSTGRES_PASSWORD
openssl rand -hex 32

# Generer SECRET_KEY
openssl rand -hex 64
```

::callout{type="warning"}
Ne commitez jamais le fichier `.env` contenant les secrets de production. Ajoutez-le au `.gitignore`.
::

## Lancement avec docker-compose

### Premier deploiement

```bash
# Se placer dans le repertoire du projet
cd /opt/libredecision

# Cloner le depot
git clone https://git.duniter.org/tools/libredecision.git .

# Configurer l'environnement
cp .env.example .env
# Editer .env avec les valeurs de production

# Construire et demarrer les services
docker compose -f docker/docker-compose.yml up -d --build

# Verifier que tous les services sont sains
docker compose -f docker/docker-compose.yml ps
```

### Services deployes

| Service | Description | Port interne | Expose |
| ------- | ----------- | ------------ | ------ |
| `postgres` | Base de donnees PostgreSQL 16 | 5432 | Non (interne uniquement) |
| `backend` | API FastAPI | 8002 | Via Traefik (`/api/*`) |
| `frontend` | Application Nuxt 4 | 3000 | Via Traefik (racine) |
| `ipfs` | Noeud IPFS kubo | 5001, 8080 | Non (interne uniquement) |

### Verifier le deploiement

```bash
# Statut des conteneurs
docker compose -f docker/docker-compose.yml ps

# Logs d'un service specifique
docker compose -f docker/docker-compose.yml logs -f backend

# Health check de l'API
curl -s https://decision.librodrome.org/api/health | jq .
```

## Migration de base de donnees (Alembic)

libreDecision utilise Alembic pour les migrations de schema PostgreSQL.

### Appliquer les migrations

```bash
# Executer les migrations dans le conteneur backend
docker compose -f docker/docker-compose.yml exec backend alembic upgrade head
```

### Verifier l'etat des migrations

```bash
docker compose -f docker/docker-compose.yml exec backend alembic current
```

### Creer une nouvelle migration

```bash
# En developpement uniquement
docker compose -f docker/docker-compose.yml exec backend alembic revision --autogenerate -m "description de la migration"
```

### Rollback d'une migration

```bash
# Revenir d'une migration
docker compose -f docker/docker-compose.yml exec backend alembic downgrade -1
```

## Seed des donnees initiales

Apres la premiere migration, vous pouvez charger les donnees de seed (documents de reference initiaux) :

```bash
# Executer le script de seed dans le conteneur backend
docker compose -f docker/docker-compose.yml exec backend python -m app.seed
```

Les donnees de seed incluent :

- La Licence G1 avec ses items
- L'Engagement Forgeron v2.0.0
- L'Engagement Comite Technique v2.0.0
- Le template de processus de Runtime Upgrade
- Les protocoles de vote par defaut avec leurs formules

## Configuration Traefik

### Configuration minimale Traefik

Si vous n'avez pas encore Traefik, voici une configuration minimale. Creez un fichier `docker-compose.traefik.yml` :

```yaml
version: "3.9"

services:
  traefik:
    image: traefik:v2.10
    restart: unless-stopped
    command:
      - "--api.dashboard=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=admin@decision.librodrome.org"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "letsencrypt:/letsencrypt"
    networks:
      - traefik

volumes:
  letsencrypt:

networks:
  traefik:
    external: true
```

```bash
docker compose -f docker-compose.traefik.yml up -d
```

### Routage

Le `docker-compose.yml` de libreDecision configure automatiquement les labels Traefik :

- **Frontend** : `Host(decision.librodrome.org)` sur le port 3000
- **Backend** : `Host(decision.librodrome.org) && PathPrefix(/api)` sur le port 8002
- Les deux utilisent le certificat Let's Encrypt (`certresolver=letsencrypt`)
- Redirection HTTP vers HTTPS automatique

### Certificat TLS

Le certificat est genere automatiquement par Traefik via Let's Encrypt (challenge TLS-ALPN-01). Assurez-vous que :

1. Le domaine pointe vers l'adresse IP du serveur (enregistrement DNS A)
2. Les ports 80 et 443 sont ouverts dans le firewall
3. L'adresse email dans la configuration Traefik est valide

## Monitoring

### Health checks

Le service PostgreSQL dispose d'un health check integre (`pg_isready`). Le backend expose un endpoint de sante :

```bash
# Health check de l'API
curl -s https://decision.librodrome.org/api/health
# Reponse attendue : {"status": "healthy"}
```

### Logs

```bash
# Suivre les logs de tous les services
docker compose -f docker/docker-compose.yml logs -f

# Logs d'un service specifique
docker compose -f docker/docker-compose.yml logs -f backend
docker compose -f docker/docker-compose.yml logs -f frontend
docker compose -f docker/docker-compose.yml logs -f postgres

# Derniers 100 lignes
docker compose -f docker/docker-compose.yml logs --tail=100 backend
```

### Metriques

Surveillez les indicateurs suivants :

| Indicateur | Commande | Seuil d'alerte |
| ---------- | -------- | --------------- |
| CPU/RAM conteneurs | `docker stats` | > 80% RAM |
| Espace disque | `df -h` | > 85% |
| Connexions PostgreSQL | `docker exec postgres psql -U libredecision -c "SELECT count(*) FROM pg_stat_activity;"` | > 80 |
| Taille base de donnees | `docker exec postgres psql -U libredecision -c "SELECT pg_size_pretty(pg_database_size('libredecision'));"` | Information |
| Statut IPFS | `docker exec ipfs ipfs id` | Erreur |

## Sauvegarde PostgreSQL

### Sauvegarde manuelle

```bash
# Dump complet de la base
docker compose -f docker/docker-compose.yml exec postgres \
  pg_dump -U libredecision -Fc libredecision > backup_$(date +%Y%m%d_%H%M%S).dump
```

### Restauration

```bash
# Restaurer un dump
docker compose -f docker/docker-compose.yml exec -T postgres \
  pg_restore -U libredecision -d libredecision --clean < backup_20260228_120000.dump
```

### Sauvegarde automatique (cron)

Ajoutez un crontab pour des sauvegardes quotidiennes :

```bash
# Editer le crontab
crontab -e

# Ajouter une sauvegarde quotidienne a 3h du matin
0 3 * * * cd /opt/libredecision && docker compose -f docker/docker-compose.yml exec -T postgres pg_dump -U libredecision -Fc libredecision > /opt/backups/libredecision_$(date +\%Y\%m\%d).dump && find /opt/backups -name "libredecision_*.dump" -mtime +30 -delete
```

Cette commande :
1. Execute un dump quotidien a 3h du matin
2. Stocke les sauvegardes dans `/opt/backups/`
3. Supprime automatiquement les sauvegardes de plus de 30 jours

## Mise a jour

### Procedure standard

```bash
cd /opt/libredecision

# 1. Tirer les dernieres images
docker compose -f docker/docker-compose.yml pull

# 2. Redemarrer les services avec les nouvelles images
docker compose -f docker/docker-compose.yml up -d --remove-orphans

# 3. Appliquer les migrations de base de donnees
docker compose -f docker/docker-compose.yml exec backend alembic upgrade head

# 4. Nettoyer les anciennes images
docker image prune -f

# 5. Verifier le deploiement
docker compose -f docker/docker-compose.yml ps
curl -s https://decision.librodrome.org/api/health
```

### Pipeline CI/CD (Woodpecker)

La pipeline Woodpecker CI automatise le deploiement lors d'un push sur `main` :

1. **test-backend** : execution des tests Python (`pytest`)
2. **test-frontend** : build du frontend Nuxt (`npm run build`)
3. **docker-backend** : construction et push de l'image Docker backend
4. **docker-frontend** : construction et push de l'image Docker frontend
5. **deploy** : connexion SSH au serveur, pull des nouvelles images, redemarrage

Les secrets CI sont configures dans l'interface Woodpecker :

| Secret | Description |
| ------ | ----------- |
| `docker_registry` | URL du registre Docker |
| `docker_username` | Identifiant du registre |
| `docker_password` | Mot de passe du registre |
| `deploy_host` | Adresse du serveur de production |
| `deploy_username` | Utilisateur SSH |
| `deploy_key` | Cle privee SSH |

## Troubleshooting

### Le backend ne demarre pas

**Symptome** : Le conteneur backend redemoarre en boucle.

```bash
# Verifier les logs
docker compose -f docker/docker-compose.yml logs backend

# Causes courantes :
# 1. DATABASE_URL incorrecte -> verifier .env
# 2. PostgreSQL pas encore pret -> verifier le health check
# 3. Migrations non appliquees -> executer alembic upgrade head
```

### Erreur de connexion a PostgreSQL

**Symptome** : `connection refused` ou `password authentication failed`.

```bash
# Verifier que PostgreSQL est sain
docker compose -f docker/docker-compose.yml exec postgres pg_isready

# Verifier les variables d'environnement
docker compose -f docker/docker-compose.yml exec backend env | grep DATABASE

# Reinitialiser le mot de passe si necessaire (attention : destructif)
docker compose -f docker/docker-compose.yml down -v  # supprime les volumes
docker compose -f docker/docker-compose.yml up -d     # recree avec le nouveau mot de passe
```

### Certificat TLS non genere

**Symptome** : Le site est inaccessible en HTTPS ou affiche un certificat invalide.

1. Verifier que le DNS A/AAAA pointe vers le serveur : `dig decision.librodrome.org`
2. Verifier que les ports 80 et 443 sont ouverts : `ss -tlnp | grep -E '80|443'`
3. Consulter les logs Traefik : `docker logs traefik 2>&1 | grep -i acme`

### IPFS ne repond pas

**Symptome** : Erreur lors de l'archivage dans le Sanctuaire.

```bash
# Verifier le statut du noeud IPFS
docker compose -f docker/docker-compose.yml exec ipfs ipfs id

# Verifier l'espace disque disponible
docker compose -f docker/docker-compose.yml exec ipfs df -h /data/ipfs

# Redemarrer le noeud IPFS
docker compose -f docker/docker-compose.yml restart ipfs
```

### Erreur WebSocket

**Symptome** : L'indicateur de connexion temps reel est rouge.

1. Verifier que Traefik supporte les WebSockets (actif par defaut)
2. Verifier le routage : le WebSocket est sur `/api/v1/ws`
3. Verifier les logs backend pour les erreurs de connexion WebSocket
