在寻找自建图床或私有云存储方案时,MinIO 往往是首选,但对于个人开发者或中小团队来说,Garage 是一个更轻量、更高性能且配置更灵活的替代方案。它基于 Rust 编写,资源占用极低,且完美兼容 AWS S3 协议。
本文将详细记录如何在 1Panel 面板上,利用 Docker Compose 部署一套生产环境可用的 Garage 对象存储服务。本文将记录并解决常见的配置坑点(如 CORS 跨域、WebUI 鉴权、域名绑定等),打造一个既安全又好用的私有 OSS。
s3.example.com —— S3 API 接口 (上传/管理工具用)img.example.com —— 公开访问域名 (博客/网站引用图片用)admin.example.com —— Web 管理后台 (WebUI)garage.toml)/opt/garage/config。garage.toml,填入以下内容:docker-compose.yml)重点:
garage.toml 才能自动读取 admin_token。$ 符号必须写成 $$。在 1Panel 创建编排 garage-stack,内容如下:
启动编排,确保两个容器都显示为“运行中”。
刚启动的 Garage 处于“未分配角色”状态,必须初始化。
查看节点 ID:
在 1Panel 终端或 SSH 中执行:
复制显示的 Node ID,例如 a8795c63e0c82b0b。
此时刷新 WebUI,状态应变为绿色的 Healthy。
为了作为图床使用,我们需要创建一个桶,并允许公开访问。
在 WebUI 中点击 Buckets -> Create Bucket,命名为 photo。
这是让图片可以通过 https://img.example.com/xxx.jpg 直接访问的关键。
也可以在 webui 中直接设置 Alias 并勾选开启 Website。

blog-key)。photo 桶的 Read/Write 权限。
docker exec -it garage /garage bucket allow --read --write --key blog-key photo我们需要在 1Panel 的“网站”功能中建立三个反向代理:
| 域名 | 代理地址 | 用途 |
|---|---|---|
s3.example.com | http://127.0.0.1:3900 | S3 API (PicList/Lsky 填这个) |
admin.example.com | http://127.0.0.1:3903 | WebUI 管理后台 |
img.example.com | http://127.0.0.1:3902 | 公开图片访问 (注意端口是 3902) |
记得为所有域名申请并启用 HTTPS 证书。
安全提示:使用 Host 模式时,建议在 1Panel 的防火墙或云服务商的安全组中,仅放行 Nginx 的 80/443 端口。不要直接对外暴露 Garage 的 3900-3903 端口,让所有请求都必须经过 Nginx 反向代理,这样更安全。
如果你在博客或其他网站引用图片,浏览器会拦截跨域请求。我们需要在 img.example.com 的 Nginx 配置中强制放行 CORS。
在 1Panel 网站设置 -> 配置文件 中,找到 location / 块,添加如下:
location / {
# CORS 配置
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
# 其余配置
}
}
保存并重载 Nginx。
现在服务已完全就绪,在图床工具中填写如下配置:
https://s3.example.comphotogarage (默认)https://img.example.commetadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "lmdb"
# 单节点部署必须设为 1;多节点请按需调整
replication_factor = 1
# 32字节的随机密钥 (openssl rand -hex 32 生成)
rpc_secret = "c9a4b8d7e6f5a1c2b3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6"
rpc_bind_addr = "[::]:3901"
rpc_public_addr = "127.0.0.1:3901"
[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"
root_domain = "s3.example.com" # 填你的API域名
[s3_web]
bind_addr = "[::]:3902"
root_domain = "img.example.com" # 填你的公开访问域名
index = "index.html"
[admin]
api_bind_addr = "[::]:3903"
metrics_token = "change_me_please" # metrics 抓取用的 token
admin_token = "change_me_please" # 管理员 token,WebUI 会自动读取
services:
garage:
image: dxflrs/garage:v2.2.0 # 较新版本
container_name: garage
restart: always
network_mode: host # Host 模式,直接使用宿主机端口
volumes:
- /opt/garage/config/garage.toml:/etc/garage.toml
- /opt/garage/meta:/var/lib/garage/meta
- /opt/garage/data:/var/lib/garage/data
garage-webui:
image: khairul169/garage-webui:latest
container_name: garage-webui
restart: always
network_mode: host # WebUI 也用 Host 模式方便通信
volumes:
# 必须挂载配置文件,否则 WebUI 无法自动鉴权
- /opt/garage/config/garage.toml:/etc/garage.toml:ro
environment:
API_BASE_URL: "http://127.0.0.1:3903"
S3_ENDPOINT_URL: "http://127.0.0.1:3900"
# 原生登录配置:用户名:Bcrypt哈希 (注意 $$ 转义)
# 生成命令: python3 -c "import bcrypt; print(bcrypt.hashpw(b'你的密码', bcrypt.gensalt()).decode())"
# 下面示例密码是 123456
AUTH_USER_PASS: 'admin:$$2b$$12$$zm7W20msXJc5fVgj.fjGR.GfGQS2M1abvQf5k.aWZTQBZGv4QexpC'
docker exec -it garage /garage status
# 分配 500GB 逻辑空间 (不会立即占用硬盘)
docker exec -it garage /garage layout assign -z dc1 -c 500G <你的NodeID>
# 应用更改
docker exec -it garage /garage layout apply --version 1
# 1. 绑定域名别名 (让 Garage 知道这个域名对应 photo 桶)
docker exec -it garage /garage bucket alias set photo img.example.com
# 2. 开启网站模式 (允许匿名 GET 请求)
docker exec -it garage /garage bucket website --allow photo --index index.html