Docker 部署 MinIO:控制台打得开,程序传不上
背景与目标
MinIO 是自托管的 S3 兼容对象存储,常用来存图片、附件、备份、模型文件。相比直接买云对象存储,它跑在自己机器上、不按流量计费,接口又和 S3 一致,迁移成本低。
但它的"坑"不在安装——镜像一拉就能跑——而在两条容易混淆的边界:
端口边界:9000 是给程序调用的 S3 API,9001 是给人操作的控制台。只映射一个,另一半就一定用不了。
权限边界:能登录控制台(root 账号)不等于业务程序能上传。程序用的是 access key,权限来自它所属用户绑定的 policy,而不是 root 密码。
本文给你一份两个端口都配好的 compose,并用"建桶 → 传文件 → 生成分享链接"走通一遍,之后逐个拆掉这两条边界上最常见的五个坑。
提示:宿主 Windows (Docker Desktop),镜像走华为云。本文用
latest起步;要长期维护请把 tag 换成具体的RELEASE.*版本号,避免自动升级带来行为变化。
步骤
第 0 步:确认 Docker Desktop 已就绪
docker version
docker compose version输出版本号即 OK。
第 1 步:准备目录
mkdir minio && cd minio第 2 步:写 docker-compose.yaml
新建 docker-compose.yaml,整段复制:
services:
minio:
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/minio/minio:latest
container_name: minio
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: "minioadmin"
MINIO_ROOT_PASSWORD: "YourStrong!Passw0rd"
TZ: "Asia/Shanghai"
ports:
- "9000:9000"
- "9001:9001"
volumes:
- ./minio-data:/data
restart: unless-stopped要点:
command后面的--console-address ":9001"必须写。不写的话控制台会用随机端口,日志里那行地址每次启动都可能变。MINIO_ROOT_USER/MINIO_ROOT_PASSWORD:root 管理账号,密码至少 8 位,否则容器直接退出。数据卷
./minio-data绑定挂载到/data,桶和对象都存在这里(含.minio.sys元数据)。两个端口缺一不可:
9000给 SDK /mc,9001给浏览器。单机单盘只适合开发与个人使用,不要拿它当生产高可用方案。
第 3 步:启动
docker compose up -d看日志,确认两行地址都打印出来:
docker compose logs -f minioAPI: http://172.x.x.x:9000 http://127.0.0.1:9000
WebUI: http://172.x.x.x:9001 http://127.0.0.1:9001看到 WebUI 那行指向 9001 即就绪。
第 4 步:连接验证
先验 API 活着(PowerShell 里要用 curl.exe,curl 是 Invoke-WebRequest 的别名):
curl.exe -i http://localhost:9000/minio/health/live返回 HTTP/1.1 200 OK 即 API 正常。
再用浏览器打开控制台:
http://localhost:9001用 MINIO_ROOT_USER / MINIO_ROOT_PASSWORD 登录,然后:
左侧 Buckets → Create Bucket,名字填
demo;进
demo,Upload 一个文件;点该文件的 Share,复制生成的预签名链接,在浏览器新标签打开。
能下载到文件,说明 API、控制台、对象读写全通了。
实操截图
容器运行

访问


