# Forgejo 部署与排障经验库 > 光湖语言世界代码仓库(guanghulab.com)从 CVM 迁移到广州轻量云服务器的完整踩坑记录。 > 换服务器或遇到类似问题时,先看这个文档,不用重新推演。 --- ## 核心:一键部署脚本 优先使用仓库里的 `scripts/smart-deploy-forgejo.sh`。 这个脚本做了智能检测(Docker可用性、COS配置、已有Forgejo状态),一条命令跑完全部部署流程。 换服务器时的第一步。 --- ## 二进制部署(Docker 不可用的替代方案) 当 Docker pull 失败(429限流或被墙)时,直接下载 Gitea 二进制: ```bash # 下载 Gitea 1.23.7(Forgejo 的底层引擎) wget -O /usr/local/bin/gitea https://dl.gitea.com/gitea/1.23.7/gitea-1.23.7-linux-amd64 chmod +x /usr/local/bin/gitea # 创建专用用户 adduser --disabled-login --gecos "" git # 目录结构 mkdir -p /opt/forgejo/custom/conf /opt/forgejo/data chown -R git:git /opt/forgejo ``` ### app.ini 配置 `/opt/forgejo/custom/conf/app.ini`: ```ini APP_NAME = 光湖 · Forgejo RUN_USER = git RUN_MODE = prod [database] DB_TYPE = sqlite3 PATH = /opt/forgejo/data/forgejo.db [server] DOMAIN = guanghulab.com HTTP_PORT = 3001 ROOT_URL = https://guanghulab.com/ HTTP_ADDR = 127.0.0.1 LANDING_PAGE = explore OFFLINE_MODE = false [service] DISABLE_REGISTRATION = false REQUIRE_SIGNIN_VIEW = false [mailer] ENABLED = false [session] PROVIDER = file [log] MODE = console LEVEL = info ROOT_PATH = /opt/forgejo/data/log ``` > **注意**:`ROOT_URL` 必须用 `https://`,否则克隆链接会显示 `http://`。 ### systemd 服务 `/etc/systemd/system/forgejo.service`: ```ini [Unit] Description=Forgejo (Git with a cup of tea) After=network.target [Service] ExecStart=/usr/local/bin/gitea web -c /opt/forgejo/custom/conf/app.ini --work-path /opt/forgejo Restart=always User=git Group=git WorkingDirectory=/opt/forgejo [Install] WantedBy=multi-user.target ``` ```bash systemctl daemon-reload systemctl enable --now forgejo systemctl status forgejo ``` --- ## 常见问题与排查流程 ### 1. Forgejo 拒绝以 root 运行 **现象**:启动报错 "Forgejo/Gitea refuses to run as root" **原因**:Forgejo/Gitea 安全机制禁止 root 运行。 **解决**: - 直接用 `sudo -u git /usr/local/bin/gitea ...` 启动 - 或者在 systemd 里设 `User=git` --- ### 2. Git 推送失败:HTTP 413 Request Entity Too Large **现象**: ``` error: RPC failed; HTTP 413 curl 22 The requested URL returned error: 413 send-pack: unexpected disconnect while reading sideband packet ``` **排查流程**: 1. 确认 Nginx 配置中有 `client_max_body_size`:`cat /etc/nginx/sites-available/guanghulab` 2. 默认 Nginx 限制 1MB,Git 推送的 packfile 远超此值 3. 检查配置是否写在正确的 server block 里(certbot 可能创建了多个配置) **解决**: ```bash # 在 location / 块中添加 client_max_body_size sudo sed -i '/location \/ {/a \ client_max_body_size 100M;' /etc/nginx/sites-available/guanghulab # 重载 Nginx sudo systemctl reload nginx ``` **验证**:`git push` 不再报 413 即为成功。 --- ### 3. 仓库数据损坏:推送成功但网页不显示文件 **现象**: - `git push` 返回成功(`[new branch] main -> main`) - Forgejo 网页显示仓库名但看不到文件 - 再次推送报错:`Gitea: Internal Server Error Decoding Failed` + `pre-receive hook declined` - 远程 HEAD 不一致:`git ls-remote` 显示的 hash 与本地不同 **根本原因**:从 COS 备份恢复旧 Gitea 数据时,Git 内部数据损坏或版本不兼容。 **排查流程**: 1. `git ls-remote origin HEAD main` — 检查远程 HEAD 是否匹配本地 2. `curl -u user:token https://domain.com/api/v1/user/repos` — 查看仓库状态(empty、size) 3. 尝试推送观察具体错误信息 **解决**: ```bash # 1. 通过 API 删除损坏仓库 curl -s -X DELETE -u "bingshuo:ACCESS_TOKEN" "https://guanghulab.com/api/v1/repos/bingshuo/guanghulab" # 2. 创建新空仓库 curl -s -X POST -u "bingshuo:ACCESS_TOKEN" \ "https://guanghulab.com/api/v1/user/repos" \ -H "Content-Type: application/json" \ -d '{"name":"guanghulab","description":"光湖代码仓库·国内镜像","private":false,"auto_init":false}' # 3. 从本地推送完整代码 git push https://user:token@guanghulab.com/user/repo.git main ``` --- ### 4. SSL 证书配置 ```bash # 申请证书 sudo certbot --nginx -d guanghulab.com # 验证自动续期 sudo certbot renew --dry-run # 证书位置 /etc/letsencrypt/live/guanghulab.com-0001/ ``` certbot 会自动在 Nginx 配置中添加 `listen 443 ssl` 和 HTTP→HTTPS 重定向。 --- ### 5. COS(腾讯云对象存储)备份恢复 ```bash # 安装 coscmd pip3 install coscmd # 配置 coscmd config -a {secret_id} -s {secret_key} -b {bucket_name} -r ap-guangzhou # 列出文件 coscmd list # 下载备份 coscmd download backups/guanghu-old-backup-20260512.tar.gz /root/forgejo-backup.tar.gz # 恢复到 Forgejo 目录 tar -xzf /root/forgejo-backup.tar.gz -C /opt/forgejo/ chown -R git:git /opt/forgejo/ ``` > **注意**:COS 存储桶名称:`zy-team-hub-1317346199`,区域:`ap-guangzhou`。 > 备份文件在 `backups/` 目录下。 --- ### 6. 验证部署完整性 **确认服务运行**: ```bash systemctl status forgejo # 服务 active curl -I http://127.0.0.1:3001 # 返回 200 curl -I https://guanghulab.com # HTTPS 正常 ``` **确认仓库状态**: - Forgejo 网页登录 → 仓库列表有数据 - `git ls-remote https://guanghulab.com/bingshuo/guanghulab.git HEAD` → 返回正确 commit - Forgejo API `GET /api/v1/user/repos` → 返回仓库列表 --- ## 部署流程速查 从零开始部署的完整流程(假设已有 COS 备份): 1. **执行 `scripts/smart-deploy-forgejo.sh`** 或手动部署二进制 2. **从 COS 下载备份并恢复** 3. **配置 Nginx 反向代理**(脚本已自动处理) 4. **申请 SSL 证书**(`certbot --nginx`) 5. **启动 Forgejo 并验证** 6. **如果仓库损坏** → 删除仓库,重建,重新推送 7. **如果推送报 413** → 配置 Nginx `client_max_body_size 100M` --- ## 注意事项 - **不要用 root 直接运行 Forgejo** — 必须用 git 用户 - **Nginx 配置改动后必须 reload** — `sudo systemctl reload nginx` - **备份恢复后检查仓库完整性** — 用 `git ls-remote` 对比本地 hash - **ROOT_URL 要写 https** — 否则克隆链接显示 http:// - **certbot 会修改 Nginx 配置** — 注意它创建的是独立的 server block --- ### MCP Server 配置 MCP Server 迁移到新服务器后需要手动启动: ```nginx # Nginx location block(放在 location / 前面) location /mcp { proxy_pass http://127.0.0.1:8083; 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; } location /repo-mcp { rewrite ^/repo-mcp$ /mcp break; proxy_pass http://127.0.0.1:3903; 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; } ``` **启动步骤**: 1. 克隆仓库:`cd /opt && git clone https://user:token@guanghulab.com/bingshuo/guanghulab.git repo-name` 2. 启动 guanghulab-mcp-server(端口 8083): - `cd /opt/repo-name/server/mcp-server && npm install` - 创建密钥:`echo "TOKEN" > /data/guanghulab/mcp-server/.access-key` - `pm2 start ecosystem.config.js` 3. 启动 repo-mcp-server(端口 3903): - `cd /opt/repo-name/mcp-servers/repo-mcp-server && npm install` - `REPO_MCP_PORT=3903 REPO_MCP_SECRET=SECRET pm2 start index.js --name repo-mcp-server` 4. 验证:`curl -X POST -H "Authorization: Bearer TOKEN" https://guanghulab.com/mcp` **常见问题**: - **Nginx location 嵌套**:sed 拼接容易把 location 块塞进其他 block 里,直接 cat 覆盖最稳 - **路径重写**:repo-mcp-server 后端路由是 `/mcp`,但对外暴露 `/repo-mcp`,Nginx 需要 `rewrite ^/repo-mcp$ /mcp break;` - **access key 路径**:`ecosystem.config.js` 写死了 `/data/guanghulab/mcp-server/.access-key`,需检查是否存在 - **PM2 进程冲突**:多次启动同一进程名会创建重复实例,用 `pm2 delete ID` 清除 errored 的 ## 2026-05-16 实测记录 本次迁移完整时间线: | 时间 | 事件 | 结果 | |------|------|------| | 14:30 | 从 COS 下载 44MB 备份 | ✅ | | 14:45 | 解压恢复 forgejo.db + 仓库 | ✅ | | 15:00 | 下载 Gitea 1.23.7 二进制 | ✅ | | 15:20 | Nginx 反向代理 + SSL 证书 | ✅ | | 15:50 | 域名解析切换到广州服务器 | ✅ | | 16:37 | 推送代码到新仓库 | ⚠️ 推送成功但网页不显示 | | 16:50 | 发现仓库数据损坏 | ❌ Internal Server Error | | 16:52 | 删除损坏仓库,重建 | ✅ | | 16:55 | 再次推送 → HTTP 413 | ❌ | | 16:58 | 排查 Nginx 配置 → 缺 client_max_body_size | ✅ 修复 | | 17:02 | 重载 Nginx,第三次推送 | ✅ 成功 | | 17:12 | MCP Server 迁移 | ❌ Invalid CSRF token / SSE 404 | | 17:15 | Nginx 配置修复(location 嵌套错误) | ❌ Nginx 配置乱 | | 17:17 | cat 覆盖完整 Nginx 配置 | ✅ 配置正常 | | 17:20 | MCP Server 启动(access key 路径问题) | ❌ 403 认证失败 | | 17:25 | 创建 /data/guanghulab/mcp-server/.access-key | ✅ 认证通过 | | 17:26 | repo-mcp-server 路由重写 `/repo-mcp` → `/mcp` | ✅ 全链路通过 | **教训总结**: 1. COS 备份恢复后,仓库数据可能损坏——先 `git ls-remote` 验证 2. Nginx 默认 1MB body 限制——Git 推送必超,需显式设置 3. MCP Server 迁移关键:access key 路径、Nginx rewrite、location 不能嵌套 4. PM2 进程迁移后要清理冲突的 errored 实例 5. 经验要沉淀到仓库里,不能只留在本地终端