minioの件から・・・
多分このアドベントカレンダーを見てる皆様ならご存知かと思いますが、
minioのUIが廃止されたり、Dockerイメージの提供が停止したり、と色々ありました・・・
WebUIはともかく、Dockerイメージの提供が停止し、ソースだけ、有志がビルド、という状況は今後のことも踏まえてminio脱退のきっかけになりました
そんなこんなで、当方セルフホスト環境で色々遊んでいることもあり、minioをやめて別のものに移行しようぜ!ということでGarageHQを選択。
タイトルからお察しの通り、止めることを検討中なのですが、
悪くはなかったということで興味があればぜひ!
GarageHQの注意点
- GarageHQは完全なS3互換性はありません
バージョニングやオブジェクトロック関連など一部の機能は実装しない前提で動いています - 公式からの管理WebUIの欠如
公式は管理UIを提供していません。非公式のWebUIは存在しており、別のイメージとなりますが連携させることは可能です。
※この記事の中でも触れます
https://github.com/khairul169/garage-webui - 最小ノード数の考え
S3という以上、バックアップの考えは当然あるのですが、最低3ノードです。
テスト用途で1ノードでも利用は可能なのでその辺りは問題ないかなと
https://garagehq.deuxfleurs.fr/documentation/cookbook/real-world/#prerequisites - OSSではあるがライセンスはAGPL
個人利用で気にする必要はなし。
コードの改変による商用利用などを考えている場合は要注意です。
https://git.deuxfleurs.fr/Deuxfleurs/garage/src/branch/main-v1/LICENSE
GarageHQの導入
環境構成
この記事で立てる環境はローカルPC用にしておきます。
- ノード数: 1
- OS: macOS Sequoia 15.6.1
- deploy: Docker compose
- storage: local(10GB)
なお、セルフホスト環境は下記のようになっております
- ノード数: 4
- Server(2つ)
- 1つ目(node * 2)
- OS: Debian GNU/Linux bullseye 11.11 aarch64
- deploy: Docker compose
- storage: 2TB, 2TB
- 2つ目(node * 2)
- OS: Debian GNU/Linux bookworm 12.12 x86_64
- deploy: Docker compose
- storage: 2TB, 3TB
- 1つ目(node * 2)
サーバー2つに対して、2つずつノードを立てることで、HDDの故障に対してのリカバリ、サーバー自体の故障でも読み取りはできるように・・・という考えのもとです。
セットアップ
compose.ymlとgarage.toml、.envを準備します。
Garage WebUIはアクセスキーをログインなしで、時間が経っても後から確認できます。
今回はローカル環境のため認証情報は追加しませんが、認証を追加する場合はREADMEをご確認ください。
https://github.com/khairul169/garage-webui?tab=readme-ov-file#authentication
ローカル環境だけで利用できる、traefikも活用したリポジトリを用意しました。
各種ポートは非表示のため、本手順のwebアクセスは一部読み替えてもらう必要がありますが、webポートも解放しているので、簡易利用にはお勧めです![]()
https://github.com/sinsky/garage-sample
番外編でも追加で紹介しているので、最初のセットアップはRepoからの設定をお勧めしときます!
services:
garage:
image: dxflrs/garage:v2.1.0
container_name: garage
restart: always
ports:
- 3900:3900 # S3 endpoint
# - 3901:3901 # RPC endpoint(複数ノードによる連携時に必要)
# - 3902:3902 # S3 Web endpoint
- 3903:3903 # admin endpoint
environment:
GARAGE_RPC_SECRET: ${GARAGE_RPC_SECRET}
GARAGE_ADMIN_TOKEN: ${GARAGE_ADMIN_TOKEN}
GARAGE_METRICS_TOKEN: ${GARAGE_METRICS_TOKEN}
volumes:
- ./garage.toml:/etc/garage.toml
# 全てカレントディレクトリ直下に作成
- ./meta:/var/lib/garage/meta
- ./data:/var/lib/garage/data
webui:
image: khairul169/garage-webui
container_name: garage-webui
restart: unless-stopped
volumes:
- ./garage.toml:/etc/garage.toml:ro
ports:
- 3909:3909
environment:
API_BASE_URL: http://garage:3903
API_ADMIN_KEY: ${GARAGE_RPC_SECRET}
S3_ENDPOINT_URL: http://garage:3900
S3_REGION: garage
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "lmdb"
metadata_auto_snapshot_interval = "6h"
replication_factor = 1
compression_level = 2
## 使わないのでコメントアウト
# rpc_bind_addr = "[::]:3901"
# rpc_public_addr = "localhost:3901"
## rpc_secret = "" # .envで指定
[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"
root_domain = ".s3.garage"
## 使わないのでコメントアウト
# [s3_web]
# bind_addr = "[::]:3902"
# root_domain = ".web.garage"
# index = "index.html"
[admin]
api_bind_addr = "[::]:3903"
# admin_token="" # .envで指定
# metrics_token = "" # .envで指定
.envはShellで作成
cat > .env << EOF
GARAGE_RPC_SECRET="$(openssl rand -hex 32)"
GARAGE_ADMIN_TOKEN="$(openssl rand -base64 32)"
GARAGE_METRICS_TOKEN="$(openssl rand -base64 32)"
EOF
こんなファイルが生成されると思います
GARAGE_RPC_SECRET="279f0def4bbe3fc117a418ed2210fbc95c7c07c9a71d5d6658e854f1ae1d198f"
GARAGE_ADMIN_TOKEN="Nqd8of4qLpYkWyBigN1PagaxbNoAJ74UkoV3kN0+vew="
GARAGE_METRICS_TOKEN="gvmqD3rO05yT+7cwn3Mt1y3bFju78fK1yFO4nR1RS7o="
立ち上げとWebUIの表示
-
docker compose up -dで立ち上げ - WebUI(http://localhost:3909)へアクセス(repo版: http://webui.127.0.0.1.traefik.me/)
GarageHQのセットアップ
GarageHQはLayoutという設定をしないと使えません。
WebUIからの設定と、CUIからの設定、どちらも利用できます。
説明は手軽なWebUIからのみ。CUIはドキュメント見てください笑
なおこの記事では省きますが、2Way以上はノード同士の接続をする必要がありますので、
その場合についても上記のドキュメントからノードの接続を行ってください。
- Cluster画面から、表示されているNodeの「...」からAssignを選択
- ZoneやCapacityなど、必要なものを入力。
実際のストレージサイズとは別に、Capacityで必要な分だけ割り当てる、ということが可能です。
タグは作らなくてもいいです。
- Saveを押下するとApply/Revertボタンが出てくるので、Applyを押下
- レイアウト変更するぞ?と出るのでOK
- 実際にレイアウトを変更すると、ログが表示されます。ノードもZoneやCapacityが設定したものが表示されるのでご確認ください
これでセットアップは完了です。
レイアウトはバージョン管理されており、後からの変更も対応しています。
が、もちろんサイズを小さくしたりノードを増やすと多分面倒なので、その辺はドキュメント見てください
https://garagehq.deuxfleurs.fr/documentation/operations/layout/
バケットの作成とキーの作成
バケットの作成
- 左のメニューからBucketを選択し、Create Bucketを押下
- バケットの名前を決めてSubmitを押下
- バケットが作成されました。
Manageを押下すると、色々みれます(Browseでも)
ただし、アクセスキーを作成しないと何もできないので、作成していきます。

アクセスキーの作成
アクセスキーとバケットの紐付け
アクセスキーを作成しただけではアクセスできません。バケットと紐付けましょう
- Bucketsからアクセスを許可したいバケットのManageを押下
- Permissionsのタブに移動して、Allow Keyを押下
- 許可するアクセスキーと、許可する権限を選択してSubmit
- 許可されたことを確認する
- Browseから操作可能
あとはrcloneなどからアップロードなどができるようになっていますので、設定後rclone lsd garage:/などでご確認ください
GarageHQを止めようとしている理由
割とセットアップ簡単じゃん!と思われたでしょう。ハイ、簡単でした。
なんならWebUIもTinyAuthで保護しながら、s3のサブドメインで1週間ほど使っておりました...
そして、そんなある日・・・
restic(profile)で日次バックアップをしていたのですが、スナップショットの一覧を見ようとしたら、エラーが出てスナップショットの確認ができないではありませんか・・・
resticprofile -n default snapshots
rclone: 2025/11/23 00:12:51 ERROR : locks/83b73ba7f4b3683e920ce5973bcadeed57c3c122f2cdbab30c3b6280d7516edb: Post request put error: operation error S3: PutObject, exceeded maximum number of attempts, 1, https response error StatusCode: 502, RequestID: , HostID: , api error BadGateway: Bad Gateway
rclone: 2025/11/23 00:12:51 ERROR : locks/83b73ba7f4b3683e920ce5973bcadeed57c3c122f2cdbab30c3b6280d7516edb: Post request rcat error: operation error S3: PutObject, exceeded maximum number of attempts, 1, https response error StatusCode: 502, RequestID: , HostID: , api error BadGateway: Bad Gateway
Save(<lock/83b73ba7f4>) returned error, retrying after 745.565906ms: unexpected HTTP response (500): 500 Internal Server Error
rclone: 2025/11/23 00:12:51 ERROR : locks/83b73ba7f4b3683e920ce5973bcadeed57c3c122f2cdbab30c3b6280d7516edb: Post request put error: operation error S3: PutObject, exceeded maximum number of attempts, 1, https response error StatusCode: 502, RequestID: , HostID: , api error BadGateway: Bad Gateway
rclone: 2025/11/23 00:12:51 ERROR : locks/83b73ba7f4b3683e920ce5973bcadeed57c3c122f2cdbab30c3b6280d7516edb: Post request rcat error: operation error S3: PutObject, exceeded maximum number of attempts, 1, https response error StatusCode: 502, RequestID: , HostID: , api error BadGateway: Bad Gateway
Save(<lock/83b73ba7f4>) returned error, retrying after 1.055820677s: unexpected HTTP response (500): 500 Internal Server Error
rclone: 2025/11/23 00:12:52 ERROR : locks/83b73ba7f4b3683e920ce5973bcadeed57c3c122f2cdbab30c3b6280d7516edb: Post request put error: operation error S3: PutObject, exceeded maximum number of attempts, 1, https response error StatusCode: 502, RequestID: , HostID: , api error BadGateway: Bad Gateway
rclone: 2025/11/23 00:12:52 ERROR : locks/83b73ba7f4b3683e920ce5973bcadeed57c3c122f2cdbab30c3b6280d7516edb: Post request rcat error: operation error S3: PutObject, exceeded maximum number of attempts, 1, https response error StatusCode: 502, RequestID: , HostID: , api error BadGateway: Bad Gateway
Save(<lock/83b73ba7f4>) returned error, retrying after 4.894215255s: unexpected HTTP response (500): 500 Internal Server Error
rclone lsd garage:/restic-backupで確認する感じ、接続はできている・・・
しかし、copyコマンドでコピーするとエラーになったので、書き込みで問題が発生していた・・
ノードのログを確認すると、データの破損で問題が起きていた模様
garage-node1 | 2025-11-22T17:31:44.120974Z INFO garage_api_common::generic_server: 172.100.0.1 (via [::ffff:172.100.0.2]:56042) (key GK74c642553fa47075b0badf6f) HEAD /restic-backup/locks/d85f4c4951bddcf194d9e156090c26a944c51269dd239f3a6f1d277853f0b1c4
garage-node1 | 2025-11-22T17:31:44.124106Z INFO garage_api_common::generic_server: Response: error 404 Not Found, Key not found
garage-node1 | 2025-11-22T17:33:41.939079Z INFO garage_block::resync: Resync block 1f6e7205ce9e3de4: fetching absent but needed block (refcount > 0)
garage-node1 | 2025-11-22T17:33:41.942512Z WARN garage_block::resync: Could not fetch needed block 1f6e7205ce9e3de4, no node returned valid data. Checking that refcount is correct.
garage-node1 | 2025-11-22T17:33:41.942662Z ERROR garage_block::resync: Error when resyncing 1f6e7205ce9e3de4: Missing block 1f6e7205ce9e3de4: no node returned a valid block
garage-node1 | 2025-11-22T17:33:42.013632Z INFO garage_block::resync: Resync block 3baf8a5e012332da: fetching absent but needed block (refcount > 0)
garage-node1 | 2025-11-22T17:33:42.018354Z WARN garage_block::resync: Could not fetch needed block 3baf8a5e012332da, no node returned valid data. Checking that refcount is correct.
garage-node1 | 2025-11-22T17:33:42.018502Z ERROR garage_block::resync: Error when resyncing 3baf8a5e012332da: Missing block 3baf8a5e012332da: no node returned a valid block
garage-node1 | 2025-11-22T17:33:42.083633Z INFO garage_block::resync: Resync block 07f9d00d352f8d1e: fetching absent but needed block (refcount > 0)
garage-node1 | 2025-11-22T17:33:42.089358Z WARN garage_block::resync: Could not fetch needed block 07f9d00d352f8d1e, no node returned valid data. Checking that refcount is correct.
garage-node1 | 2025-11-22T17:33:42.089746Z ERROR garage_block::resync: Error when resyncing 07f9d00d352f8d1e: Missing block 07f9d00d352f8d1e: no node returned a valid block
garage-node1 | 2025-11-22T17:33:42.178459Z INFO garage_block::resync: Resync block 3b90be5385c0828b: fetching absent but needed block (refcount > 0)
garage-node1 | 2025-11-22T17:33:42.184076Z WARN garage_block::resync: Could not fetch needed block 3b90be5385c0828b, no node returned valid data. Checking that refcount is correct.
garage-node1 | 2025-11-22T17:33:42.184453Z ERROR garage_block::resync: Error when resyncing 3b90be5385c0828b: Missing block 3b90be5385c0828b: no node returned a valid block
garage-node1 | 2025-11-22T17:33:42.289532Z INFO garage_block::resync: Resync block 589e043d75261153: fetching absent but needed block (refcount > 0)
garage-node1 | 2025-11-22T17:33:42.293945Z WARN garage_block::resync: Could not fetch needed block 589e043d75261153, no node returned valid data. Checking that refcount is correct.
garage-node1 | 2025-11-22T17:33:42.294343Z ERROR garage_block::resync: Error when resyncing 589e043d75261153: Missing block 589e043d75261153: no node returned a valid block
garage-node1 | 2025-11-22T17:34:04.229131Z INFO garage_block::resync: Resync block dde2b95f5fd41739: fetching absent but needed block (refcount > 0)
そして何もわからないまま、再び使えるようになりました・・・
ログを見る感じネットワークの問題?各ノードの再起動で治ったみたいではある
その前にほかのバケットで色々遊んで消したりしたのが原因・・・?
ホストしているデバイス自体は起動し続けていたし、ほかのログも見当たらない。ディスクなども問題なし。
データの破損もぱっと見、大丈夫そうだったのでとりあえず使い続けていますが、
minioに戻るか、ほかのFSを選ぼうか悩み中・・・
最後に
Garageの紹介は以上です!いかがでしたでしょうか。
セットアップの簡単さや軽量性、分散ストレージとしての設計思想など、魅力的な面は多いかと思います。
ライセンスやら、データの整合性による問題など、悩ましい点もあったりなかったりしますが、
手軽に使える良いものかなと思います!
WebUIも公式が提供していないとはいえ、使いやすいものを提供してくださっていたので、
シンプルな構成でいきたい!って場合は結構おすすめです。
付録
自宅で利用中の環境
Garage WebUIのみPublicに公開(ただしTinyAuthによるOIDCを挟んでアクセス制御)
他のGarageノード間、およびWebUIの通信はTailscaleネットワークに参加させて通信させています。
PCのバックアップなどは全てTailscaleネットワークに入らないと利用できない構成なので、セキュリティ的にも安全な・・・はず!
ちなみに構成がデカくなるので記載していませんが、ForgejoやEnte、Linkwardenなど色々たてております。
TailscaleとTraefikに感謝!Let's Encryptの管理もいらないからね!
使用率とか
こちらもお勧めしたいのですが、Beszelによるリソースの監視も行われております。(目的が変わってきている)
resticのバックアップやEnteによる画像のアップロードを行ってましたが、使用量的にはこんなもん(初回のresticスナップショットが40GBちょい)
メモリやCPUもそんなに高性能である必要はないので、良さげです
あとはS3のバックアップということで、GoogleのCloudStorageにrestic経由でバックアップを予定
(Google Developer Programに参加しているのでクレジット消費も兼ねて)
クレジットがなかったらR2にしてたかな〜笑
番外編(traefik.meを使ったローカルのリバースプロキシ環境とwebの利用)
時間があったので、ローカルで活用できるリポジトリも用意しました!
web portもtraefikで公開しているので、http://*.web.127.0.0.1.traefik.me でアクセスできます!
docker compose up -dでコンテナを立ち上げ、バケットの作成とアクセスキーの割り当てまで終わってる前提で、
バケットのWebsite Accessを有効化して、index.htmlをアップロード。
Website Accessに記載のあるバケット名をサブドメインのリンクを開く
demo bucket --> http://demo.web.127.0.0.1.traefik.me/
見える!!(Claude作成)
ぱぱっと動かすには、やはりちょうどいい(褒めすぎ)









