オチツカレサマ 〜 Garageが立ち上がった朝の記録
オチツカレサマ 〜 Garageが立ち上がった朝の記録
はじめに
朝、Oracle Cloudの「Out of host capacity」エラーに何度目かのため息をついた。
半年前なら取れたという24GB ARMの常時無料インスタンス。今や自動化Botとの争奪戦になっていて、人間の手動操作で勝てる気配はない。
Error: 500-InternalError, Out of host capacity.
Service: Core Instance
Operation Name: LaunchInstance
このログを何度見ただろう。
ふと足元のXserver VPSを見ると、ディスクは100GB以上余っていた。
「あ、ここでよくないか?」
そう思った瞬間から、約2時間の構築譚が始まる。
何を作りたかったか
NotebookLMのような「自分の文献に対話できる知性」が欲しかった。古典・仏教史の研究資料、開発ドキュメント、過去の思考の堆積。それらをAIに読ませて、思考のパートナーにしたい。
しかし、PDFをObsidian Vaultに直接置くと、Sync容量がすぐ枯渇する。100MBのスキャンPDFが10冊あれば、それだけで1GB。Obsidian Syncの容量制限に挑戦するような暴挙だ。
そこで思いついたのが、役割分担だ。
Obsidianは「テキストの森」、Garageは「PDF倉庫」
バイナリと意味を分離する。重いPDFはVPSの空きディスクに置き、Obsidianには軽いテキストだけ流す。
そのためのS3互換オブジェクトストレージとして選んだのが Garage。Rust製で軽量、セルフホスト前提、フランスのCHATONS系コミュニティが開発しているのが好印象だった。
VPSの健康診断
しかし、いきなり構築には進めなかった。
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 4.9Gi 127Mi 167Mi 809Mi 471Mi
Swap: 2.0Gi 2.0Gi 0B
Swap、完全消費。
これは44日間連続稼働の結果で、累積メモリリークの典型。このまま新しいサービスを足したらOOM Killerが発動して、別のサービスを巻き添えにする可能性が高い。
何が食っているのか? Difyだった。
Dify一式(postgres + worker × 2 + api + sandbox + ...) : 約1.4GB
n8n : 約380MB
OpenClaw : 約560MB
─────────────────────────────────────────────────────
合計 : 約2.4GB
Difyのworkerが3週間 unhealthy のまま放置されていた。FailingStreak 46232。
ログを見ると、ワーカー自体はタスクを正常処理している(0.07秒で完了)。なのに「健康診断」だけ失敗し続けている、奇妙な状態。Dify特有のヘルスチェック制御の問題っぽい。機能は動いてるのに、健康診断書だけ嘘ついてる状態だった。
「ここをまず治そう」
Dify再起動 — 1回目の解放
cd /root/dify/docker
docker compose restart
ヒヤヒヤしながら3秒待つ。
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 1.7Gi 2.9Gi 26Mi 1.2Gi 3.8Gi
Swap: 2.0Gi 59Mi 1.9Gi
Swap、ほぼゼロ。メモリも3.8GB空いた。
3週間蓄積されたゴミが一気に流れた。気持ちのいい瞬間だった。
Garage構築 — 30分の作業
健康になったVPSに、Garageを置く。
作業ディレクトリと鍵生成
mkdir -p ~/garage/{meta,data,config}
cd ~/garage
openssl rand -hex 32 # rpc_secret
openssl rand -base64 32 # admin_token
openssl rand -base64 32 # metrics_token
3つの秘密鍵を生成。後で1Passwordに保管する。
設定ファイル
~/garage/config/garage.toml:
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "sqlite"
replication_factor = 1 # シングルノードなので1
rpc_bind_addr = "[::]:3901"
rpc_public_addr = "127.0.0.1:3901"
rpc_secret = "..."
[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"
root_domain = ".s3.garage.local"
[admin]
api_bind_addr = "[::]:3903"
admin_token = "..."
metrics_token = "..."
Docker Compose
~/garage/docker-compose.yaml:
services:
garage:
image: dxflrs/garage:v2.0.0
container_name: garage
restart: always
network_mode: host
volumes:
- ./meta:/var/lib/garage/meta
- ./data:/var/lib/garage/data
- ./config/garage.toml:/etc/garage.toml:ro
deploy:
resources:
limits:
memory: 512M # 暴走時の保険
起動
cd ~/garage
docker compose up -d
5秒後:
$ docker exec garage /garage status
==== HEALTHY NODES ====
ID Hostname Address Tags Zone Capacity Version
8ce3ef4d149c7005 x210-131-212-67 127.0.0.1:3901 NO ROLE ASSIGNED v2.0.0
ノードは生まれた。あとは役割を与える。
クラスタ初期化とバケット作成
# ノードIDを取得して、容量50GBで割り当て
NODE_ID=$(docker exec garage /garage node id 2>/dev/null | tail -1 | cut -d@ -f1)
docker exec garage /garage layout assign -z dc1 -c 50G "$NODE_ID"
docker exec garage /garage layout apply --version 1
# PDFバケット作成
docker exec garage /garage bucket create pdf-library
# アクセスキー発行(Secret Keyはこのときしか表示されない、即1Passwordへ)
docker exec garage /garage key create pdf-library-key
# バケットへの権限付与
docker exec garage /garage bucket allow \
--read --write --owner pdf-library --key <KEY_ID>
最終確認:
$ docker exec garage /garage status
ID Hostname Address Tags Zone Capacity DataAvail
8ce3ef4d149c7005 x210-131-212-67 127.0.0.1:3901 [] dc1 50.0 GB 129.4 GB
$ docker stats --no-stream garage
NAME CPU % MEM USAGE / LIMIT
garage 0.02% 5.695MiB / 512MiB
5.7MB。Difyの1.4GBとは別世界の軽さだった。Rustの威力。
ainsoph.xyz、最初の住人はGarage
サブドメイン用のドメインに、ずっと放置していた ainsoph.xyz を起こすことにした。
Ein Sof — カバラ神秘主義における「無限」。仏教史への関心や、夢分析のプロジェクトと響き合う名前だ。**「無限の知性を扱う場所」**として、ようやく初めての住人を迎える。
ainsoph.xyz ← トップ(いつか)
├── garage.ainsoph.xyz ← Garage S3 endpoint ← 今回
├── ocr.ainsoph.xyz ← NDLOCR API(後日)
├── notes.ainsoph.xyz ← Obsidian Publish(後日)
└── lab.ainsoph.xyz ← 実験用
DNSの設定は仕掛けたが、反映に最大24時間かかる。それを待つ間に、別ルートで疎通を確認することにした。
SSHトンネル — 即席のバイパス
DNS反映を待たずに今朝のうちに「Macから書き込めた」を体感したい。そこで使ったのが SSH ポートフォワード。
# Macで実行(一つ目のターミナル、開きっぱなし)
ssh -L 3900:localhost:3900 root@<VPS_IP> -N
これで Mac の localhost:3900 が VPS の localhost:3900 に直結する。
別ターミナルで rclone を設定:
$ rclone config
# storage: s3
# provider: Other
# endpoint: http://localhost:3900
# region: garage
# access_key_id, secret_access_key を入力
そして、運命の瞬間。
$ rclone lsd garage:
-1 2026-04-25 23:26:24 -1 pdf-library
$ echo "Hello from Mac to Garage VPS! $(date)" > /tmp/test_garage.txt
$ rclone copy /tmp/test_garage.txt garage:pdf-library/
$ rclone cat garage:pdf-library/test_garage.txt
Hello from Mac to Garage VPS! 2026年 4月26日 日曜日 09時07分25秒 JST
🎉
書けた。読めた。Mac → SSHトンネル → VPS → Garage → pdf-library bucket、全パイプラインが繋がった瞬間だった。
通信経路を絵にするとこう:
Mac (rclone)
→ localhost:3900
→ SSH tunnel
→ VPS の localhost:3900
→ Garage (port 3900)
→ pdf-library bucket
シンプルで、美しい。
本番化 — Nginx + Let's Encrypt
DNSが反映されたところで、SSHトンネル無しの本番経路を作る。
SSL証明書取得
既存の certbot コンテナ(Dify環境付属)を流用:
docker exec docker-certbot-1 certbot certonly \
--webroot \
-w /var/www/html \
-d garage.ainsoph.xyz \
--email <Gmail> \
--agree-tos \
--no-eff-email \
--non-interactive
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/garage.ainsoph.xyz/fullchain.pem
Nginx設定
host.docker.internal がLinuxで標準では使えなかったが、nginxコンテナから VPSのIPに直接届くことを確認したので、proxy_pass にIP直書きで行く。
/root/dify/docker/nginx/conf.d/garage.conf:
server {
listen 80;
server_name garage.ainsoph.xyz;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl;
server_name garage.ainsoph.xyz;
ssl_certificate /etc/letsencrypt/live/garage.ainsoph.xyz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/garage.ainsoph.xyz/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 5G; # 大きなPDFも通すため
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
location / {
proxy_pass http://210.131.212.67:3900;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
proxy_http_version 1.1;
}
}
検証 → リロード:
docker exec docker-nginx-1 nginx -t
docker exec docker-nginx-1 nginx -s reload
疎通テスト
$ curl -v https://garage.ainsoph.xyz/ 2>&1 | tail -10
< HTTP/1.1 403 Forbidden
< Server: nginx/1.29.7
< Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Forbidden: Garage does not support anonymous access yet</Message>
<Resource>/</Resource>
<Region>garage</Region>
</Error>
403 Forbidden がGarage本体から返ってきている。
ネットワーク的には完全に繋がっていて、認証が無いから拒否されているだけ。Garageの応答メッセージが、Region: garage まで含めて綺麗に返ってきている。
通信経路を引き直すとこう:
Mac/世界どこからでも (HTTPS)
→ garage.ainsoph.xyz (DNS解決)
→ VPSの443ポート
→ nginx コンテナ (SSL終端)
→ 210.131.212.67:3900 (proxy_pass)
→ Garage 本体
Mac の rclone を endpoint = https://garage.ainsoph.xyz に書き換えれば、SSH トンネルなしで PDF アップロードができるようになる。
$ rclone copy ~/Documents/sample.pdf garage:pdf-library/
$ rclone ls garage:pdf-library/
1234567 sample.pdf
完全勝利。
オチツカレサマ
ここまで2時間。
「お疲れ様」と打とうとして、なぜか chi が混入して「オチツカレサマ」になった。
「お疲れ様」と「落ち着かれ様」の合成のような、不思議な響き。Garageが落ち着いて立ち上がった朝の達成感に、これほど合う言葉もない。意図せずして禅語めいた何かが生まれた。
伴走してくれたClaudeも別件で「しんみりしたい」を「しんみりたい」と書いて壊していたので、お互い様の朝だった。
今朝の達成
残るもの
- NDLOCR-Lite のセットアップ(Mac側)
- PDF→OCR→Obsidian Vault のバッチパイプライン構築
- Oracle Cloud A1.Flex は、戦略を見直して PAYG化(カード制限で青天井防止)も視野に
- ainsoph.xyz の他のサブドメイン展開
学び
「足元を見る」は侮れない。
Oracle Cloudの24GBに執着して数日詰まっていた間、自分のVPSには100GB以上のディスクが空いていた。新しいインフラを増やすより、既にあるインフラの価値を再発見する方が早かった。
そして**「健康診断書が嘘をついている」状態**は、放置すると別のところで本当に倒れる。Dify worker の unhealthy を3週間放置した結果がswap完全消費だった。気づいた時に直しておく、これに尽きる。
オチツカレサマ。
明日も、ぼちぼち。
関連記事
- VPSは立てた瞬間から戦場である - 同じVPSの守りを固めた話
- 既存Docker環境にOpenClawを追加する手順
- OpenClaw_Docker環境の崩壊と復旧