背景说明
记录一下准备搭建图床时的选型思路,以及为什么选择 Chevereto + Cloudflare R2 这套方案。
为什么不是继续用 PicGo + GitHub
之前用过 PicGo + GitHub + Cloudflare 的方案,这种方式在早期很省事,但越往后越容易遇到问题:
- GitHub 仓库并不适合长期堆大量图片资源
- 图片管理能力很弱,更像“上传文件”而不是“管理图片”
- 后续想做图库、随机图、分类整理时会比较别扭
- 一旦图片越来越多,迁移和维护都会变麻烦
所以这次我更希望直接把图床搭成一个“能长期用的图片管理系统”。
选择 Chevereto + Cloudflare R2 的原因
Chevereto 更接近一个完整的图片托管系统,而不是单纯的上传工具:
- 支持图片托管和管理
- 支持相册、分类、公开访问
- 更适合后续给博客做图库数据源
- 支持 API 和更完整的后台思路
- 官方支持 S3 兼容存储
Cloudflare R2 的优势主要在于:
- 兼容 S3 API
- 和对象存储生态兼容性好
- 适合作为图片底层存储
- 后续要切换展示层时,存储层可以尽量保持不变
Chevereto 官方支持 S3 Compatible 存储,而 R2 本身也提供 S3-compatible API,所以两者组合起来是成立的。
实际使用后的补充说明
这套方案在图片管理、相册组织、存储解耦这些方面,确实比之前的 PicGo + GitHub + Cloudflare 更完整,也更像一套正式的图床系统。
不过在实际使用一段时间后,我也发现一个很现实的问题:
Chevereto + Cloudflare R2 并不一定在访问速度上优于 PicGo + GitHub + Cloudflare。
在部分使用场景里,虽然图片请求已经可以命中 Cloudflare 缓存,但整体体感速度仍然未必比原先方案更快。
所以如果核心诉求是:
- 博客配图尽量快
- 更在意访问速度而不是后台管理
- 希望方案越简单越好
那么 PicGo + GitHub + Cloudflare 这类纯静态资源方案,可能依然是更适合的选择。
而 Chevereto + Cloudflare R2 更适合这些场景:
- 想要完整的图片管理后台
- 需要相册、分类、公开访问能力
- 希望把图片存储和博客系统彻底分离
- 后续可能把图床作为图库或内容源继续扩展
整体架构
上传链路:浏览器 / Chevereto 后台 ↓Chevereto ↓R2
访问链路:用户 ↓img.example.com(自定义域名) ↓Cloudflare Cache / CDN ↓R2Chevereto 部署
如果是第一次搭建,建议直接走 Docker 部署。
创建项目目录
sudo mkdir -p /opt/cheveretocd /opt/chevereto
sudo mkdir -p data/mysqlsudo mkdir -p data/images创建 docker-compose.yml
sudo vim /opt/chevereto/docker-compose.ymlservices: mariadb: image: mariadb:11 container_name: chevereto-db restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: change_this_root_password # MariaDB root 密码 MYSQL_DATABASE: chevereto # 自动创建的数据库名 MYSQL_USER: chevereto # 应用连接数据库使用的用户名 MYSQL_PASSWORD: change_this_db_password # 应用连接数据库的密码 volumes: - ./data/mysql:/var/lib/mysql # 挂载数据库数据目录,防止容器删除后数据丢失
chevereto: image: chevereto/chevereto:latest container_name: chevereto restart: unless-stopped depends_on: - mariadb # 先启动数据库,再启动 chevereto ports: - "8080:80" # 宿主机 8080 映射到容器 80 environment: CHEVERETO_DB_HOST: mariadb # 数据库主机名,对应上面的服务名 CHEVERETO_DB_USER: chevereto # 数据库用户名 CHEVERETO_DB_PASS: change_this_db_password # 数据库密码 CHEVERETO_DB_PORT: 3306 # 数据库端口 CHEVERETO_DB_NAME: chevereto # 数据库名 CHEVERETO_MAX_POST_SIZE: 128M # POST 请求大小限制 CHEVERETO_MAX_UPLOAD_SIZE: 128M # 单次上传大小限制 volumes: - ./data/images:/var/www/html/images # 本地图片目录挂载启动容器
docker compose up -d查看状态:
docker psdocker compose logs -f如果两个容器都正常运行,说明本体已经起来了。
常见问题Chevereto 容器里的 PHP 运行用户 www-data,没有权限写入 /images/ 目录
宿主机上的
./data/images被挂进容器后,容器里的www-data没法写。问题原因
Docker 挂载目录时,容器内程序以
www-data身份运行,但宿主机目录可能是root:root所以容器虽然能看到目录,但不能写入,这在 PHP 程序里很常见。解决办法
项目目录下执行:
Terminal window cd /opt/cheveretosudo chown -R 33:33 ./data/imagessudo chmod -R 775 ./data/images这里的
33:33通常就是 Debian/Ubuntu 系里www-data的 uid/gid。然后重启容器:
Terminal window docker compose restart
配置 Nginx
创建站点配置文件(推荐独立文件,便于管理):
sudo vim /etc/nginx/conf.d/chevereto.confserver { # 80 强制跳转到 443 listen 80;
server_name chevereto.example.com;
# HTTP 请求永久重定向到 HTTPS return 301 https://$host$request_uri;}
server { listen 443 ssl http2;
# 当前 HTTPS 站点绑定的域名 server_name chevereto.example.com;
# ssl证书配置 ssl_certificate /etc/nginx/ssl/chevereto.example.com/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/chevereto.example.com/privkey.pem;
# 关闭 session tickets,减少某些场景下的安全风险 ssl_session_tickets off;
# 关闭“优先使用服务端加密套件”设置 ssl_prefer_server_ciphers off;
# 日志配置 access_log /var/log/nginx/chevereto.example.com.access.log; error_log /var/log/nginx/chevereto.example.com.error.log warn;
# 上传大小限制 client_max_body_size 128M;
location / { proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1; 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 https; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port 443;
# 与后端建立连接的超时时间 proxy_connect_timeout 60s;
# 向后端发送请求的超时时间 proxy_send_timeout 60s;
# 等待后端响应的超时时间 proxy_read_timeout 60s;
# 关闭代理缓冲 proxy_buffering off; }}检查并重载 Nginx:
nginx -tnginx -s reload完成 Chevereto 首次安装
浏览器打开:https://chevereto.example.com
进入初始化页面:
完成管理员账号创建

修改为中文界面
创建 Cloudflare R2 Bucket
创建 R2 Bucket
在 Cloudflare 控制台里操作:
-
点左侧存储和数据库
-
进入 R2 对象存储
-
点击创建存储桶

- 填写存储桶信息,点击创建

创建 API 密钥
-
回到 R2 对象存储
-
点击创建 API 密钥

记录
S3 API,后续配置 Chevereto 的Endpoint时需要。
- 填写密钥信息,点击创建

创建 API 密钥,拿到:
- Access Key ID (访问密钥 ID)
- Secret Access Key (机密访问密钥)

机密访问密钥通常只显示一次。 如果没保存,后面一般只能重新生成。
公网访问地址
-
回到 R2 对象存储
-
进入新建的 images,点击设置
-
启用公共开发 URL,点击保存(生产环境建议使用自定义域名,公共开发 URL 存在限速问题)

配置 Chevereto 连接 R2
进入配置页面
仪表盘 -> 设置 -> Upload storage -> Add 存储
配置 R2 存储桶
| 项目 | 值 |
|---|---|
API | S3 Compatible |
| 名称 | Cloudflare R2 |
| 区域 | auto |
Bucket | images |
| 访问密钥 ID | Access Key ID |
| 私有访问密钥 | Secret Access Key |
Endpoint | S3 API |
| 存储容量 | 1 TB |
URL | R2 自定义域名(测试可用公共开发 URL) |

保存完成后需要勾选启用存储
部署完成后的访问推荐
图片访问域名与后台域名分离
建议:
chevereto.example.com:Chevereto 管理站img.example.com:R2 图片访问域名(开发测试阶段可临时使用 R2 公共开发 URL,正式环境更建议绑定自定义域名)
配置定时任务
Chevereto 需要 cron 来执行后台任务。
这些任务包括:
- 清理未确认用户
- 删除过期图片
- 删除待清理存储文件
- 删除废弃上传分片
- 检查更新等
可以加到系统 cron 里,例如每 5 分钟执行一次:
*/5 * * * * docker exec --user www-data chevereto app/bin/cli -C cron >/dev/null 2>&1前面 compose 里已经指定了容器为 chevereto,所以如果你的容器不是 chevereto,记得替换掉。
上传测试图验证连接
接入 R2 后,上传一张图片,检查三件事:
1. Chevereto 后台能看到图片
说明应用逻辑正常。
2. R2 bucket 里出现对象
说明上传已经真正写进对象存储。
3. 图片 URL 能直接访问

说明 Storage URL 和公网访问配置正确。
如果第 2 条成功但第 3 条失败,通常问题不在 Chevereto,而在:
- Storage URL 填错
- R2 公共访问没开
- 自定义域名没配好
- Cloudflare 代理规则不对

