# Gitea 迁移指南 > **什么时候看这篇**:自建 Gitea 要换机器 / 换地址时,**从头照着做**。 > 迁移的坑不在「搬数据」,而在**本仓有 9 处地方写死了旧地址**(第三节),漏一处就会在旧机下线那天才报错。 --- ## 一、为什么要迁 | 项 | 现状 | |---|---| | 承载机器 | **境外 VPS `23.254.236.47`**(2 核 / 2 G 内存 / 50 G 磁盘) | | **到期时间** | **2026 年 11 月**(见 `deploy/gitea/README.md` 原始记录) | | 版本 | Gitea **28.0.0**(`/api/v1/version` 实测) | | 实例地址 | `http://23.254.236.47:3001`(HTTP 直连 :3001,**没有走域名/证书**) | | 仓库 | `zqlit/blog`(私有) | | 账号 | `zqlit` | **它在架构里的角色**:**代码辅仓** —— 只推不拉、不参与构建、不受第三方平台规则约束。 主仓是 CNB(`origin`,推送即触发构建)。 > 换句话说:**它是备份体系里「在线的那一路」**,不是构建链路的一环。 > 这一点决定了迁移比想象中简单得多 —— 见下一节。 --- ## 二、★ 迁移范围已经大幅缩小(先读这节,别照旧清单瞎忙) 仓库里的 [`deploy/gitea/迁移前检查清单.md`](../deploy/gitea/迁移前检查清单.md) 是 **2026-10-04** 写的, 当时的目标是「把 **Gitea + act_runner + 又拍云同步** 整套搬到国内机」。 **那个前提已经不存在了**: | 2026-10-04 的迁移面 | 现在 | 为什么 | |---|---|---| | Gitea 本体 | ✅ **仍要迁** | 唯一的代码辅仓 | | `act_runner`(自建 Actions) | ❌ **不需要** | 构建已由 **CNB** 托管,`.github/` 早已删除 | | `upyun-sync`(又拍云同步容器) | ❌ **不需要** | 又拍云同步已在 CNB 流水线里 | | 广州中转机 | ❌ **不需要** | CNB 构建节点在国内,直连又拍云 | | `cos_sign.py` / COS | ❌ **早已砍掉** | 见归档的 `砍COS改造步骤.md` | → **迁移实际只剩一件事:把 Gitea 的数据卷搬到新机器,再把 9 处地址改掉。** `deploy/gitea/docker-compose.yml` 里那一大坨 `runner` / `upyun-sync` 服务定义, **迁移时不必启动**(可以直接注释掉,或干脆不管 —— 只 `up -d gitea` 即可)。 --- ## 三、★★ 必须同步修改的位置(漏一处 = 旧机下线后才报错) 按「易漏程度」排序。**第 4、5 项是最容易漏的**,因为它们在线上机器上,不在本仓库里。 | # | 位置 | 改成 | 漏掉的后果 | |---|---|---|---| | 1 | **本机** `.git/config` 的 `gitea` remote | 新地址 | `git pushall` 推不动辅仓 | | 2 | `scripts/setup-cnb-remotes.sh` 里的 `GITEA_URL` 默认值 | 新地址 | 重跑脚本时把远端**指回旧机** | | 3 | `deploy/editor-api/bootstrap.sh` 里的 `GITEA_URL` 默认值 | 新地址 | 新部署的容器指向旧机 | | 4 | ★ **线上国内机 `/srv/editor-api/.env`** 的 `GITEA_URL` | 新地址 | 写作后台发文章时辅仓推送**持续报错**(主仓成功、只警告,**不会有人发现**) | | 5 | ★ **线上国内机 `/srv/blog`** 的 `gitea` remote | 新地址 | 同上 | | 6 | `docs/GITEA_SECRETS.md` | 地址 + 凭据 | 后人照文档操作时指向旧机 | | 7 | `架构总览.md`(§1.2 地址地图 / §2 旅程图 / §5.2 远端表 / §6 待办 #7) | 新地址 | 文档失真 | | 8 | `README.md`(发布流程图 + 推送说明 + 脚本表) | 新地址 | 文档失真 | | 9 | `editor-api/README.md`(辅仓那一行)、`scripts/backup-run.mjs`(头部注释) | 新地址 | 文档失真 | **不用改的**(它们只引用 remote 名字 `gitea`,不含地址): `docker-compose.editor.yml`(`PUSH_REMOTES: origin,gitea`)、`editor-api/server.mjs`、 `pushall` 别名本身。 ### 一条命令改本机那两处(1 + 2) ```bash cd /e/GitHub/blog # 1) 本机 remote —— 换 GITEA_URL 即可,凭据会自动拼进去 GITEA_URL='http://<新机IP>:3001/zqlit/blog.git' \ GITEA_PASS='' \ bash scripts/setup-cnb-remotes.sh https://cnb.cool/zqlit/blog # 验证:两个远端都在、地址是新的 git remote -v | sed -E 's#://[^@]*@#://***@#' ``` ### 线上国内机那两处(4 + 5) 国内机没有 SSH,走 1Panel API 执行(本机 `python .editor-tmp/cnrun.py <脚本>`): ```bash set -uo pipefail NEW='http://<新机IP>:3001/zqlit/blog.git' PASS='' # ① .env —— 改 GITEA_URL(GITEA_PASS 不变的话不用动) sed -i "s#^GITEA_URL=.*#GITEA_URL=$NEW#" /srv/editor-api/.env # ② /srv/blog 的 remote git -C /srv/blog remote remove gitea git -C /srv/blog remote add gitea "$(printf '%s' "$NEW" | sed -E "s#^(https?://)#\1zqlit:${PASS}@#")" # ③ 重建容器(容器启动时读 .env) cd /srv/editor-api && docker compose up -d --build # ④ 验证 docker exec editor-api sh -c 'cd /srv/blog && git remote -v' | sed -E 's#://[^@]*@#://***@#' curl -s http://127.0.0.1:8017/health; echo # remotes 应为 ["origin","gitea"] ``` --- ## 四、建议:新实例用域名,不要再用 IP 现在写死的是 `23.254.236.47:3001` —— **IP 一换,上面 9 处全部要改**。 如果新实例挂个域名(如 `gitea.usj.cc`,DNS 指向新机),以后再搬机器**只改 DNS**,上面 9 处全都不用动。 > ⚠️ **Gitea 28 起不再读 `[server] DOMAIN`** —— 实例域名(含默认 SSH 域名)全部来自 **`ROOT_URL`**。 > 改域名时**只改 `ROOT_URL`** 一处。(`deploy/gitea/docker-compose.yml` 里两个都填了,所以现在的配置没问题。) 换域名的额外成本:要在 1Panel 的 OpenResty 里加一个反代站点 → `127.0.0.1:3001`,并配证书。 (目标机 80/443 已被 1Panel 的 OpenResty 占用,Gitea 容器映射的是宿主 `3001`/`2222`。) **权衡**:花一次配置,换掉以后每一次迁移都要改 9 处的麻烦。**推荐做。** --- ## 五、迁数据 ### 5.1 迁移前的硬伤检查(已修,确认一下即可) | 项 | 状态 | |---|---| | `docker-compose.yml` 镜像 tag | ✅ **已修**(2026-10-06):原来是 `gitea/gitea:1.28.0-rootless` —— **这个 tag 不存在**,Gitea 28 起去掉了 `1.` 前缀。现为 `28.0.0-rootless`(pin 死版本,别用 `latest`) | | `deploy/gitea/.gitignore` 缺 `backups/` | ✅ **已补**:跑一次 `backup.sh` 会在 `backups/` 落 `gitea-dump-*.zip`(含 secrets)与完整 data 卷,原先漏忽略,`git add .` 一下就进历史 | ### 5.2 数据怎么搬 —— **用 rsync,不用 dump** | 方式 | 适用 | 说明 | |---|---|---| | **rsync 数据卷**(推荐) | 同架构迁移 | 保完整(含仓库、app.ini、头像、LFS)。**用 `rsync -a` 保留属主** | | `gitea dump` | 跨版本 / 跨数据库 | `deploy/gitea/scripts/backup.sh` 已封装;适合做归档,不适合日常迁移 | ```bash # 旧机 → 新机(在新机上执行,或先把 data 卷打包传过去) rsync -av --progress root@23.254.236.47:/opt/gitea/data/ /srv/gitea/data/gitea/ ``` > ⚠️ **属主必须是 uid/gid 1000** —— Gitea rootless 镜像按 1000 跑,属主不对容器起不来。 > `rsync -a` 会保留;如果中间过了 Windows 或换了 uid,事后要 `chown -R 1000:1000`。 > ⚠️ **新版本必须 ≥ 旧版本**(现在是 28.0.0)。把数据从新版本搬到旧版本会出兼容问题。 ### 5.3 启动 ```bash cd deploy/gitea cp .env.example .env # 填 GITEA_DOMAIN / GITEA_ROOT_URL(用域名的话) mkdir -p data/gitea docker compose up -d gitea # 只起 gitea,runner/upyun-sync 不需要 docker compose ps ``` > `deploy/gitea/docker-compose.yml` 里还留着 `runner` / `upyun-sync` 两个服务定义 —— > 那是 2026-10-04 的形态,**现在不需要**(构建已交给 CNB)。只 `up -d gitea` 即可, > 或在迁移时顺手把这两段注释掉。 --- ## 六、切换顺序(关键:**别让辅仓出现空窗**) 辅仓的价值在于「主仓出问题时它那儿还有」。所以顺序是 **先建好新的、验证通过、再拆旧的**: ``` 1. 新机起 Gitea,数据已就位,验证能 clone 2. 本机配好新 remote(第三节 1、2 项) 3. 从本机推一次,确认新实例能收到 ← 新的已经可用 4. 改线上国内机(第三节 4、5 项),重建容器,实测双推 ← 两条推送入口都切过来了 5. 观察一天:`git pushall` 与后台发文章,都确认辅仓同步正常 6. 旧机下线(等到期即可,不必急) ← 旧的才拆 ``` **不要在 3、4 步之前就把旧机停掉** —— 那会出现「新的还没验证、旧的已经没了」。 --- ## 七、验证清单 ```bash # ① 本机:两个远端都是新地址 git remote -v | sed -E 's#://[^@]*@#://***@#' # ② 本机实测双推(推临时分支 → 确认 SHA → 删除) git pushall git push gitea HEAD:refs/heads/tmp-migrate-test git ls-remote gitea refs/heads/tmp-migrate-test # 应回新实例的 SHA git push gitea --delete refs/heads/tmp-migrate-test git ls-remote gitea refs/heads/tmp-migrate-test # 应为空 # ③ 三处 main 对齐 echo "local = $(git rev-parse HEAD)" echo "origin = $(git ls-remote origin refs/heads/main | cut -f1)" echo "gitea = $(git ls-remote gitea refs/heads/main | cut -f1)" # ④ 历史完整性:新实例上的提交数应与本地一致 git ls-remote gitea refs/heads/main git rev-list --count HEAD # ⑤ 线上容器(国内机执行) curl -s http://127.0.0.1:8017/health # "remotes":["origin","gitea"] docker exec editor-api sh -c 'cd /srv/blog && GIT_TERMINAL_PROMPT=0 git push gitea --dry-run' ``` > ★ **最后一条特别重要**:`editor-api/src/git.mjs` 对辅仓失败是**只警告不阻断**的 —— > 辅仓悄悄推不上去,发布照样报成功。所以**必须主动 `--dry-run` 验一次**,别等要用备份时才发现。 --- ## 八、回滚 迁移全程是**加法**(新实例起来 → 改地址 → 推一次),旧实例在最后一步之前一直没动: ```bash # 回滚本机 remote GITEA_URL='http://23.254.236.47:3001/zqlit/blog.git' \ GITEA_PASS='<旧密码>' bash scripts/setup-cnb-remotes.sh https://cnb.cool/zqlit/blog # 回滚线上国内机 .env(把 GITEA_URL 改回去)+ docker compose up -d --build ``` **只要没停旧机,回滚就是把地址改回去。** --- ## 九、★ 迁移前先决定:这个辅仓还要不要留 用户提过「感觉架构还是复杂了」。Gitea 是当前**唯一需要你自己运维的常驻服务**(2 核 2 G 境外机), 而它**不参与构建、不参与发布** —— 纯粹是备份体系里「在线的那一路」。 | 选项 | 换来什么 | 代价 | |---|---|---| | **A. 迁到新机**(本文档前面全部内容) | 保留「可 clone、可按提交追溯、不受平台规则约束」的在线副本 | 继续养一台机器 + 一处需要运维的实例 | | **B. 不迁,去掉辅仓** | 少一台机器、少一个要运维的部件;架构回到「CNB 主仓 + F50 离线加密 bundle」两层 | 少一层在线冗余。离线层仍是**完整历史**(`git bundle --all`),且备份已带失败告警 | | **C. 换成托管平台的私有仓** | 不用自己运维 | CNB 已是托管平台,再挂一家性质相同的,收益有限(见归档的 `平台与选型/` 系列) | **判断依据**:Gitea 现在挡的是什么?——「CNB 跟 GitHub 一样出问题」。 但离线 bundle 已经覆盖了这个场景(且加密、带告警、每天自动跑)。 Gitea 多出来的独有价值只有两点:**在线可浏览**、**可增量拉取**(不用等一天一次的备份)。 如果这两点你用不上,**选项 B 是合理的简化**。 > 若选 B:把 `deploy/gitea/` 保留在仓库里即可(迁移清单和部署包留着,随时能重建), > 然后参考本文档第三节,把那 9 处的 `gitea` 相关引用一并清掉 > —— 别忘了 **第 4、5 项**(线上那两处),否则容器会一直对着一个已经没了的辅仓报错。