从GitLab到Gitea:轻量级Git服务器迁移与资源优化实践 📅 发布时间:2026/9/11 19:54:24 👁 浏览次数: 三个月前我还在为一个 GitLab 容器占掉 6GB 内存发愁。那天我登录服务器看了一眼docker ps里躺着 10GB 多的镜像宿主机内存只剩不到 300MB磁盘还在被 CI 缓存一点点塞满。换方案的想法不是没有过但一直担心迁移成本太高直到我把团队仓库迁到一台只有 1GB 内存的旧机器上才真正意识到10GB 和 600MB 的差距远远不只是体积数字。这说的就是 Gitea。一个用 Go 写的轻量级 Git 服务镜像 600MB 出头内存占用经常只有两三百兆开箱即用自带 Web 界面、Issue、PR 评审、内置 Actions。它不是 GitLab 的“替代品”而是“另一种产品思路”对小团队、自托管、内网场景来说少即是多。如果你正纠结服务器被 GitLab 吃光、想重新掌控维护成本这篇文章会把技术选型、部署细节、迁移踩坑一次讲透。1. 从 GitLab 到 Gitea一次“减负”背后的关键决策1.1 为什么 GitLab 会变得难以承受我得先说清楚GitLab 不是不好。对一个几百人的研发组织来说GitLab 的 CI 编排、代码评审流、安全扫描、依赖管理这些能力确实把需求都管得明明白白。但问题是这些能力是有“价格”的。我之前的处理方式是 Docker 部署gitlab/gitlab-ce:latest服务刚起来时一切正常越跑越不对劲。镜像体积 10GB 起内存长期在 4-6GB 徘徊后台进程非常多sidekiq、puma、gitaly、workhorse各有各的资源账单。你要是像我一样在一台 8GB 内存的机器上同时跑 Jenkins、Nexus 和数据库GitLab 一键占据大半个机箱的“感受”会非常真实。还有一点很容易被低估升级和维护成本。GitLab 的版本升级文档厚得能当字典翻跨大版本升级还得一步步来稍不留神就会遇到迁移失败、后台任务卡死。再加上 422 登录错误、token 权限配置、未授权访问路径扫描这类安全提醒也很多每次都必须花时间确认补丁、处理漏洞。不是不能解决而是“为了用版本管理工具先搭了个运维项目”本末倒置了。1.2 轻量级替代品怎么选Gitea、Gogs、Forgejo 的取舍想换掉 GitLab 的时候市面上的轻量方案最常被拿出来比较的是 Gitea、Gogs 和 Forgejo。Gogs 是最早的 Go 语言 Git 服务界面简洁但开发节奏偏慢很多扩展功能更新不及时。Forgejo 是 Gitea 分叉出去的社区版理念更偏开放治理正常情况下两者功能相差不大。我最后选 Gitea理由很实际社区活跃度高版本迭代快遇到问题资料多。Docker 部署极度简单一个docker-compose.yml搞定镜像体积 600MB 左右内存占用在 300MB 内。自带 Web 端 Git 管理、Issue、PR、里程碑、Wiki、Webhook日常够用。内置 Gitea Actions语法兼容 GitHub Actions迁移 CI 非常顺。数据库可选 SQLite 或 PostgreSQL小团队连额外安装数据库这一步都能省。当然如果你对代码评审有强需求流程必须极其严格那 Gerrit 会是另一个方向但它的使用习惯和 GitLab 差距太大团队学习成本反而高。相较之下Gitea 是“切换成本最低、收益最直接”的方案。2. 核心细节部署 Gitea 前必须先想清楚的几个设计点2.1 镜像体积与运行资源到底差多少标题里写的“10GB vs 600MB”不是随意对比而是我实际拿docker images看到的数字。GitLab CE 镜像因为打包了 Redis、PostgreSQL 依赖和一堆后台组件解压后体积非常可观。而且这只是镜像本身真正的资源压力在运行时。对比项GitLab CEGitea镜像体积10GB约 600MB空闲内存占用4-6GB约 250-400MB后台进程数量5 个常驻进程单进程数据库依赖需配套数据库部署复杂默认 SQLite可选 PostgreSQL首次启动时间2-5 分钟几秒版本升级复杂度跨版本升级风险高拉镜像重启即可如果你的服务器是老机器、云主机低配 1GB 内存或者只是想在公司内网跑一个“能用的 Git 服务”这个对比已经能说明问题。我迁移后的第一周旧服务器内存使用率从 90% 降到 40%那种一查free -h就舒坦的感觉只有被 GitLab 卡过的人才懂。2.2 仓库存储与备份策略备份逻辑也要提前想清楚。GitLab 的数据目录包含仓库、数据库、上传文件由gitlab-backup统一处理思路没问题但备份时间久恢复时还会遇到版本匹配问题。Gitea 的思路简单很多所有仓库在data/git/repositories下配置在app.ini数据库存问题、用户、权限等元数据。我推荐至少每周跑一次gitea dump它会把配置、仓库、数据库一并打包。你要是用 Docker命令也很直白进容器后执行一下把生成的 zip 拉到备份目录就行。仓库量不大时甚至可以直接对repositories目录做增量同步恢复时挂到新机器就能用。这里有个容易忽略的点如果你之前用了 GitLab 的 LFS 大文件存储迁移仓库时要额外检查 LFS 对象是否完整。Web 端迁移不一定能把所有 LFS 对象带过去建议迁移后用git lfs fetch --all验证一下。2.3 SSH、HTTPS 和 Web 端口的规划我见过不少人部署 Gitea 后连不上问题基本都出在端口规划上。Gitea 容器内部默认 Web 端口是 3000SSH 默认 22。如果宿主机 22 端口已经被系统 SSH 占用你就得把容器的 2222 映射到容器内的 22并存到配置里最终 clone 地址会是这样的git clone ssh://gitgit.example.com:2222/username/repo.gitHTTPS 方面我建议不要直接暴露 3000 端口给用户。用 Nginx 做统一入口托管 TLS 证书再把请求转发到 3000 端口。这样一个域名、一个 443 端口干干净净。另外记得把app.ini里的ROOT_URL改成用户真正访问的域名否则 Web 页面里的 Clone 地址永远会显示成http://localhost:3000/...这个问题非常常见。3. 实操记录Docker Compose 部署一套可上线的 Gitea3.1 compose 文件怎么写附可直接复制的配置我不喜欢用官网那种零散的docker run而是直接写docker-compose.yml便于后续维护。下面这份配置经我实际测试跑了几十个仓库一点问题没有直接复制就能用version: 3 services: gitea: image: gitea/gitea:latest container_name: gitea restart: always environment: - USER_UID1000 - USER_GID1000 - GITEA__server__DOMAINgit.example.com - GITEA__server__SSH_PORT2222 - GITEA__server__ROOT_URLhttps://git.example.com/ - GITEA__server__HTTP_PORT3000 volumes: - ./gitea:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro ports: - 3000:3000 - 2222:22为什么要用environment而不是直接改app.ini因为这样升级镜像后核心配置不会丢尤其是ROOT_URL和 SSH 端口这两项一旦漏配就是“能访问页面但拉不了代码”的尴尬状态。如果团队仓库多、并发高建议再加一个 PostgreSQL 服务db: image: postgres:16 restart: always environment: - POSTGRES_USERgitea - POSTGRES_PASSWORDgitea_pass - POSTGRES_DBgitea volumes: - ./postgres:/var/lib/postgresql/data对应在 Gitea 服务里加上数据库依赖首次安装时数据库填内部服务名db即可。小团队用 SQLite 也行但并发稍大或后续要做备份恢复PostgreSQL 更稳。3.2 首次启动与核心配置项跑docker compose up -d之后浏览器访问http://服务器IP:3000第一次会进入初始化页面。这里有三个关键字段数据库类型生产环境选 PostgreSQL测试环境或仓库少选 SQLite。域名填git.example.com。SSH 端口填2222因为宿主机 22 端口大概率被系统占用。初始化完成后系统生成的配置文件叫做app.ini一般在宿主机./gitea/gitea/conf/app.ini下。如果你想开内置 CI需要手动在app.ini里加这一段[actions] ENABLED true改完配置不要忘了重启容器很多配置只有在启动时才会读取。3.3 SSH 密钥和客户端使用方式Git 服务器的核心工作是接受 clone、push、pull所以 SSH 密钥配置必须顺手。Gitea 的入口在页面右上角头像里依次点“设置 → SSH 密钥”把本地的公钥贴进去。生成密钥的命令很简单ssh-keygen -t ed25519 -C your_emailexample.com把生成的.pub内容粘贴到 Gitea 后本地测试连接ssh -T -p 2222 gitgit.example.com如果返回Hi there, Youve successfully authenticated就说明 SSH 配置成功了。接下来就是拉代码把原来 GitLab 的远程地址改成 Gitea 的新地址git remote set-url origin gitgit.example.com:username/repo.git改完之后直接git push就能推上去历史记录完整保留团队本地分支也不受影响。4. 项目迁移实录从 GitLab 把仓库、权限、CI 搬过去4.1 仓库与历史记录搬运三种方式我测试下来仓库迁移主要三种方式按团队体量选方式一Gitea Web 端直接迁移登录 Gitea 后点右上角“”号选“迁移外部仓库”填 GitLab 的地址和 token。Gitea 会自动抓取仓库的 Issue、PR、里程碑、标签这对不复杂的项目来说是最省事的路子。方式二本地镜像 clone 后 pushGitLab 仓库权限比较严格Web 端迁移有时会卡在 token 或网络问题上这时可以用本地中转git clone --mirror gitgitlab.example.com:team/repo.git cd repo.git git push --mirror gitgit.example.com:team/repo.git--mirror会把所有分支、标签、引用一块推过去历史不会丢。这种方式对仓库大小不敏感几百 MB 也能跑。方式三脚本批量同步几十个仓库的情况下手点太累。可以写一个简单的 Bash 循环处理for repo in repo-a repo-b repo-c; do git clone --mirror gitgitlab.example.com:team/$repo.git cd $repo.git git push --mirror gitgit.example.com:team/$repo.git cd .. rm -rf $repo.git done注意一点如果仓库里有 Git LFS 对象clone --mirror默认不会拉全 LFS 数据。迁移完成后一定要进入仓库目录执行git lfs fetch --all校验或者干脆在 Gitea 页面重新传一次大文件对象。4.2 权限和组织结构重建GitLab 迁移到 Gitea权限体系不会自动跟过来。用户得重新注册或由管理员统一创建组织、团队、项目授权需要手动重建。我当时的做法是先在 Gitea 建好“开发组”“测试组”“运维组”三个团队再按旧 GitLab 里的权限映射规则把项目批量加进去。这里分享一个我踩过的坑仓库迁移完成后保护分支规则是空的。GitLab 里如果 master/main 分支设置了“禁止直接 push”迁到 Gitea 后默认没人保护。万一哪个同事手快直接往主干推了代码CI 都拦不住。一定要记得在 仓库“设置 → 分支”里把 main 分支勾上“禁止直接推送”和“需要 PR 审查”。除此之外Webhook 也要重新配置。如果你原来有用企业微信、钉钉或飞书机器人通知旧的 webhook 地址全部失效需要在 Gitea 的仓库配置里重新添加并确认事件类型包含 push、PR、Issue 等。4.3 CI 设施迁移从 GitLab CI 到 Gitea ActionsCI 是迁移中最大的“隐形成本”。GitLab 的.gitlab-ci.yml和 Gitea Actions 的.gitea/workflows/*.yml虽说都是 YAML 文件但语法区别很大不能直接替换。举个例子GitLab CI 常见的构建阶段stages: - build - test build-job: stage: build script: - echo build...改成 Gitea Actions 之后的写法name: CI on: push: branches: - main jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Build run: echo build...关键差异在于事件触发和步骤引用。Gitea Actions 基本沿用了 GitHub Actions 的on语法所以团队如果之前接触过 GitHub适应起来非常快。要跑 Actions 构建任务还需要一个 Gitea Runner。GitLab 的 Runner 是单独安装、单独注册的Gitea 这边类似也需要执行act_runner register动作注册到实例上。我用 Docker 方式部署 Runner一条命令就能跑起来docker run -d \ --name gitea-runner \ -e GITEA_INSTANCE_URLhttp://gitea:3000 \ -e GITEA_RUNNER_REGISTRATION_TOKENxxxx \ -v /var/run/docker.sock:/var/run/docker.sock \ gitea/act_runner:latest需要注意的是 Runner 要用到 Docker socket 来执行容器化任务这种权限要控制好别随意暴露。5. 常见问题与排查速查迁移后最容易踩的坑5.1 “login failed. check api token or gitlab version”这一类 token 问题迁移过程中最烦的报错之一就是 API token 校验失败。很多人以为凡是 token 都通用其实 GitLab 里有两种概念一个是用户私有 token一个是 OAuth application token。有的接口认前者有的接口只认后者拿错类型就会出现类似“login failed”的提示。遇到这种情况我建议在执行迁移前先在 GitLab 里生成一个新的私有 token并且勾选api权限。Gitea 的 Web 迁移页面里把 GitLab 地址和 token 填对后项目列表通常就能拉出来。如果还是失败先检查 GitLab 服务版本和 token 是否过期不要在浏览器里直接复制截图里的 token没想到这种东西也会因为复制多了一个空格卡住半天。5.2 登录 422 错误、隐身模式却能登录登录 GitLab 时偶尔会遇到 422但用浏览器隐身模式却能正常登录。这本质上不是账号问题而是浏览器缓存了旧的 cookie 或 CSRF token。隐身模式什么都不存自然就绕过去了。解决办法也很简单清掉该站点的 cookie 缓存或者强制刷新一次再登录。这个 422 问题在 GitLab 上比较出名迁移到 Gitea 后我倒是一次没遇到过。但如果你在 Gitea 里也碰到类似报错先别怀疑服务器试着清缓存或换浏览器大概率是前端会话问题。5.3 SSH 密钥识别、默认端口与拉取代码失败换到 Gitea 之后最常见的失败场景是git push提示Permission denied (publickey)。第一嫌疑是 SSH 端口。如果服务器 22 被系统占用你映射了 2222那 clone 地址必须带上端口号且远程 URL 中要区分冒号的含义。正确格式是git clone ssh://gitgit.example.com:2222/username/repo.git如果写成gitgit.example.com:username/repo.gitGit 会默认走 22 端口这时候怎么都连接失败。这个坑最容易发生在从 GitLab 迁移后因为之前没想过端口会变。第二嫌疑是本地 SSH 代理缓存。有时候你已经正确换了新密钥但 ssh-agent 里缓存了旧密钥需要执行ssh-add -D ssh-add ~/.ssh/id_ed25519然后再次测试连接。这种细枝末节的问题排查起来比大故障还消耗耐心建议放在自查清单里。5.4 安全补丁和版本升级怎么安排GitLab 因为组件多历史上有很多高危漏洞曝出来提交到公开网络的实例经常会被扫描。Gitea 相对组件少攻击面小但不代表可以不维护。我一般建议做好几件事关闭开放注册注册需管理员审核。开启两步验证2FA。内网实例不要暴露到公网尤其是没有 HTTPS 的情况下。定期升级 Docker 镜像Gitea 版本更新很快拉新镜像重启容器即可没有 GitLab 那种沉重的版本链包袱。公开仓库如果不需要也建议默认私有。Gitea 的私有仓库做内部代码管理绝对够用一旦公开就会被扫描器抓取安全问题就会多出来。6. 个人体会与适配建议6.1 什么场景适合换到 Gitea什么场景该继续用 GitLab换方案不能跟风得结合团队实际。我的建议是小团队、个人项目、内网部署、课设实验、几十个仓库以内的场景Gitea 是首选。它让你把维护精力从 GitLab 本身释放出来专注在业务代码上。但如果你用 GitLab 的深度代码评审流、大型单体仓库、复杂流水线、安全合规审计并且这些能力是团队的核心依赖就不要轻易拆掉。GitLab 企业版那些管控能力Gitea 目前还替代不了。工具是为团队服务的不是用来“秀轻量”的。6.2 迁移之后的“轻量红利”和可持续运营建议迁移完成后的最大感受是两个字从容。以前重启一次 GitLab 等几分钟现在 Gitea 秒起。以前升级一次要读十几页发布说明现在直接换镜像。对一个小团队来说这套方案的成本优化是肉眼可见的。运营上我保留了两个习惯一是每天定时把data目录同步到异地备份机二是每季度做一次恢复演练保证备份真的能用而不是备份了个心理安慰。另外如果团队开始用 Gitea Actions 跑 CI建议给 Runner 单独的机器或容器资源避免构建任务和 Gitea 主服务抢内存。最后分享一个小细节迁移后我第一时间把团队文档里所有 GitLab 的 clone 地址统一替换成了 Gitea 的新地址顺手在 README 里加了一个git remote set-url origin的示例。整个项目无缝切换到现在已经跑了几个月再也没人提“服务器为什么又卡了”。如果你也在被重型 Git 服务拖得焦虑试试这个方向大概率会和我一样换完之后只想说一句早该换了。