本文根据官方的quick-start文档编写,着重记录、解决一些“坑点”。
Garage有一些独有的特性(后续详细介绍),初次使用可能有点难上手,但是只要你熟悉了,就和用MinIO等众多S3实现差不多了,其实我也不想多花时间去学,但是没办法啊,MinIO自己作死=-=,我知道还有一个RustFS,我也部署过,但是目前来看还是等他们发布正式版再考虑了,有些不痛不痒的小毛病用着还是有点蛋疼。
安装Docker:
apt -y update apt -y install curl curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh
创建目录新建compose文件:
mkdir /opt/garage && cd /opt/garage && nano docker-compose.yml
写入如下内容:
services:
garage:
image: dxflrs/garage:v2.1.0
container_name: garage
restart: unless-stopped
ports:
- 127.0.0.1:3900:3900
- 127.0.0.1:3901:3901
- 127.0.0.1:3902:3902
- 127.0.0.1:3903:3903
volumes:
- ./garage.toml:/etc/garage.toml
- ./meta:/var/lib/garage/meta
- ./data:/var/lib/garage/data
新建garage.toml配置文件:
nano garage.toml
写入如下内容:
metadata_dir = "/var/lib/garage/meta" data_dir = "/var/lib/garage/data" db_engine = "sqlite" replication_factor = 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.example.com" [s3_web] bind_addr = "[::]:3902" root_domain = ".web-garage.example.com" index = "index.html" [k2v_api] api_bind_addr = "[::]:3904" [admin] api_bind_addr = "[::]:3903" admin_token = "" metrics_token = ""
注意事项:
配置rpc_secret、admin_token、metrics_token请使用如下命令生成高强度的密钥:
openssl rand -hex 32
S3 API的root_domain:.s3-garage.example.com,将其更换为自己的域名,并添加通配符(*)DNS解析记录。
S3 Web的root_domain:.web-garage.example.com,将其更换为自己的域名,并添加通配符(*)DNS解析记录。
S3 Web是Garage与其他S3实现的第一个不同之处,在Garage中没有存储桶策略(bucket policy)的概念,取而代之的是“将存储桶公开为网站”的功能。
举个栗子:在MinIO/RustFS中,你可以设置一个存储桶是公开还是私有,公开就代表这个存储桶可以被匿名访问,反之亦然。而Garage创建的存储桶没有这个功能,默认情况下不能匿名访问,当你用浏览器访问的时候会提示:Forbidden: Garage does not support anonymous access yet。如果你需要存储桶可以被匿名访问,那么就需要启用“将存储桶公开为网站”的功能。
Garage还支持根据存储桶的名称来推断出实际的域名,例如你的存储桶名称是bucket.lala.im,那么这个域名将可以直接访问该存储桶。这个功能主要的目的就是可以让用户选择使用自己的域名来访问存储桶。如果用户没有自己的域名,也可以使用配置文件内指定的子域名访问存储桶:bucket.lala.im.web-garage.example.com
将存储桶公开为网站的这个功能有限:目前仅支持静态网站(不支持PHP或其他语言)、不支持目录列表(列目录)、首页在garage.toml中定义(如index.html)
启动Garage:
docker compose up -d
配置Ferron反向代理:
nano /etc/ferron.kdl
写入如下内容:
s3-garage.example.com,*.s3-garage.example.com {
proxy "http://127.0.0.1:3900/"
proxy_request_header_replace "Host" "{header:Host}"
}
*.web-garage.example.com {
proxy "http://127.0.0.1:3902/"
proxy_request_header_replace "Host" "{header:Host}"
}
注意事项:
其中s3-garage.example.com域名用于path(路径风格),*.s3-garage.example.com域名用于virtual-hosted-style(虚拟主机风格)
*.web-garage.example.com用于为用户提供子域名访问存储桶,也就是之前提到的“将存储桶公开为网站”功能。
Ferron默认会将请求发送到后端服务器之前重写“Host”标头,并在“X-Forwarded-Host”标头中保留原始的“Host”标头值。然而,Garage无法在这种配置下正常工作,必须将“Host”标头值设置为原始值。所以上面的配置添加了:proxy_request_header_replace “Host” “{header:Host}”
另外因为需要用到通配符域名证书,务必将申请证书的方式改为DNS-01,这里我配置的DNS服务商是CloudFlare,将yourkey修改成你在CloudFlare申请的key:
// auto_tls_challenge "http-01"
auto_tls_challenge "dns-01" provider="cloudflare" api_key="yourkey"
重载Ferron:
systemctl reload ferron
Garage第二个不同之处在于需要先初始化(创建布局)才能使用,执行如下命令查看并复制节点ID:
docker exec -ti garage /garage status
创建布局:-z指定区域名称,可任意指定,-c指定容量大小,单位可以用G/T。后跟上节点ID:
docker exec -ti garage /garage layout assign -z netcup -c 1T d34ebe436e60eaa8
应用布局:
docker exec -ti garage /garage layout apply --version 1
如果你后续又创建了新的布局,则这里的版本号要在其基础上+1,查看当前的布局:
docker exec -ti garage /garage layout show
如果正常的话,应该有类似下图的输出:
现在就可以实际使用了,创建一个存储桶,名称为storage:
docker exec -ti garage /garage bucket create storage
创建API密钥:
docker exec -ti garage /garage key create storage-app-key
输出类似:
==== ACCESS KEY INFORMATION ==== Key ID: GKd9e9193ca3c7fd8e35f79530 Key name: storage-app-key Secret key: 445c98c1bd96d1742a8c71f69027d0a9f1d1ae191f5690f88c4fe0d2cae7ef9e Created: 2025-12-30 11:56:51.778 +00:00 Validity: valid Expiration: never Can create buckets: false ==== BUCKETS FOR THIS KEY ==== Permissions ID Global aliases Local aliases
其中Key ID和Secret key是访问凭据,务必保存好。现在我们有了存储桶和密钥,接下来需要授予该密钥对存储桶的访问权限:
docker exec -ti garage /garage bucket allow --read --write --owner storage --key storage-app-key
查看存储桶信息:
docker exec -ti garage /garage bucket info storage
输出类似:
==== BUCKET INFORMATION ==== Bucket: 85115ec8c3c33bd9345c8c6c2cb6c4b13a3f711ef520c73243d8e1268b7fe55f Created: 2025-12-30 11:56:27.146 +00:00 Size: 0 B (0 B) Objects: 0 Website access: false Global alias: storage ==== KEYS FOR THIS BUCKET ==== Permissions Access key Local aliases RWO GKd9e9193ca3c7fd8e35f79530 storage-app-key
配置存储桶的CORS策略(跨域资源共享),这对于某些程序来说非常重要。有些程序设计的逻辑是直接通过浏览器访问S3,不经过后端处理,通常在浏览器(前端)直接访问S3会遇到CORS问题。安装awscli:
apt install awscli
配置Access Key ID与Secret Access Key,以及默认region(区域):
aws configure
流程如下:
AWS Access Key ID [None]: GKd9e9193ca3c7fd8e35f79530 AWS Secret Access Key [None]: 445c98c1bd96d1742a8c71f69027d0a9f1d1ae191f5690f88c4fe0d2cae7ef9e Default region name [None]: garage Default output format [None]: json
默认的凭据与配置文件保存位置:
~/.aws/config ~/.aws/credentials
新建一个cors.json配置文件:
nano cors.json
写入如下内容:
{
"CORSRules": [
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD", "OPTIONS"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}
]
}
应用CORS配置:
aws s3api put-bucket-cors --bucket storage --endpoint-url https://s3-garage.example.com --cors-configuration file://cors.json
查看存储桶当前的CORS配置,确保是之前应用的内容:
aws s3api get-bucket-cors --bucket storage --endpoint-url https://s3-garage.example.com
启用存储桶匿名访问(将存储桶公开为网站的功能)
docker exec -ti garage /garage bucket website --allow storage
现在可以通过下面的这个URL访问到存储桶的资源:
https://storage.web-garage.example.com
之前提到过Garge还支持根据存储桶的名称来推断域名,这样就可以让用户使用自己的域名来访问存储桶。配置起来也很简单,只需要在创建存储桶的时候,将桶的名称设置为相应的域名,例如:
docker exec -ti garage /garage bucket create bucket.lala.im
将域名bucket.lala.im添加DNS解析记录,并且在Ferron内配置好域名:
*.web-garage.example.com,bucket.lala.im {
proxy "http://127.0.0.1:3902/"
proxy_request_header_replace "Host" "{header:Host}"
}
需要注意的是,由于通配符证书只能匹配一层子域,对于这种bucket.lala.im.web-garage.example.com更深层的域名是无法匹配到的(浏览器访问会提示不安全)
所以,这两种域名访问的方式最好任选其一,或者你得加上:
*.web-garage.example.com,bucket.lala.im,bucket.lala.im.web-garage.example.com {
proxy "http://127.0.0.1:3902/"
proxy_request_header_replace "Host" "{header:Host}"
}
一般情况下是不需要搞这么复杂的,直接用*.web-garage.example.com即可。
其他有用的命令:
docker exec -ti garage /garage bucket list # 列出全部存储桶 docker exec -ti garage /garage bucket delete bucket.lala.im --yes # 删除存储桶 docker exec -ti garage /garage bucket website --deny storage # 取消存储桶匿名访问 docker exec -ti garage /garage bucket deny --read --write --owner storage --key storage-app-key # 取消API密钥对存储桶的访问权限 docker exec -ti garage /garage key list # 列出API密钥 docker exec -ti garage /garage key delete storage-app-key --yes # 删除API密钥
如果你觉得这种命令行操作非常不方便,现在有一个第三方的Web UI可供使用:
services:
garage:
image: dxflrs/garage:v2.1.0
container_name: garage
restart: unless-stopped
ports:
- 127.0.0.1:3900:3900
- 127.0.0.1:3901:3901
- 127.0.0.1:3902:3902
- 127.0.0.1:3903:3903
volumes:
- ./garage.toml:/etc/garage.toml
- ./meta:/var/lib/garage/meta
- ./data:/var/lib/garage/data
webui:
image: khairul169/garage-webui:latest
container_name: garage-webui
restart: unless-stopped
volumes:
- ./garage.toml:/etc/garage.toml:ro
ports:
- 3909:3909
environment:
API_BASE_URL: "http://garage:3903"
S3_ENDPOINT_URL: "http://garage:3900"
AUTH_USER_PASS: "username:$2y$10$/6t1aIVS5p9b4h6nmDuHg.f8C..."
其中AUTH_USER_PASS需要使用如下命令生成:
htpasswd -nbBC 10 "YOUR_USERNAME" "YOUR_PASSWORD"
如果找不到htpasswd命令请安装如下软件包:
apt install apache2-utils
这个Web UI功能有限,并且开发不积极,缺少很多关键功能,例如:创建布局、配置存储桶的CORS策略,这些功能依旧只能使用命令行操作。
荒岛
















