
De MinIO à Garage : benchmark, migration et premiers retours en production
Comment nous avons benchmarké Garage face à MinIO, migré un data lake de production sans interruption, et ce que nous en retirons après un mois de coexistence et deux semaines en source de vérité.
De la théorie à la production
Depuis février 2026, le dépôt public minio/minio est en mode archive et l'éditeur pousse ses utilisateurs vers AIStor, un produit propriétaire dont le ticket d'entrée se compte en dizaines de milliers d'euros. La question n'est plus de savoir s'il faut migrer ; c'est de choisir où aller, et comment le faire proprement.
Nous avions posé le sujet sur le papier dans notre précédent article (« Stockage objet auto-hébergé : comprendre les alternatives à MinIO »). Depuis, nous sommes passés à la pratique sur l'infrastructure d'un client — un éditeur français de pilotage et reporting téléphonie B2B. Le chemin a été progressif :
- Fin mars : benchmark complet en sandbox dédiée, sur des charges Parquet réalistes.
- Début avril : déploiement de Garage en parallèle de MinIO sur le cluster K3s du client, en mode lecture/écriture cohabitante.
- 7 mai : cutover — Garage devient la source de vérité, MinIO bascule en miroir J-1 read-only.
- Aujourd'hui : environ deux semaines en production, zéro intervention, recette complète.
Voici le retour d'expérience, étape par étape.
Cet article s'adresse à toute équipe qui se pose la question d'une migration ou d'un choix de techno pour un data lake S3 self-hosted. Les chiffres et la méthodo sont reproductibles ; le code du benchmark est public sur github.com/DataKhiProject/benchmark-garage.
Étape 1 — Le benchmark : ce que nous avons mesuré
Avant d'engager une migration, il fallait des chiffres. Nous avons construit un banc d'essai reproductible — le code est public sur github.com/DataKhiProject/benchmark-garage — avec un objectif simple : comparer Garage v2.2.0 et MinIO RELEASE.2025-09-07 sur des charges proches de celles d'un data lake d'analytics.
Méthodologie
- 43 860 fichiers Parquet représentant ~5 Go, organisés en 60 « clients » fictifs sur 731 jours (2023–2024). On reproduit la structure typique d'un data lake partitionné par client / date.
- 14 requêtes couvrant trois familles : lectures point (Q1–Q4), lectures larges (Q5–Q9), glob listing (Q10) et écritures unitaires ou concurrentes (W1–W4).
- 5 runs par requête plus un warmup non comptabilisé.
- Deux topologies testées : cluster 3 nœuds (Garage replication_factor=2, MinIO erasure coding 3+3) et mono-nœud (Garage RF=1, MinIO standalone).
- Côté client : DuckDB (httpfs) pour les lectures Parquet, boto3 pour les écritures, derrière un load balancer nginx. Bande passante mesurée : 940 Mbits/sec.
- Infra : trois VMs Proxmox (Xeon E5-2683v3, 8 vCPU / 8 Go RAM / disque HDD), conteneurs Docker bridés à 2 CPU et 4 Go de RAM chacun pour simuler une contrainte « petit budget ».
Résultats — cluster 3 nœuds
| Workload | MinIO (p50) | Garage (p50) | Gain Garage |
| Lecture petit fichier (Q1) | 904 ms | 148 ms | 6,1× |
| Lecture client volumineux 1 mois (Q4) | 1 620 ms | 760 ms | 2,1× |
| Lecture 2 ans / 730 fichiers (Q5) | 33 463 ms | 21 388 ms | 1,6× |
| Glob 43 860 fichiers (Q10) | 21 137 ms | 4 577 ms | 4,6× |
| Écriture 20 lignes (W1) | 750 ms | 101 ms | 7,4× |
| Écriture 10 workers concurrents (W3) | 1 887 ms | 201 ms | 9,4× |
- Lectures : Garage 1,6× à 6,1× plus rapide
- Écritures : Garage 3× à 9,4× plus rapide
- Listing : Garage 4,6× plus rapide
- CPU conteneur observé : MinIO 82–124 % en moyenne, Garage 13–25 % — un facteur 5 à 6 en faveur de Garage
Résultats — mono-nœud
Le mono-nœud écrase les gains spectaculaires du cluster (plus de parallélisation), mais Garage reste systématiquement devant :
| Workload | MinIO (p50) | Garage (p50) | Gain Garage |
| Lecture point (Q1) | 935 ms | 469 ms | 2,0× |
| Lecture 2 ans (Q5) | 22 348 ms | 18 526 ms | 1,2× |
| Glob 43 860 fichiers (Q10) | 11 039 ms | 5 269 ms | 2,1× |
| Écriture unitaire (W1) | 83 ms | 30 ms | 2,8× |
| Écriture 10 000 lignes (W2) | 1 667 ms | 456 ms | 3,7× |
CPU moyen : MinIO 57 %, Garage 20 % — environ 3× moins pour Garage.
Sur la portée de ces chiffres
Le banc d'essai a tourné sur une config représentative d'un cluster économique ou d'un K3s mutualisé (HDD, conteneurs 2 CPU / 4 Go). Sur du NVMe bare-metal avec plus de RAM, l'écart en lecture pure se resserrera ; en revanche, les gains de CPU et d'écritures concurrentes tiennent à l'architecture (CRDT vs erasure coding stateful) et restent structurels. Autrement dit : Garage devant, partout — l'amplitude varie avec le matériel.
Le test de résilience qui surprend
Nous avons stoppé un nœud du cluster pendant une lecture lourde (Q5, 7,3 millions de lignes). Premier constat : MinIO continue de répondre via le load balancer, Garage retourne des 502 Bad Gateway.
Mauvais point pour Garage ? Non. En sortant le LB nginx et en interrogeant directement n'importe quel nœud Garage encore debout, la requête passe : Garage utilise une redirection interne entre nœuds, qui suppose qu'on ne masque pas la topologie derrière un LB classique. Une fois le pattern d'accès ajusté, 100 % de la cohérence est préservée (les 43 860 fichiers attendus sont bien retournés, en 162 secondes sans erreur).
À retenir : Garage n'a pas le même modèle de proxy que MinIO. Configurer une infrastructure 3 nœuds derrière un nginx « classique » casse la résilience. C'est documenté, mais facile à manquer.
Étape 2 — Pourquoi nous avons retenu Garage
Au-delà des chiffres, trois éléments ont pesé :
- Empreinte CPU / mémoire divisée par 3 à 6. Sur des machines bare-metal modestes, c'est ce qui fait la différence entre un nœud qui peut accueillir le reste de la stack (orchestrateur, base, monitoring) et un nœud dédié au stockage.
- Modèle CRDT côté Garage (vs erasure coding stateful côté MinIO). Plus simple à raisonner, plus tolérant aux pannes asymétriques, et particulièrement adapté à un déploiement géo-distribué multi-datacenter.
- Souveraineté et licence. Garage est porté par Deuxfleurs (collectif français/européen), AGPLv3, sans surprise contractuelle. Sur un sujet « stockage de données client » en B2B, c'est un argument que nos prospects entendent.
Pour mémoire : nous avions aussi évalué SeaweedFS et Ceph. SeaweedFS cible plutôt les très gros volumes orientés filesystem, et Ceph reste opérationnellement disproportionné pour un déploiement de cette taille. Garage l'emporte aussi sur un point qui pèse en B2B : une communauté et un mainteneur ancrés en Europe, avec qui un échange direct reste possible.
Le seul vrai contre que nous avons identifié : Garage est plus jeune, son écosystème (clients tiers, outils de monitoring) est moins fourni. Cela se sent surtout côté observabilité — il faut composer.
Étape 3 — Le déploiement en parallèle de MinIO (avril)
Plutôt que de basculer du jour au lendemain, nous avons déployé Garage en coexistence sur le K3s du client. Pendant un mois (8 avril → 7 mai), MinIO restait la source de vérité, mais nous avions :
- Un nœud Garage v2.2.0 actif dans le namespace, avec ses 4 buckets miroirs alimentés par
rclone. - Des scripts de test (DuckDB httpfs, boto3, MinIO client) qui interrogeaient les deux backends en parallèle.
- Des outils SFTP downstream (livraison de rapports vers les clients finaux) connectés indifféremment à l'un ou l'autre.
Cette phase nous a permis de :
- Valider la conformité SigV4 des différents clients S3 du code base (Airflow, ETL Python, DuckDB, SFTPGo). On a découvert pendant cette phase que DuckDB et boto3 exigeaient une région explicite (
region='garage') que MinIO acceptait par défaut. Trouvé en sandbox, corrigé en sandbox. - Mesurer la dérive réelle entre les deux backends pendant des semaines : zéro divergence non expliquée.
- Tester le pattern de rollback avant d'en avoir besoin.
C'est cette phase de coexistence qui a vraiment de-risqué le cutover. Au moment de basculer, nous n'avions presque plus d'inconnues.
Étape 4 — Le cutover (7 mai)
Le client est un éditeur français de pilotage et reporting téléphonie pour PME et ETI. Donnée traitée : ~70 organisations clientes finales, plusieurs centaines de milliers d'appels par jour, agrégés dans un data lake S3 puis chargés dans un PostgreSQL en étoile pour des dashboards Power BI. Volume au moment de la migration : ~1,8 Go de Parquet sur 4 buckets (raw, staging, debug, rapports).
Approche : coexistence puis bascule
Nous voulions zéro downtime et un rollback instantané si le moindre signal d'instabilité apparaissait. La séquence retenue :
- Re-sync final (la veille) :
rclonecomplet MinIO → Garage. 105 588 fichiers / 2,0 GiB validés identiques sur les 4 buckets. - Bascule code : un seul
Variable.set("minio_secret_key", clé_garage)côté Airflow et un changement d'endpoint dans les configs. Nom de variable conservé pour zéro churn de code — l'astuce a permis de ne toucher à aucun script. - Cutover : exécution en fenêtre nocturne, redémarrage des composants Airflow, observation des premiers runs ETL quotidiens.
- Mirror inverse : un CronJob Kubernetes synchronise désormais Garage → MinIO chaque soir à 18h UTC. MinIO devient un mirror J-1 read-only, conservé pour rollback ultra-rapide pendant les premières semaines.
Durée du cutover lui-même : environ deux heures, dont l'essentiel sur la fenêtre re-sync + redémarrage.
Le seul incident notable
Au premier run post-cutover, certaines requêtes S3 ont échoué avec une erreur de signature SigV4. La région explicite manquait sur un client S3 que nous n'avions pas exercé en sandbox (le code path de génération des rapports mensuels, qui ne tournait pas en pré-prod). Un commit ciblé a réglé l'affaire en moins de trente minutes — typiquement le genre de friction qu'un benchmark ne révèle pas et qu'une coexistence bien menée fait remonter tôt.
Étape 5 — Deux semaines en source de vérité
Environ deux semaines après le cutover, nous avons fait la recette complète avant de décommissionner MinIO. Voici les indicateurs :
| Indicateur | Résultat |
| DAG runs Airflow (phases 1 et 2 quotidiennes) | 100 % success, zéro intervention humaine |
| Erreurs S3/SigV4 dans les logs scheduler | 0 sur toute la période |
| Cohérence Garage ↔ MinIO mirror (rclone check) | 99,6 % à 100 % selon bucket — les écarts portent uniquement sur J-1 (le mirror tourne à 18 h UTC, normal) |
| Fichiers historiques (pré-cutover) | bit-pour-bit identiques entre Garage et MinIO, vérifié sur échantillon |
| Tests métier (règles de dédoublonnage, FK, distribution des statuts) | tous passent (35+ checks) |
Le CronJob de mirror Garage → MinIO tourne désormais depuis plusieurs nuits consécutives en moins de 17 minutes par run. La stack Garage tourne sur un seul nœud K3s, consomme une fraction de ce que prenait MinIO en RAM, et n'a pas eu besoin de la moindre intervention.
Ce que le benchmark n'a pas dit, mais que la production a révélé
- Sur lectures DuckDB en partition daily (le cas d'usage typique du data lake reporting), la différence n'est pas perçue par l'utilisateur final. Les deux solutions sont assez rapides pour que le temps utilisateur soit dominé par le rendu du dashboard, pas par le S3.
- Sur écritures concurrentes pendant un backfill (re-traitement de plusieurs mois d'historique en parallèle sur des clients lourds), c'est là que Garage a fait la différence : pas de contention observable, là où MinIO commençait à présenter des pics de latence en pré-prod.
- Sur le cycle de retour à la stabilité après un redémarrage de pod, Garage est nettement plus rapide à reprendre la main. Inintéressant en run normal, salvateur en exploitation.
- Côté observabilité, Garage est plus discret que MinIO (pas d'UI riche pré-intégrée). Nous avons branché un Garage WebUI tiers, mais c'est moins « out-of-the-box ».
Côté business : qu'est-ce que ça change concrètement ?
Au-delà des courbes de latence et des graphes CPU, la migration apporte des effets tangibles côté exploitation et côté commercial :
- Empreinte serveur allégée : le nœud K3s consacre désormais une fraction de ses ressources au stockage, là où MinIO en captait une grosse part. Cela libère du budget CPU/RAM pour le reste de la stack (orchestrateur, base, monitoring) sans changer de machine. À moyen terme, c'est une marge de manœuvre concrète pour ajouter des clients ou des cas d'usage sans réinvestir.
- Réversibilité retrouvée : la nouvelle licence MinIO faisait peser un risque de dépendance lent mais réel. Garage sous AGPLv3, porté par un collectif européen, ferme cette inquiétude pour de bon. Il devient possible de documenter formellement la chaîne de licence et la gouvernance dans une réponse à appel d'offres ou une revue de sécurité.
- Crédibilité sur la souveraineté : pour un éditeur SaaS B2B dont les clients finaux sont des PME et ETI françaises soumises aux exigences de protection des données, pouvoir présenter une stack 100 % open source et hébergée en France est un vrai argument commercial — et un facilitateur côté DSI client.
- Posture de résilience claire : le miroir Garage → MinIO en J-1, et à terme un cluster géo-distribué multi-zones, fournissent un plan de continuité que MinIO seul (sur un nœud) ne donnait pas, sans pour autant doubler la facture cloud.
Ces effets sont rarement visibles dans une démo, mais ils pèsent dans la durée. Ce sont eux qui font qu'une migration techniquement réussie devient aussi une migration commercialement utile.
Et à plus gros volume ?
Question légitime : nos 1,8 Go en production, c'est modeste. Trois éléments cadrent la réponse pour qui veut monter à plusieurs To :
- Architecture pensée pour le scale horizontal : chaque nœud Garage ajouté augmente proportionnellement la capacité et le débit, sans rééquilibrage coûteux. Le modèle CRDT évite les points de contention typiques de l'erasure coding stateful.
- Test glob (Q10) sur 43 860 fichiers : c'est l'opération qui fait souffrir un système de stockage objet quand le namespace grossit. Garage y est 4,6× plus rapide que MinIO sur notre infra — un écart qui découle de l'algorithme de listing, pas du cache, et qui se maintient mécaniquement à mesure que le namespace grossit.
- À l'échelle du To, la principale variable cesse d'être le moteur S3 et devient le sous-système disque (HDD vs SSD vs NVMe, RAID, cache). Garage tournant en mode événementiel asynchrone, le coût d'I/O reste plus contenu que sur un MinIO qui doit maintenir ses bandes d'erasure en cohérence stricte.
Garage est conçu pour scaler ; le benchmark le confirme à petite échelle, la production confirme la stabilité. Une cible géo-distribuée multi-zones reste accessible à un budget bare-metal modéré pour plusieurs To utilisables, ce qui en fait une solution réaliste pour un éditeur qui veut maîtriser son infrastructure de bout en bout.
Limites connues et bonnes pratiques
Pour qui voudrait reproduire la démarche, quelques pièges à connaître :
- Pas de load balancer applicatif « bête » devant Garage en cluster. Privilégier soit l'accès direct multi-endpoint côté client, soit un proxy qui respecte la redirection interne.
- Mono-nœud = SPOF. Garage en mono-nœud apporte gain CPU et simplicité, mais il faut un plan de sauvegarde solide (mirror J-1, snapshot LMDB, ou Backup Agent de l'hébergeur). Dans notre cas, la coexistence avec MinIO en lecture seule fait office de filet de sécurité pendant la phase de consolidation.
- Région SigV4 explicite dans tous les clients S3 :
region='garage'ou équivalent. Indispensable pour DuckDB, boto3, et tout outil qui contrôle strictement la signature SigV4. - Variables Airflow : conserver les noms d'origine (ici
minio_secret_key) pour éviter une refonte du code. Stocker la clé MinIO en backup (minio_secret_key_minio_backup) pour rollback instantané. - Écritures inter-datacenter : si l'on déploie un cluster géo-distribué multi-zones, les écritures synchrones cross-DC sont plus lentes qu'en LAN. Anticiper pour les jobs en hot path.
- Phase de coexistence longue (au moins 2–4 semaines) : c'est elle qui rentabilise la migration. Court-circuiter cette étape transfère le risque sur le cutover.
En résumé
| Point | Verdict |
| Performances brutes | Garage devant, 2× à 9× selon workload |
| Empreinte ressources | 3× à 6× moins de CPU côté Garage |
| Résilience | OK des deux côtés à condition de respecter le modèle Garage (accès direct) |
| Souveraineté | Garage AGPLv3, gouvernance européenne |
| Maturité écosystème | MinIO devant, mais Garage suffit pour la plupart des cas |
| Migration | Faisable en un cycle court (benchmark mars → coexistence avril → cutover mai), sans downtime |
| Profil économique | Compatible avec un budget bare-metal modeste, du mono-nœud au cluster géo-distribué |
Pour des charges de type data lake analytics, multi-client, où l'on cherche à la fois la performance, la maîtrise du coût et la conformité européenne, Garage est notre référence. Le benchmark a confirmé l'intuition, la coexistence a de-risqué la mise en œuvre, la production valide la décision — deux semaines sans incident, sur une charge réelle, avec zéro intervention.
Vous menez un projet de data lake auto-hébergé, une migration MinIO, ou un déploiement S3 souverain ? Datakhi accompagne ses clients sur l'ensemble de la chaîne — choix de techno, benchmark sur leurs données, migration progressive, exploitation. Discutons-en.
Pour aller plus loin
- Article précédent : « Stockage objet auto-hébergé : comprendre les alternatives à MinIO »
- Code du benchmark : github.com/DataKhiProject/benchmark-garage — suite Python + Docker reproductible, 14 requêtes, génération de données synthétiques calibrée sur du data lake d'analytics
- Documentation Garage : garagehq.deuxfleurs.fr