一、迁移前准备(新旧机都要看)
- 记录旧机信息
- Gitea 版本:
docker exec gitea gitea --version - 数据库连接:库名/用户/密码/Host/端口(你的是宿主机 MySQL8,比如
10.10.0.8:3306或新机内网 IP) - 数据卷路径:compose 里
./gitea-data:/data,实际在宿主机~/gitea/gitea-data - 关键配置:DOMAIN / ROOT_URL / SSH_DOMAIN / SSH_PORT / SSH_LISTEN_PORT / LFS路径 / 附件路径
- 是否开 Actions、LFS、包仓库、Webhook、OAuth、LDAP
- Gitea 版本:
- 新机预装
- Docker + compose
- 宿主机 MySQL8(字符集 utf8mb4、collation 建议 utf8mb4_bin,InnoDB、DYNAMIC),或者继续用老机 MySQL 只改授权也行
- 若新机也用宿主机 MySQL,提前建库建用户并授权来自 Gitea 容器网段
- 原则
- 先装“和旧机完全相同大版本”的 Gitea 再迁数据,迁完验证后再升级;跨大版本升级容易踩 schema 坑。
- 迁移期间旧机停 Gitea(或只读),避免 dump 后又有新提交。
二、方案 A:官方 gitea dump 全量(最省事)
1)旧机备份
cd ~/gitea
# 停服务保证一致(小实例也可不停,但建议停)
docker compose stop gitea
# 进容器用 git 用户 dump,输出在 /data/backups 或 /tmp
docker exec -u git -it gitea sh -c 'cd /data/backups && /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini'
docker compose start gitea # 如果刚才停了,现在可先起回;传完再最终停
dump 会生成 gitea-dump-时间戳.zip,里面含:
app.ini(配置)gitea-db.sql(数据库逻辑备份;MySQL 模式可直接导回 MySQL)repos/(所有仓库)data/(附件、LFS、头像、配置等,视版本而定)
拷出来:
docker cp gitea:/data/backups/gitea-dump-xxxx.zip ./gitea-dump-xxxx.zip
scp ./gitea-dump-xxxx.zip newserver:~/gitea-backup/
如果你只想要数据库备份而不走 dump,也可以单独 mysqldump(见方案 B)。但仓库、附件、LFS、hooks 还是建议跟 dump 一起拿。
2)新机恢复
新机先起一个“空 Gitea”把卷结构建出来,或者直接把数据写进卷再起。
# 新机 ~/gitea 放好 docker-compose.yml(同版本镜像),先不依赖旧库
docker compose up -d gitea # 让它生成 /data 结构,或你直接建空目录也行
docker compose stop gitea # 恢复前停掉
# 把 zip 放进容器或解到宿主机再拷
mkdir -p /tmp/restore && cd /tmp/restore
cp ~/gitea-backup/gitea-dump-xxxx.zip .
unzip gitea-dump-xxxx.zip # 得到 app.ini、gitea-db.sql、repos/、data/ 等
恢复数据(Docker 默认卷映射 ./gitea-data:/data,按你实际路径改):
# 配置
cp /tmp/restore/app.ini ~/gitea/gitea-data/gitea/conf/app.ini
# 通用数据(附件/LFS/头像等)
cp -r /tmp/restore/data/* ~/gitea/gitea-data/gitea/ 2>/dev/null || true
# 仓库
mkdir -p ~/gitea/gitea-data/git/repositories
cp -r /tmp/restore/repos/* ~/gitea/gitea-data/git/repositories/
# 内置SSH密钥目录(若用内置ssh)保留 .ssh
cp -r /tmp/restore/data/git/.ssh ~/gitea/gitea-data/git/.ssh 2>/dev/null || true
chown -R 1000:1000 ~/gitea/gitea-data # 容器默认 git 1000:1000,按USER_UID/GID改
恢复 MySQL(宿主机 MySQL8):
# 新机MySQL建库
mysql -uroot -p -e "CREATE DATABASE gitea CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;"
mysql -uroot -p -e "CREATE USER 'gitea'@'172.17.0.0/255.255.0.0' IDENTIFIED BY '强密码'; GRANT ALL ON gitea.* TO 'gitea'@'172.17.0.0/255.255.0.0'; FLUSH PRIVILEGES;"
# 导入 dump 里的 sql
mysql --default-character-set=utf8mb4 -uroot -p gitea < /tmp/restore/gitea-db.sql
如果 dump 出来的是其他库类型 SQL、而你要用 MySQL,官方不推荐转库迁移,最好旧机本身就是 MySQL 再 dump。
改 app.ini 适配新内网 IP(见第四节)。
起服务并重建 hooks:
cd ~/gitea
docker compose up -d
docker compose exec --user git gitea gitea -c /data/gitea/conf/app.ini admin regenerate hooks
docker compose exec --user git gitea gitea -c /data/gitea/conf/app.ini doctor check --all --fix
regenerate hooks 是跨机迁移必做,否则 push 时 hook 路径/容器路径不对会失败。
三、方案 B:手工分件(宿主机 MySQL 独立,最清晰)
适合你这种“MySQL 不在容器里”的情况。
1)旧机导出
MySQL:
mysqldump -h127.0.0.1 -P3306 -uroot -p --single-transaction --routines --triggers --default-character-set=utf8mb4 gitea > gitea-mysql-$(date +%F).sql
Gitea 文件(按你卷路径,默认 Docker 映射 ./gitea-data:/data):
cd ~/gitea
tar czf /tmp/gitea-data.tgz gitea-data
# 重点目录:gitea-data/git/repositories、gitea-data/gitea(配置/附件/lfs/avatar)、gitea-data/git/.ssh(内置ssh)
传新机:
scp gitea-mysql-*.sql newserver:~/mig/
scp /tmp/gitea-data.tgz newserver:~/mig/
2)新机导入
MySQL:
mysql -uroot -p -e "CREATE DATABASE gitea CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;"
mysql -uroot -p gitea < ~/mig/gitea-mysql-*.sql
# 按新Gitea容器源段建用户
mysql -uroot -p -e "CREATE USER 'gitea'@'172.17.0.0/255.255.0.0' IDENTIFIED BY '强密码'; GRANT ALL ON gitea.* TO 'gitea'@'172.17.0.0/255.255.0.0'; FLUSH PRIVILEGES;"
Gitea 数据:
cd ~/gitea
rm -rf gitea-data # 若是全新空卷;要保留旧结构也可只覆盖子目录
tar xzf ~/mig/gitea-data.tgz -C ~/gitea # 解出 gitea-data/
chown -R 1000:1000 ~/gitea/gitea-data
compose 里数据库 HOST 指向新机 MySQL 内网 IP(或 host.docker.internal+extra_hosts),启动后 regenerate hooks、doctor check。
四、改配置:内网 IP 场景
新机如果用 10.8.0.1 内网访问,恢复后改 gitea-data/gitea/conf/app.ini:
[server]
DOMAIN = 10.8.0.1
ROOT_URL = http://10.8.0.1:3000/
SSH_DOMAIN = 10.8.0.1
SSH_PORT = 222
SSH_LISTEN_PORT = 22
HTTP_PORT = 3000
[database]
DB_TYPE = mysql HOST = 10.10.0.8:3306 ; 新机MySQL内网/VPN IP,或127.0.0.1但容器要能通 NAME = gitea USER = gitea PASSWD = 强密码
[lfs]
PATH = /data/git/lfs ; 按你旧值保持,若换路径要同步移文件
用环境变量方式就把对应项改成:
- GITEA__server__DOMAIN=10.8.0.1
- GITEA__server__ROOT_URL=http://10.8.0.1:3000/
- GITEA__server__SSH_DOMAIN=10.8.0.1
- GITEA__server__SSH_PORT=222
- GITEA__server__SSH_LISTEN_PORT=22
- GITEA__database__HOST=10.10.0.8:3306
注意:环境变量会在运行时覆盖 app.ini 同名项;如果你两处都设,以环境变量优先,避免改了 ini 不生效。
五、秘钥和 SSH 不要重新生成
app.ini 里这些必须原样迁移,别换新,否则旧 token、LFS JWT、内部API、session 全废:
SECRET_KEYINTERNAL_TOKENJWT_SECRET/ LFS 相关 JWT- 如果用内置 SSH:
/data/git/.ssh的 host key 和authorized_keys一起拷;用系统 sshd 则恢复后执行gitea admin regenerate keys重写 authorized_keys。
六、启动后验证清单
docker compose ps
docker compose logs -f gitea
功能核查:
- 网页打开
http://10.8.0.1:3000,管理员登录,看组织/用户/仓库数是否和旧机一致 - 随机进几个仓库,看代码、Issues、PR、附件、头像、LFS 文件
- SSH 克隆:
git clone ssh://git@10.8.0.1:222/xxx/yyy.git,能拉能推 - HTTP 克隆:
git clone http://10.8.0.1:3000/xxx/yyy.git - 有 Actions:后台 Runners 重新注册(runner 不随 Gitea 数据迁移,换机要重装 act_runner 并用新 token 注册)
- Webhook:如果旧 hook 指向旧 IP/域名,逐个改;迁移后可用 doctor 检查
- LFS:随便 pull 一个大文件验证
七、常见翻车点
- 只导了 MySQL 没拷 repositories → 网页能登但仓库空/报错
- 只拷 /data 没导 MySQL → 用户/issue/配置全无
- 版本不一致直接迁 → schema migrate 失败;先同版本,再升级
- 用系统 sshd 但没 regenerate keys/authorized_keys → SSH 克隆报权限拒绝
- 内置 SSH 但新机映射端口不是 222,SSH_PORT 仍写 22 → 克隆地址对但连不上
- MySQL 只监听 127.0.0.1,容器用
10.x连 → 改 bind-address 或走 host-gateway - 文件权限不是 1000:1000(或你 compose 设的 USER_UID/GID) → 容器起不来/仓库只读
- LFS 存在对象存储/S3:只迁数据库不够,要确认 LFS_CONTENT_PATH 或 S3 凭证在新机可用