Guide d'installation en production — Repod¶
Ce guide couvre l'installation complète de Repod en production sur un serveur
Linux. Repod est une application unique — un backend FastAPI, une base
PostgreSQL, une interface web React — capable de servir des paquets .deb,
.rpm et Alpine .apk selon la variable REPO_FORMAT. Il n'existe plus
d'« édition APT » ou « édition RPM » séparées. Suivez chaque étape dans l'ordre.
Table des matières¶
- Prérequis système
- Ports réseau
- Récupération du projet
- Configuration
- Structure des volumes de données
- Premier démarrage
- Configuration GPG
- Vérification du bon fonctionnement
- Configuration du pare-feu
- Activation au démarrage (systemd)
- Pile RPM autonome
- Sauvegarde automatique
- Mise à jour de l'application
- Résolution des problèmes courants
1. Prérequis système¶
Système d'exploitation¶
- Debian 11/12 ou Ubuntu 22.04/24.04 LTS (système hôte — Repod tourne dans des conteneurs et peut servir n'importe quelle combinaison APT/RPM/APK)
- Accès root ou sudo
Logiciels requis¶
| Logiciel | Version minimale | Vérification |
|---|---|---|
| Docker Engine | 24.0 | docker --version |
| Docker Compose | 2.20 (plugin) | docker compose version |
| Git | 2.x | git --version |
| OpenSSL | 1.1+ | openssl version |
Utiliser le plugin Compose v2
Utilisez docker compose (plugin v2), pas l'ancienne commande docker-compose.
Installation de Docker (si absent)¶
sudo apt-get remove -y docker docker-engine docker.io containerd runc
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker
Ressources matérielles minimales¶
| Ressource | Minimum | Recommandé |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 3 Go | 6 Go+ |
| Disque | 20 Go | 100 Go+ |
La RAM doit couvrir PostgreSQL, ClamAV (clamd charge ~800 Mo de signatures
dans le conteneur backend) et Grype. L'espace disque doit couvrir tous les
binaires de paquets sous repos/pool/ et repos/rpm/, plus le volume Docker
postgres_data. Dimensionnez selon le volume de paquets attendu.
2. Ports réseau¶
Le docker-compose.yaml fourni démarre REPO_FORMAT=all par défaut — les
serveurs de dépôts APT/APK et RPM sont tous deux démarrés.
| Port | Service | Exposition |
|---|---|---|
80 |
depot-apt — dépôts APT (.deb) et Alpine (.apk) |
LAN ou public (clients apt/apk) |
8080 |
depot-rpm — dépôts RPM (.rpm) |
LAN ou public (clients dnf/zypper) |
3003 |
Interface web (frontend-ui) |
Interne ou VPN |
8000 |
API backend (backend-api) |
Reverse proxy uniquement — jamais exposé directement |
| — | PostgreSQL (repod-db) |
Réseau Docker interne uniquement, non publié |
Si vous n'avez besoin que d'un seul format de paquet, définissez REPO_FORMAT
en conséquence (apt, rpm ou apk) et retirez le service de dépôt inutilisé
de docker-compose.yaml — voir
Démarrage rapide — Étape 2.
3. Récupération du projet¶
sudo mkdir -p /opt/repod
sudo chown $USER:$USER /opt/repod
cd /opt/repod
git clone https://github.com/getautoflow/repod .
Vérifier la structure du projet :
ls /opt/repod
# Attendu : backend/ frontend/ repos/ docker-compose.yaml .env.example backend.env.example
4. Configuration¶
4.1 Fichier .env (ports, mot de passe de la base et URLs frontend)¶
Contenu type — remplacez repo.example.com par votre domaine ou adresse IP :
# Lier sur loopback quand un reverse proxy gère le trafic externe
BIND_HOST=127.0.0.1
# URLs publiques intégrées au bundle frontend à la construction
# REACT_APP_API_URL doit rester vide (appels relatifs /api/v1/...)
REACT_APP_API_URL=
REACT_APP_REPO_URL=https://repo.example.com
REACT_APP_RPM_REPO_URL=https://repo.example.com:8080
# Mapping des ports
BACKEND_PORT=8000
FRONTEND_PORT=3003
APT_PORT=80
RPM_REPO_PORT=8080
# PostgreSQL — doit correspondre à DATABASE_URL dans backend.env
POSTGRES_PASSWORD=<sortie de openssl rand -hex 24>
4.2 Fichier backend.env (secrets, connexion base de données, configuration)¶
Générer les secrets requis (un openssl rand -hex 32 par secret) :
# ── Base de données ─────────────────────────────────────────────────────────
# Le mot de passe doit correspondre à POSTGRES_PASSWORD dans .env
DATABASE_URL=postgresql://repod:<même-mot-de-passe-que-.env>@db:5432/repod
# ── Format de dépôt ─────────────────────────────────────────────────────────
REPO_FORMAT=all
# ── Sécurité ────────────────────────────────────────────────────────────────
JWT_SECRET_KEY=<sortie de openssl rand -hex 32>
JWT_EXPIRE_MINUTES=60
SETTINGS_ENCRYPTION_KEY=<sortie de openssl rand -hex 32>
WEBHOOK_SECRET=<sortie de openssl rand -hex 32>
# ── Environnement ───────────────────────────────────────────────────────────
ENV=production
APP_VERSION=v1.2.0
# ── Reverse proxy ────────────────────────────────────────────────────────────
TRUSTED_PROXIES=127.0.0.1
CORS_ORIGINS=https://repo.example.com
Obligatoires en production
JWT_SECRET_KEY et WEBHOOK_SECRET sont obligatoires. L'application
refuse de démarrer en production si une valeur par défaut ou vide est détectée.
Sécuriser les fichiers :
4.3 Créer le compte admin¶
Aucun compte admin n'est créé au démarrage. Une fois la pile lancée (section 6), créez le premier admin via l'assistant de configuration :
curl -X POST http://localhost:8000/api/v1/setup/ \
-H "Content-Type: application/json" \
-d '{"admin_username":"admin","admin_password":"VotreMotDePasse!"}'
Politique de mot de passe : - Minimum 8 caractères - Au moins une majuscule - Au moins un chiffre ou caractère spécial
POST /api/v1/setup retourne 409 si un admin existe déjà — l'assistant ne
peut être exécuté qu'une seule fois.
Pré-provisionner un admin (optionnel, déploiements automatisés)
Définir ADMIN_USERNAME et ADMIN_PASSWORD_HASH (hash bcrypt, $ doublés
en $$) dans backend.env avant le premier démarrage :
Si ADMIN_PASSWORD_HASH est absent, vide ou invalide, aucun admin n'est
créé et l'assistant de configuration reste disponible — c'est le
comportement sûr par défaut.
Protéger l'assistant de configuration (optionnel)
Définir SETUP_TOKEN=<sortie de openssl rand -hex 32> dans backend.env
pour exiger un header X-Setup-Token sur POST /api/v1/setup jusqu'à la
création du premier admin. GET /api/v1/setup/status reste public dans
tous les cas.
5. Structure des volumes de données¶
mkdir -p /opt/repod/repos/{audit,clamav-db,conf,db,dists,gnupg,grype-db,\
imports,logs,manifests,package-index,pool,rpm,apk,security,staging/incoming,staging/quarantine}
| Répertoire | Contenu |
|---|---|
audit/ |
Journaux d'audit JSONL en ajout seul (un fichier par jour) |
clamav-db/ |
Base de signatures ClamAV (~800 Mo) |
conf/ |
Configuration des distributions reprepro (mode APT) |
db/ |
Base de données interne reprepro (mode APT) |
dists/ |
Arborescences de distributions APT, servies par depot-apt |
rpm/ |
Arborescences de distributions RPM (<codename>/<arch>/repodata/), servies par depot-rpm |
apk/ |
Dépôts Alpine (<codename>/main/<arch>/APKINDEX.tar.gz), servis par depot-apt sous /apk/ |
gnupg/ |
Trousseau GPG partagé entre le backend et les conteneurs de dépôt |
grype-db/ |
Cache de la base de données CVE Grype |
imports/ |
Répertoire de travail pour les imports sync/mirror |
logs/ |
Journaux de téléchargement Nginx (analysés pour les statistiques) |
manifests/ |
Manifestes JSON par paquet et index.json central |
pool/ |
Binaires de paquets .deb / .rpm (stockage canonique) |
security/ |
Décisions CVE, caches CISA KEV et EPSS |
staging/ |
Zone d'arrivée des uploads et quarantaine |
Les comptes utilisateurs, l'index des manifestes, les données d'inventaire et
les empreintes de clés SSH ne sont pas stockés sous /repos/ — ils vivent
dans PostgreSQL, dans le volume Docker postgres_data géré par le service db.
Aucune création manuelle de répertoire n'est nécessaire pour la base de
données ; docker compose up crée le volume automatiquement et Alembic
applique les migrations au démarrage du backend.
6. Premier démarrage¶
Avec REPO_FORMAT=all (par défaut), Docker construit et démarre cinq conteneurs :
| Conteneur | Rôle | Port |
|---|---|---|
repod-db |
PostgreSQL 16 — base de données applicative | (interne) |
depot-apt |
Nginx — dépôts APT (.deb) + Alpine (.apk) |
80 |
depot-rpm |
Nginx — dépôts RPM (.rpm) |
8080 |
backend-api |
FastAPI, pipeline de sécurité | 8000 |
frontend-ui |
Interface web React + Nginx | 3003 |
Vérifier que tous les conteneurs tournent et sont sains :
Suivre les logs de démarrage :
Le premier démarrage est plus lent
ClamAV charge ~800 Mo de signatures au premier démarrage (jusqu'à 2 minutes
— le endpoint de santé peut indiquer "clamav": false pendant ce temps).
PostgreSQL effectue également sa propre initialisation et Alembic applique
les migrations. C'est normal.
7. Configuration GPG¶
Les index de dépôt doivent être signés GPG pour que les clients APT/RPM/APK vérifient les paquets.
- Ouvrir
http://VOTRE_HOTE:3003dans un navigateur - Se connecter avec le compte admin créé via l'assistant de configuration
- Aller dans Paramètres → GPG
- Cliquer sur Générer une clé GPG
- Renseigner le nom réel et l'adresse e-mail, puis cliquer Générer
docker exec backend-api gpg --homedir /repos/gnupg \
--batch --gen-key <<EOF
%no-protection
Key-Type: RSA
Key-Length: 4096
Name-Real: Repod Repository
Name-Email: [email protected]
Expire-Date: 2y
%commit
EOF
# Initialiser les distributions après génération de la clé
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"VotreMotDePasse!"}' | jq -r .access_token)
curl -X POST http://localhost:8000/api/v1/distributions/init \
-H "Authorization: Bearer $TOKEN"
8. Vérification du bon fonctionnement¶
# 1. Tous les conteneurs sont en cours d'exécution et sains
docker compose ps
# 2. Sonde de vivacité de l'API
curl -s http://localhost:8000/health/live
# Attendu : {"status":"ok"}
# 3. Health check complet (inclut PostgreSQL, ClamAV et Grype)
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"VotreMotDePasse!"}' | jq -r .access_token)
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8000/health | jq .
# 4. Interface web accessible
curl -s -o /dev/null -w "%{http_code}" http://localhost:3003
# Attendu : 200
# 5. Serveur de dépôt APT/APK accessible
curl -s -o /dev/null -w "%{http_code}" http://localhost:80
# Attendu : 200
# 6. Serveur de dépôt RPM accessible (REPO_FORMAT=rpm/both/all)
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080
# Attendu : 200
# 7. Swagger UI désactivé en production
curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/docs
# Attendu : 404
9. Configuration du pare-feu¶
sudo ufw enable
sudo ufw allow 22/tcp # SSH — ne pas s'oublier
sudo ufw allow 3003/tcp # Interface web
sudo ufw allow 80/tcp # Clients APT / APK
sudo ufw allow 8080/tcp # Clients RPM (à omettre si REPO_FORMAT=apt)
# Port 8000 — NE PAS exposer publiquement, utiliser un reverse proxy
sudo ufw status numbered
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-port=3003/tcp # Interface web
sudo firewall-cmd --permanent --add-port=80/tcp # Clients APT / APK
sudo firewall-cmd --permanent --add-port=8080/tcp # Clients RPM
# Port 8000 — NE PAS exposer, utiliser un reverse proxy
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
Ne jamais exposer le port 8000 directement
L'API backend (port 8000) ne doit être accessible que via un reverse proxy TLS. Une exposition publique directe transmet identifiants et tokens en clair. Voir Reverse proxy →
10. Activation au démarrage (systemd)¶
cat > /etc/systemd/system/repod.service << 'EOF'
[Unit]
Description=Repod Package Repository Manager
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/repod
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable repod
sudo systemctl start repod
11. Pile RPM autonome¶
Si vous n'avez besoin que d'un dépôt RPM et souhaitez une isolation complète
(base PostgreSQL propre, réseau propre, noms de conteneurs propres — aucun état
partagé avec un déploiement APT/all), utilisez le fichier compose dédié au
lieu de la section 6 :
Cela démarre repod-db-rpm (PostgreSQL), depot-rpm, backend-api-rpm
(REPO_FORMAT=rpm) et frontend-ui-rpm, sur les ports RPM_REPO_PORT (défaut
8080), BACKEND_PORT (défaut 8001) et FRONTEND_PORT (défaut 3004).
À exécuter de manière autonome — ne pas le fusionner avec docker-compose.yaml
via -f.
12. Sauvegarde automatique¶
Repod fournit une sauvegarde intégrée déclenchable par un administrateur ou
planifiée via le scheduler (backup_daily, configurable dans settings.json).
Elle archive le dump PostgreSQL (pg_dump) ainsi que pool/, manifests/,
audit/, security/, gnupg/ et settings.json.
Sauvegarde manuelle via l'API¶
Planification¶
Dans Paramètres → Sauvegardes, activer backup.enabled et définir
l'heure quotidienne (par défaut 04:30).
Sauvegarde des secrets¶
Les éléments suivants doivent être sauvegardés séparément, hors serveur, dans un coffre-fort sécurisé :
Perte de la clé GPG
La perte de la clé GPG privée (repos/gnupg/) rend impossibles les futures
signatures du dépôt. Sauvegardez-la de manière sécurisée et séparée des
sauvegardes automatiques.
13. Mise à jour de l'application¶
cd /opt/repod
# 1. Sauvegarder avant la mise à jour (via l'API, voir section 12)
# 2. Récupérer les nouvelles versions
git fetch origin
git pull origin main
# 3. Reconstruire et redémarrer
docker compose up -d --build
# 4. Vérifier le bon démarrage
docker compose ps
curl http://localhost:8000/health/live
Alembic applique automatiquement les migrations PostgreSQL au démarrage du backend — aucune étape manuelle de migration n'est nécessaire.
Rollback en cas de problème¶
14. Résolution des problèmes courants¶
Le conteneur backend-api ne démarre pas¶
Symptôme : docker compose ps affiche Exited pour backend-api.
Causes fréquentes :
DATABASE_URLabsent ou mot de passe ne correspondant pas àPOSTGRES_PASSWORD—db/engine.pylève uneRuntimeErrorJWT_SECRET_KEYouWEBHOOK_SECRETnon défini ou utilisant la valeur par défaut dansbackend.env$non doublé en$$dans le hash bcrypt debackend.env- Erreur de syntaxe dans
backend.env(espaces autour des=)
Le conteneur repod-db ne démarre pas / migrations en échec¶
Cause fréquente : le volume postgres_data contient des données d'une
version incompatible. Vérifiez les logs Alembic pour l'erreur de migration
exacte avant toute action — ne supprimez jamais le volume sans sauvegarde
préalable.
L'interface web affiche une erreur de connexion à l'API¶
Cause fréquente : CORS_ORIGINS dans backend.env ne correspond pas
exactement à l'URL réellement utilisée par les navigateurs (protocole, port,
domaine).
Les clients rejettent le dépôt (erreur de signature)¶
Symptôme : apt update / dnf makecache / apk update échoue avec une
erreur de signature.
Solution : Si aucune clé n'est listée, générez une clé GPG (section 7) puis re-publiez le dépôt via l'interface web.
ClamAV ne se met pas à jour¶
Espace disque insuffisant¶
df -h /opt/repod/
du -sh /opt/repod/repos/pool/* /opt/repod/repos/rpm/* | sort -rh | head -20
ls /opt/repod/repos/staging/quarantine/
Réinitialiser le mot de passe admin¶
Si le mot de passe admin est perdu, voir la procédure d'urgence dans
CLAUDE.md — génération d'un hash bcrypt et mise à jour
directe de la table users en PostgreSQL :
docker exec backend-api python3 -c "
import bcrypt
print(bcrypt.hashpw(b'NouveauMotDePasse!', bcrypt.gensalt(12)).decode())
"
docker exec repod-db psql -U repod -d repod -c \
"UPDATE users SET hashed_password = '<hash>' WHERE username = 'admin';"
Consulter tous les logs en temps réel¶
docker compose logs -f
docker compose logs -f backend-api
docker compose logs -f frontend-ui
docker compose logs -f depot-apt
docker compose logs -f depot-rpm
Annexe : Commandes de référence rapide¶
# Démarrer en production
docker compose up -d
# Arrêter tous les services
docker compose down
# Redémarrer un service spécifique
docker compose restart backend-api
# Voir l'état des services
docker compose ps
# Health check
curl http://localhost:8000/health/live
# Voir les logs
docker compose logs -f backend-api
# Accéder au shell d'un conteneur
docker exec -it backend-api bash
docker exec -it repod-db psql -U repod -d repod