Aller au contenu
medium

Garage : stockage objet S3 léger et géo-distribué

26 min de lecture

logo garage

Garage est un stockage objet S3 auto-hébergé conçu pour être simple, léger et géo-distribué. Un seul binaire Rust de ~25 Mo, ~50 Mo de RAM par nœud, aucune dépendance externe : c’est l’alternative idéale à MinIO ou Ceph RGW quand vous avez besoin de S3 sans la complexité. Ce guide déploie un cluster 3 nœuds avec réplication en 3 zones, teste l’API S3 et l’hébergement web statique, avec toutes les commandes vérifiées sur Ubuntu 24.04.

  • Architecture Garage : hash ring, zones, CRDT, quorum
  • Lab complet : cluster 3 nœuds avec KVM et systemd
  • API S3 : buckets, clés, upload/download avec AWS CLI
  • Site web statique : héberger un site directement depuis un bucket
  • Administration : layout, clés, monitoring, dépannage

Garage est un système de stockage objet distribué open source créé par l’association française Deuxfleurs. Il fournit une API compatible S3 et un module d’hébergement web statique, le tout dans un unique binaire Rust sans dépendance.

Analogie : imaginez un réseau de casiers postaux répartis dans 3 villes différentes (les zones). Quand vous déposez un colis (un objet S3) dans n’importe quel bureau, il est automatiquement copié dans les 2 autres villes. Si un bureau ferme, les 2 autres continuent à servir vos colis. Garage fonctionne exactement comme ça : sans chef central, chaque nœud est autonome.

CaractéristiqueDescription
Ultra-légerUn seul binaire (~25 Mo), ~50 Mo de RAM par nœud. Tourne sur un Raspberry Pi
Géo-distribuéConçu nativement pour répartir les données sur plusieurs sites (zones)
S3 compatibleAPI S3 standard, fonctionne avec AWS CLI, MinIO Client, Rclone, Restic
Web statiqueHéberge des sites directement depuis les buckets (port dédié)
Sans dépendancePas de Docker, pas de base de données externe, pas de JVM
CRDTRésolution de conflits sans coordination centrale
Open sourceLicence AGPLv3, développé par Deuxfleurs
CritèreGarageMinIOCeph RGWSeaweedFS
Type de stockageObjet (S3)Objet (S3)Objet + Bloc + FichierObjet + Fichier
Ressources~50 Mo RAM/nœud~1 Go RAM/nœud~4 Go RAM minimum~200 Mo RAM/nœud
ComplexitéTrès faibleFaibleÉlevéeModérée
Géo-distributionNative (zones)Site replicationRADOS multi-siteMulti-datacenter
LicenceAGPLv3AGPLv3LGPLApache 2.0
Cas d’usageSelf-hosted S3 petit/moyenS3 haute perfCloud privé completS3 + fichiers
K8s natifNon (mais utilisable)Opérateur officielRookOpérateur CSI

Garage utilise un hash ring (anneau de hachage) pour distribuer les données entre les nœuds. Chaque objet est découpé en blocs de 1 Mo (par défaut), chaque bloc est répliqué selon le replication_factor (typiquement 3).

Les composants clés :

  • Nœud : une instance Garage (un binaire, un fichier de config). Tous les nœuds sont égaux, il n’y a pas de maître.

  • Zone : un regroupement logique de nœuds (datacenter, rack, site géographique). Garage garantit que chaque copie d’un objet est stockée dans une zone différente.

  • Layout : la carte du cluster qui définit la capacité et la zone de chaque nœud. Doit être appliqué explicitement après modification.

  • Partition : subdivision du hash ring. Les données sont assignées aux partitions, elles-mêmes assignées aux nœuds selon le layout.

Garage utilise des CRDT (Conflict-free Replicated Data Types) pour les métadonnées et un quorum pour les opérations de données :

  • Écriture : réussit quand une majorité de nœuds (2/3 en replication_factor=3) confirment
  • Lecture : renvoie la donnée dès qu’un nœud répond (mode consistent : vérifie le quorum)
  • Cohérence : read-after-write garantie en mode consistent
PortProtocoleUsage
3900HTTPAPI S3 (accès client)
3901TCPRPC inter-nœuds (protocole interne)
3902HTTPHébergement web statique
3903HTTPAPI d’administration + métriques Prometheus
  • 3 machines (VMs, serveurs physiques ou même Raspberry Pi)
  • Ubuntu 24.04 (ou toute distro Linux)
  • 512 Mo de RAM et 1 vCPU minimum par nœud
  • Connectivité réseau entre les 3 nœuds (port 3901)