踩坑与解决
1. 控制台打不开,或者打开只看到一屏 XML
问题:访问 9000 只得到
<Error><Code>AccessDenied</Code>...,访问 9001 又连接失败。复现:只映射
9000:9000时,浏览器打开 9000 是 XML 报错;只映射9001:9001时,控制台页面加载不出来。根因:MinIO 有两个监听端口。9000 是 S3 API,只返回 XML 风格的协议响应;9001 才是控制台。控制台本身还要回连 API,所以两个端口都必须可达。
解决:compose 里同时映射 9000 和 9001,并显式写
--console-address ":9001";容器起来后确认日志里WebUI行是 9001。验证:浏览器打开
http://localhost:9001能看到登录页。
2. 容器一启动就退出:root 密码太短
问题:
docker compose up -d后容器反复重启。复现:
docker compose logs minio报MINIO_ROOT_PASSWORD长度不足或Unable to initialize backend。根因:MinIO 对 root 凭据有硬性要求:用户名至少 3 位、密码至少 8 位。太短直接拒绝启动,而不是降级运行。
解决:换成至少 8 位的强密码(如
YourStrong!Passw0rd),docker compose up -d重建。验证:日志不再报凭据错误,两行地址正常打印。
3. 用新 access key 上传报 AccessDenied
问题:控制台里建了用户/access key,SDK 却传不上去,返回
AccessDenied。复现:程序用新 key 调
PutObject,报Access Denied;用 root 账号同样的代码却成功。根因:MinIO 的权限来自 policy。新建用户默认没有任何 policy,access key 继承用户的权限,所以"能建 key"不代表"能读写"。用 root 跑业务则是把管理账号暴露给程序,风险更大。
解决:给用户绑内置 policy(需要宿主机装了
mc,单文件客户端):mc alias set local http://localhost:9000 minioadmin 'YourStrong!Passw0rd' mc admin user add local appuser 'AnotherStrong!Passw0rd' mc admin policy attach local readwrite --user appuser然后在控制台为该用户建 access key,程序用这对 key。旧版
mc的写法是:mc admin policy set local readwrite user=appuser验证:用新 key 跑一次
PutObject,返回 200;mc admin user info local appuser能看到readwrite。
4. 文件传上去了,浏览器直接打开却是 403
问题:对象明明在上传列表里,拼出
http://localhost:9000/demo/xxx.png访问却被拒。复现:浏览器打开对象直链,返回
AccessDenied。根因:桶默认是私有的。对象只能通过带签名的预签名 URL 访问,裸链接不带签名,服务端当然拒绝。
解决:二选一:
私有对象:用控制台的 Share,或 SDK 的
presigned_get_object生成带签名的临时链接(默认 7 天有效);公开素材:显式开放匿名读
mc anonymous set download local/demo,需要收回时mc anonymous set none local/demo。
验证:私有桶用预签名链接能下载;公开桶用直链能下载。
5. 预签名链接打不开,或者报 SignatureDoesNotMatch
问题:后端程序生成的分片上传 / 下载链接,前端拿到后打不开。
复现:链接形如
http://minio:9000/demo/xxx.png?...,浏览器无法解析minio这个主机名;换成别的域名访问又报签名不匹配。根因:预签名 URL 的主机名和端口来自生成签名时使用的 endpoint,而且签名覆盖了 Host。用容器网络里的
http://minio:9000生成,宿主机和浏览器当然访问不到;换成另一个域名/端口,签名又对不上。解决:生成签名的一方必须使用客户端实际访问的地址作为 endpoint(本地就是
http://localhost:9000,对外就是公网域名 + 对应端口)。走反向代理时保留原始Host头,不要改写。验证:前端拿到的链接主机名与浏览器地址栏一致,打开即下载。
通用坑(与 SQL Server / MySQL / Redis / MongoDB / PostgreSQL 篇相同,不再重复展开)
端口被占:9000 或 9001 已被别的程序占用,报
port is already allocated。改外部端口即可,如19000:9000、19001:9001;注意改了 9000 之后,SDK 和预签名 URL 里的端口要同步改。数据丢了:没挂数据卷,容器销毁即丢失。本教程已挂
./minio-data:/data,down再up后桶和对象仍在。容器秒退:先看
docker compose logs minio,再对照上面第 2 条。
小结与参考
用 Docker 部署 MinIO,两个端口都要映射:
9000走 S3 API,9001走控制台,并用--console-address固定控制台端口。root 账号只做管理;业务程序用独立用户 + access key,权限靠 policy 授予(
readwrite/readonly/consoleAdmin)。本篇核心矛盾:控制台能登录 ≠ 程序能上传。前者用 root 口令,后者用 access key + policy;再加上预签名 URL 里的 endpoint 必须是客户端可达地址,这两处才是 MinIO 真正的门槛。
私有桶走预签名 URL,公开素材才用
mc anonymous set download。数据落在
./minio-data,容器重建不丢。
参考:
镜像:
swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/minio/minio:latestMinIO 客户端
mc:https://min.io/docs/minio/linux/reference/minio-mc.html