Le script suivant crée 3 VMs légères avec cloud-init :

#!/bin/bash
# Créer 3 VMs pour le lab Garage (1 Go RAM, 1 vCPU, 10 Go disque)
for i in 1 2 3; do
sudo qemu-img create -f qcow2 \
-b /var/lib/libvirt/images/ubuntu-24.04-cloud.img -F qcow2 \
/var/lib/libvirt/images/garage${i}.qcow2 10G
sudo virt-install --name garage${i} \
--ram 1024 --vcpus 1 \
--disk /var/lib/libvirt/images/garage${i}.qcow2 \
--os-variant ubuntu24.04 \
--cloud-init user-data=cloud-init-garage${i}.yaml \
--network network=default \
--graphics none --noautoconsole --import
done
  1. Télécharger le binaire sur chaque nœud

    Garage est un binaire statique unique. Le chemin de téléchargement utilise un répertoire x86_64-unknown-linux-musl : c’est un binaire compilé avec musl, compatible avec toutes les distributions Linux sans dépendance :

    Fenêtre de terminal
    curl -fsSL -o /usr/local/bin/garage \
    https://garagehq.deuxfleurs.fr/_releases/v2.2.0/x86_64-unknown-linux-musl/garage
    chmod +x /usr/local/bin/garage

    Vérification :

    Fenêtre de terminal
    garage --version
    # garage v2.2.0 [features: bundled-libs, consul-discovery, lmdb, ...]
  2. Générer le secret RPC partagé

    Tous les nœuds du cluster doivent partager le même secret RPC pour s’authentifier mutuellement :

    Fenêtre de terminal
    openssl rand -hex 32
    # 22c9b4a6b8225ee259298ee643512676e956a2e50702a18bc9a68141c3625e28

    Conservez cette valeur : elle sera identique dans la configuration de chaque nœud.

  3. Créer le fichier de configuration

    Sur chaque nœud, créez /etc/garage.toml (adapter rpc_public_addr à l’IP du nœud) :

    metadata_dir = "/var/lib/garage/meta"
    data_dir = "/var/lib/garage/data"
    db_engine = "lmdb"
    metadata_auto_snapshot_interval = "6h"
    replication_factor = 3
    consistency_mode = "consistent"
    compression_level = 1
    rpc_secret = "22c9b4a6b8225ee259298ee643512676e956a2e50702a18bc9a68141c3625e28"
    rpc_bind_addr = "[::]:3901"
    rpc_public_addr = "192.168.122.236:3901"
    [s3_api]
    api_bind_addr = "[::]:3900"
    s3_region = "garage"
    root_domain = ".s3.garage"
    [s3_web]
    bind_addr = "[::]:3902"
    root_domain = ".web.garage"
    [admin]
    api_bind_addr = "0.0.0.0:3903"

    Les paramètres essentiels :

    ParamètreRôle
    replication_factorNombre de copies de chaque objet (3 = tolérance 1 panne)
    consistency_modeconsistent garantit read-after-write, degraded privilégie la disponibilité
    db_enginelmdb est le plus rapide et le plus testé
    compression_level1 = compression zstd légère, 0 = désactivée
    rpc_secretClé partagée entre tous les nœuds (identique partout)
    rpc_public_addrAdresse IP accessible par les autres nœuds
  4. Créer le service systemd

    Fenêtre de terminal
    sudo mkdir -p /var/lib/garage/meta /var/lib/garage/data
    sudo tee /etc/systemd/system/garage.service << 'EOF'
    [Unit]
    Description=Garage S3-compatible object store
    After=network-online.target
    Wants=network-online.target
    [Service]
    Type=simple
    ExecStart=/usr/local/bin/garage server
    Restart=always
    RestartSec=5
    StateDirectory=garage
    ProtectSystem=full
    [Install]
    WantedBy=multi-user.target
    EOF
    sudo systemctl daemon-reload
    sudo systemctl enable --now garage

    Vérification :

    Fenêtre de terminal
    sudo systemctl status garage
    # ● garage.service - Garage S3-compatible object store
    # Active: active (running) since ...
  5. Connecter les nœuds entre eux

    Chaque nœud possède un identifiant unique généré automatiquement au premier démarrage. Récupérez-le :

    Fenêtre de terminal
    garage node id
    # 794367e3ef23feb8...@192.168.122.236:3901

    Depuis n’importe quel nœud, connectez les autres :

    Fenêtre de terminal
    garage node connect <ID_NOEUD2>@192.168.122.115:3901
    # Success.
    garage node connect <ID_NOEUD3>@192.168.122.5:3901
    # Success.

    Vérification :

    Fenêtre de terminal
    garage status
    ==== HEALTHY NODES ====
    ID Hostname Address Tags Zone Capacity DataAvail Version
    794367e3ef23feb8 garage1 192.168.122.236:3901 NO ROLE ASSIGNED v2.2.0
    d0fb5d2f74e66c45 garage2 192.168.122.115:3901 NO ROLE ASSIGNED v2.2.0
    79ab760dda72a403 garage3 192.168.122.5:3901 NO ROLE ASSIGNED v2.2.0
  6. Assigner les rôles (layout)

    Informez Garage de la capacité et de la zone de chaque nœud. Les zones garantissent que les 3 copies d’un objet sont stockées sur 3 sites différents :

    Fenêtre de terminal
    garage layout assign 794367e3 -z dc1 -c 5G -t garage1
    garage layout assign d0fb5d2f -z dc2 -c 5G -t garage2
    garage layout assign 79ab760d -z dc3 -c 5G -t garage3

    Prévisualisez puis appliquez :

    Fenêtre de terminal
    garage layout show
    garage layout apply --version 1
    Partitions are replicated 3 times on at least 3 distinct zones.
    Optimal partition size: 19.5 MB
    Usable capacity / total cluster capacity: 15.0 GB / 15.0 GB (100.0 %)
    Effective capacity (replication factor 3): 5.0 GB

    Vérification finale :

    Fenêtre de terminal
    garage status
    ==== HEALTHY NODES ====
    ID Hostname Address Tags Zone Capacity DataAvail Version
    794367e3ef23feb8 garage1 192.168.122.236:3901 [garage1] dc1 5.0 GB 7.2 GB (77.6%) v2.2.0
    d0fb5d2f74e66c45 garage2 192.168.122.115:3901 [garage2] dc2 5.0 GB 7.2 GB (77.6%) v2.2.0
    79ab760dda72a403 garage3 192.168.122.5:3901 [garage3] dc3 5.0 GB 7.2 GB (77.6%) v2.2.0

Garage sépare les concepts de bucket (espace de stockage) et de clé API (identifiant d’accès). Une clé peut avoir accès à plusieurs buckets avec des permissions différentes.

Fenêtre de terminal
# Créer une clé API
garage key create demo-app-key
==== ACCESS KEY INFORMATION ====
Key ID: GK54fbf9edf0f36dda7eeb5162
Key name: demo-app-key
Secret key: 0b2d21f3b2911a8b084a81a65ed9cf1e6ef52bf5ead54cba8a5a0c76d869113a
Fenêtre de terminal
# Créer un bucket
garage bucket create demo-bucket
# Autoriser la clé sur le bucket (read + write + owner)
garage bucket allow --read --write --owner demo-bucket --key demo-app-key

Vérification :

Fenêtre de terminal
garage bucket info demo-bucket
==== BUCKET INFORMATION ====
Bucket: 37c984b2c7c9944fa9218b0d3b413d7c671b1a81eab8c9e0389bf4a5e9a29aa3
Global alias: demo-bucket
Objects: 0
==== KEYS FOR THIS BUCKET ====
Permissions Access key Local aliases
RWO GK54fbf9edf0f36dda7eeb5162 demo-app-key

Configurez AWS CLI pour pointer vers votre cluster Garage :

Fenêtre de terminal
export AWS_ACCESS_KEY_ID="GK54fbf9edf0f36dda7eeb5162"
export AWS_SECRET_ACCESS_KEY="0b2d21f3b2911a8b084a81a65ed9cf1e6ef52bf5ead54cba8a5a0c76d869113a"
export AWS_DEFAULT_REGION="garage"
Fenêtre de terminal
# Lister les buckets
aws --endpoint-url http://192.168.122.236:3900 s3 ls
# 2026-03-01 10:35:11 demo-bucket
# Uploader un fichier
echo 'Bonjour depuis le cluster Garage !' > test.txt
aws --endpoint-url http://192.168.122.236:3900 s3 cp test.txt s3://demo-bucket/test.txt
# upload: ./test.txt to s3://demo-bucket/test.txt
# Lister les objets d'un bucket
aws --endpoint-url http://192.168.122.236:3900 s3 ls s3://demo-bucket/
# 2026-03-01 10:36:16 35 test.txt
# Télécharger un objet
aws --endpoint-url http://192.168.122.236:3900 s3 cp s3://demo-bucket/test.txt -
# Bonjour depuis le cluster Garage !
# Supprimer un objet
aws --endpoint-url http://192.168.122.236:3900 s3 rm s3://demo-bucket/test.txt
# delete: s3://demo-bucket/test.txt

Les presigned URLs permettent de partager un objet sans exposer les identifiants S3 :

Fenêtre de terminal
aws --endpoint-url http://192.168.122.236:3900 \
s3 presign s3://demo-bucket/test.txt --expires-in 3600
# http://192.168.122.236:3900/demo-bucket/test.txt?X-Amz-Algorithm=...

Le lien est valide 1 heure (3600 secondes). N’importe qui peut le télécharger sans clé API.

L’un des atouts de Garage : les données sont accessibles depuis n’importe quel nœud du cluster, quelle que soit la zone d’upload :

Fenêtre de terminal
# Upload via garage1
aws --endpoint-url http://192.168.122.236:3900 \
s3 cp test.txt s3://demo-bucket/test.txt
# Lecture via garage2 (autre zone)
aws --endpoint-url http://192.168.122.115:3900 \
s3 cp s3://demo-bucket/test.txt -
# Bonjour depuis le cluster Garage !
# Lecture via garage3 (encore une autre zone)
aws --endpoint-url http://192.168.122.5:3900 \
s3 cp s3://demo-bucket/test.txt -
# Bonjour depuis le cluster Garage !

Garage peut servir un site directement depuis un bucket, sans reverse proxy supplémentaire. Le module web écoute sur le port 3902 et route les requêtes via l’en-tête Host.

  1. Créer le bucket et activer le mode web

    Fenêtre de terminal
    garage bucket create mon-site
    garage bucket allow --read --write --owner mon-site --key demo-app-key
    garage bucket website --allow mon-site

    Vérification :

    Fenêtre de terminal
    garage bucket info mon-site
    Website access: true
    index document: index.html
    error document: (not defined)
  2. Uploader le contenu

    Fenêtre de terminal
    cat > index.html << 'HTML'
    <!DOCTYPE html>
    <html>
    <head><title>Mon site sur Garage</title></head>
    <body>
    <h1>Site hébergé sur Garage</h1>
    <p>Stockage S3 distribué, servi directement.</p>
    </body>
    </html>
    HTML
    aws --endpoint-url http://192.168.122.236:3900 \
    s3 cp index.html s3://mon-site/index.html --content-type text/html
  3. Tester l’accès

    Le module web utilise l’en-tête Host pour identifier le bucket :

    Fenêtre de terminal
    curl -H 'Host: mon-site.web.garage' http://192.168.122.236:3902/
    <!DOCTYPE html>
    <html>
    <head><title>Mon site sur Garage</title></head>
    <body>
    <h1>Site hébergé sur Garage</h1>
    <p>Stockage S3 distribué, servi directement.</p>
    </body>
    </html>
Fenêtre de terminal
# Statut du cluster (commande la plus utilisée)
garage status
# Identifiant du nœud local
garage node id
# Connecter un nœud
garage node connect <ID>@<IP>:3901
# Afficher le layout actuel
garage layout show
# Modifier la capacité d'un nœud
garage layout assign <ID> -z <zone> -c <capacité> -t <tag>
# Appliquer les changements de layout
garage layout apply --version <N>
# Annuler les changements en attente
garage layout revert
Fenêtre de terminal
# Lister les buckets
garage bucket list
# Créer un bucket
garage bucket create mon-bucket
# Infos détaillées
garage bucket info mon-bucket
# Activer l'hébergement web
garage bucket website --allow mon-bucket
# Supprimer un bucket (doit être vide)
garage bucket delete --yes mon-bucket
Fenêtre de terminal
# Lister les clés
garage key list
# Créer une clé
garage key create ma-cle
# Afficher les infos (avec secret)
garage key info --show-secret ma-cle
# Autoriser une clé sur un bucket
garage bucket allow --read --write mon-bucket --key ma-cle
# Révoquer l'accès
garage bucket deny --read --write mon-bucket --key ma-cle
# Supprimer une clé
garage key delete --yes ma-cle
Fenêtre de terminal
# Lancer un scrub de vérification des données
garage repair scrub start
# Vérifier le statut du scrub
garage repair scrub status
# Snapshot des métadonnées
garage meta snapshot

Le rpc_secret dans garage.toml est la seule barrière d’authentification entre les nœuds du cluster. Si cette clé est compromise, un attaquant peut rejoindre le cluster.

PratiqueDescription
Secret RPC fortGénérer avec openssl rand -hex 32 (256 bits)
Fichier protégéchmod 600 /etc/garage.toml — seul root doit le lire
Réseau dédiéIsoler le port RPC (3901) sur un VLAN interne
Pare-feuExposer uniquement 3900 (S3) aux clients, bloquer 3901/3903 de l’extérieur
TLSPlacer un reverse proxy TLS devant le port 3900 pour les clients
Clés APICréer une clé par application avec les droits minimaux
SymptômeCause probableSolution
garage status affiche 0 nœudsLe service ne tourne passudo systemctl status garage puis vérifier les logs : journalctl -u garage
Nœud visible mais NO ROLE ASSIGNEDLayout non appliquégarage layout assign + garage layout apply --version N
Connection refused sur le port 3901Pare-feu ou mauvaise IPVérifier rpc_public_addr et ufw allow 3901/tcp
Unauthorized sur l’API S3Clé API incorrecte ou pas de permissionVérifier garage key info et garage bucket allow
Upload OK mais lecture échouereplication_factor=3 avec moins de 3 nœuds upVérifier garage status, attendre la re-synchronisation
rpc_secret mismatch dans les logsSecrets RPC différents entre nœudsVérifier que /etc/garage.toml a le même rpc_secret partout
Layout version mismatchLayout non propagéRelancer garage layout apply --version N
Site web renvoie 404Pas de fichier index.html ou web non activégarage bucket website --allow et vérifier le upload
Fenêtre de terminal
# Logs du service
sudo journalctl -u garage -f --no-pager
# Statut détaillé du cluster
garage status
# Vérifier l'intégrité des données
garage repair scrub start
garage repair scrub status
# Vérifier les métriques (si admin API activée)
curl http://localhost:3903/metrics
ComposantMinimum (test)Recommandé (production)
Nœuds33+ (au moins 3 zones)
RAM par nœud512 Mo1-2 Go
CPU1 vCPU2+ vCPU
Réseau100 Mbps1 Gbps+ entre zones
DisqueHDDSSD pour les métadonnées, HDD pour les données
  • Séparer métadonnées et données sur des disques différents en production : SSD pour metadata_dir (accès aléatoire intense), HDD pour data_dir (accès séquentiel).
  • Une clé API par application : ne pas partager les clés entre services.
  • Nommer les tags pour identifier les nœuds : garage layout assign <ID> -t "paris-srv01".
  • 3 zones minimum avec replication_factor = 3 pour une vraie tolérance aux pannes de site.
  • Snapshots automatiques des métadonnées : metadata_auto_snapshot_interval (6h par défaut) crée des sauvegardes du fichier LMDB.
  • compression_level = 1 (zstd rapide) est un bon compromis. Passez à 0 pour du contenu déjà compressé (images, vidéos, archives).
  • block_size = "1M" (défaut) convient à la plupart des workloads. Augmentez à 4M ou 64M pour des fichiers volumineux.
  • consistency_mode = "degraded" si la disponibilité prime sur la cohérence stricte (lectures servies même si un nœud est down, sans vérifier le quorum).
  • Garage = stockage S3 ultra-léger : un seul binaire Rust, ~50 Mo de RAM, aucune dépendance.
  • Géo-distribution native : les zones garantissent que chaque copie est stockée sur un site différent.
  • Pas de maître : tous les nœuds sont égaux, le hash ring distribue les données.
  • 3 commandes suffisent pour un cluster : node connect, layout assign, layout apply.
  • API S3 standard : fonctionne avec AWS CLI, MinIO Client, Rclone, Restic.
  • Hébergement web intégré : servez un site statique directement depuis un bucket.
  • Idéal pour : sauvegardes, stockage Nextcloud, artefacts CI/CD, sites statiques, partage de fichiers.
  • Pas adapté pour : stockage bloc (VM), systèmes de fichiers POSIX, workloads haute performance à faible latence.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn