1. 10GB的内存黑洞与600MB的容器画像:一次部署就把账算清
先交代一下背景。我们团队大概二十来人,代码托管一直用的 GitLab,跑在内网一台 8C16G 的虚拟机上。最早用 GitLab 的时候图它功能全,CI/CD、容器镜像仓库、wiki、Issue 跟踪全都有,一整套 DevOps 全家桶。但用了两年多,痛点越来越明显——真实机子上内存常年 85% 以上,动不动就告警,重启一次要好几分钟。
一开始以为是机器配置不够,后来仔细排查才发现,GitLab 的架构实在太重了。Ruby on Rails 写的主应用,还要配 PostgreSQL、Redis、Sidekiq、Gitaly、Prometheus、Nginx 一堆组件,光启动进程就能把内存吃满。官方建议的最低配置是 4C4G,但那是给几个人的团队用的,像我们这种二十多人、代码库又多的团队,16G 内存完全不够看。我见过最夸张的一次,GitLab 全家桶跑起来大概占了 10GB 内存,Swap 都快被打爆了。
当时我做了个测试,在同一台机器上用 Docker 部署了一个轻量级 Git 托管服务,镜像压缩后只有几十 MB,跑起来的内存占用才 600MB 左右,响应速度反而更快。这个对比太震撼了——我们一直默认 GitLab 是标准答案,但它其实一点都不适合我们这种中小团队。于是我们决定换。
这里先声明一个观点:我不是说 GitLab 不好,它的功能确实强大,尤其是大型企业,代码量几千个仓库、权限体系复杂、审计要求高的场景,GitLab 是对的。但如果是中小团队、自托管服务、不想一直折腾运维成本,GitLab 就是一场灾难。换掉之后,我从「GitLab 管理员」变回了「写代码的人」,这感觉差别太大了。
那这个轻量级神器到底是谁?答案你可能已经猜到了——Gitea(现在叫 Forgejo 也可以,Forgejo 是社区 fork 版)。我们用 Gitea 已经跑了半年,今天就从头到尾讲讲我们为什么换、怎么迁、换了之后踩了哪些坑、以及哪些场景下你不该换。内容比较长,但都是实操过程中真实积累的经验,希望对正在纠结 GitLab 还是轻量方案的朋友有帮助。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先算清旧账:GitLab 的 10GB 到底花在哪了
很多团队放着 GitLab 告警不管,是因为「也不知道它为什么吃这么多内存」。我之前也是一样,直到有一天实在忍不了,SSH 到服务器上一个个进程排查,才把这个账彻底算清楚了。
2.1 进程级拆解:谁在吃掉我们的内存
GitLab 的架构决定了它是一个进程怪兽。我用 htop 和 ps aux --sort=-%mem 看过进程列表,最吃内存的几个大户大概是这样的:
gitaly:Ruby 写的 Git RPC 服务,负责底层 git 操作。代码库越大、越活跃,Gitaly 占的内存越高。我们几十个仓库,Gitaly 大概占了 3-4GB。最关键的是有时候它还会悄悄缓存一些对象数据,内存只涨不降。sidekiq:Rails 的异步任务队列。备份任务、邮件发送、Webhook 触发、仓库统计这些全走它。这个进程数量可以多到几十个 worker,单个不占多少,全部加起来很可观。prometheus+grafana:GitLab 默认自带监控全家桶,自己都 8GB 内存了还要监控自己,这是我后来想想最哭笑不得的地方。puma/unicorn:Web 服务进程,每个 worker 至少几百 MB,默认配置的 worker 数还不少。postgres/redis:这两个不是 GitLab 专属,但你跑 GitLab 就必须养着它俩,内存占用大概再加 1GB 多。
这么说吧,GitLab 自己家官方文档里给的 prometheus.yml 默认抓着几十个指标,每 15 秒抓一次,这些数据都存在内存里,能不重吗。而且我用的还是 Omnibus 安装包,它会把所有组件都给你默认装好,你根本没机会只装核心。
2.2 为啥 GitLab 的容器镜像也巨大
不光是运行时内存,GitLab 的 Docker 镜像体积也大得夸张。官网拉下来的 gitlab/gitlab-ce 镜像,解压后大概 2.5GB 左右,实际用来做数据持久化的目录动辄十几 GB,因为里面有 PostgreSQL 数据、仓库文件、CI artifacts、容器镜像库,全堆在一起。
我见过最夸张的一次,是某个同事误提交了一个包含 1.4GB 视频文件的 commit,GitLab 直接把这个文件存进了 git 对象库里。后来即使删掉这个 commit、push 了新的代码,这个对象还留在仓库里占空间。GitLab 还特意不让你在 Web 界面直接清理,要上服务器跑 git gc 和一堆命令才行。这种情况在 Gitea 上就好得多——不是因为它自带清理,而是你迁移的时候本来就是重新建仓、重新 push,这个历史包袱直接就被丢掉了。
2.3 对比数据:同样功能的轻量方案长什么样
下面这张表是我实际部署过程中记录的对比数据,环境是同一台 8C16G 虚拟机:
| 项目 | GitLab (Omnibus) | Gitea (Docker Compose) |
|---|---|---|
| 服务端内存占用 | 约 10GB | 约 500MB - 800MB |
| Docker 镜像大小 | 约 2.5GB | 约 50MB - 100MB |
| 仓库数据持久化目录 | 约 15GB | 约 2GB |
| 随机器开机自启 | 需要配置,较慢 | 需要配置,极快 |
| 启动到可用时间 | 2-5 分钟 | 3-5 秒 |
| 邮件服务 | 需额外配置 | 有内置支持,配一下即可 |
| CI/CD | 内置 GitLab CI,功能强大 | 可通过 Drone / Woodpecker / Jenkins |
| 安装复杂度 | Omnibus 一键但组件多 | Docker 一条命令 |
需要注意的是,Gitea 在 Docker 里如果配置了 --memory 限制,默认就能控制在 800MB 以内。如果不开限制,它的内存占用还会更低,因为很多操作都是惰性加载的。对于中小团队来说,这个差异是颠覆性的体验——你终于不用天天盯着监控面板提心吊胆了。
3. 迁移实操:从 GitLab 到 Gitea 的完整链路
说完了「为什么换」,接下来是干货最多的部分——到底怎么迁。如果只是建个新服务、让大家手动改 remote url,那代码历史、MR/Issue、Webhook 配置、权限关系全都得重新来,工作量巨大。我们的目标是尽可能平滑迁移,让团队几乎无感知。
3.1 仓库迁移:最核心的一步
仓库迁移其实要分两层:git 数据和平台数据(MR、Issue、标签、评论等)。先说 git 数据,这是最基础、也是不能出错的。
GitLab 提供了一种导出项目的功能,在项目设置里可以导出「Project export」,会生成一个 tar.gz 包。但注意,这个包默认只包含 git 仓库和项目级别的元数据(如分支、标签、描述),不包含机器用户、MR 评论这些平台层面的东西。Gitea 也支持导入 GitLab 的项目导出包,但亲测下来在 MR 迁移上并不完美,评论可能会丢一部分。所以如果你没有特别强的历史评论保留需求,我建议直接按「裸仓库克隆 + 重新 push」的方式来搬代码,这样最干净、最可控。
具体步骤:
bash复制# 1. 在 Gitea 上手动建一个空仓库,名字与 GitLab 上保持一致
# 2. 在本地把 GitLab 仓库完整克隆下来(包含所有分支和标签)
git clone --mirror http://gitlab.example.com/team/project.git
cd project.git
# 3. 把 Gitea 加为 remote 并 push
git remote add gitea http://gitea.example.com/team/project.git
git push gitea --mirror
这一步做完,代码、分支、tag 就全部同步过去了。--mirror 保证了远端所有 ref 都会同步,不会漏分支。要注意的是 webhook 里的 push 事件,需要你在新仓库上重新配置一遍,GitLab 的 webhook 不会自动跟着仓库走。
如果你仓库很多,想批量迁移,可以写个 shell 脚本循环。我提供了一个简化版:
bash复制#!/bin/bash
# 注意:需要一个能以 API 方式创建 Gitea 仓库的 token
GITEA_API="http://gitea.example.com/api/v1"
GITEA_TOKEN="your_gitea_token"
GITEA_ORG="team"
for repo in $(gitlab_api_list_repos); do
curl -X POST "${GITEA_API}/orgs/${GITEA_ORG}/repos" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"name\": \"${repo}\", \"private\": true}"
git clone --mirror "http://gitlab.example.com/team/${repo}.git"
cd "${repo}.git"
git remote add gitea "http://gitea.example.com/${GITEA_ORG}/${repo}.git"
git push gitea --mirror
cd ..
done
这种方式适合几十上百个仓库的批量迁移。如果是单个仓库或零星几个,手动操作更稳妥,因为可以顺便检查有没有遗漏的 protected branch、required status check 这些设置。
3.2 平台数据迁移:MR/Issue/评论到底迁不迁
这是我踩过的坑。最开始我想把 GitLab 里的所有 MR、Issue、评论都迁过去,因为团队用了两年多,里面有大量历史讨论。试过 GitLab 的 export + Gitea 的 import,结果发现:
- MR 的 commit、diff 数据能迁过去,但评论、approval 状态、一些自定义状态标签会丢。
- Issue 的时间、作者能保留,但关联的 MR 引用关系就乱了。
- 如果 MR 里有人提了「Fixes #123」这种关键词,Gitea 不会根据旧 MR 自动关闭对应 Issue。
最后我们做了个折中方案:保留 GitLab 一个月作为只读归档,新开发全部切到 Gitea。重要的历史 Issue(比如还在进行中的需求)手动在 Gitea 重建并注明原链接,其余历史数据就留在 GitLab 里随时可查。这样既不用在迁移工具上耗费太多精力,又保证了新流程不被旧数据拖累。
我的建议是:如果你真的需要完整保留 MR/Issue 历史,请直接考虑用 GitLab API 写脚本自己迁数据,别指望一键导入。但如果你的团队像我一样,更看重「迁移效率」和「新流程顺畅」,只迁代码库 + 归档旧系统是最稳妥的。
3.3 成员与权限模型重设
Gitea 的权限模型比 GitLab 简化很多,就几个层级:
Owner:组织/仓库所有者,可改任何配置。Writer:可以 push 代码、管理标签和 MR。Reader:只读权限,可以看代码、提 Issue。Admin:比 Owner 权级低一级,但能管大部分设置。
GitLab 那边可能有更细分的角色(Developer、Maintainer、Reporter、Guest……),迁移时 5 个角色映射到 Gitea 的 4 个角色,大概对应关系如下:
| GitLab 角色 | Gitea 角色 | 说明 |
|---|---|---|
| Owner / Admin | Owner | 完全管理权 |
| Maintainer | Admin | 可管理仓库设置 |
| Developer | Writer | 可 push、合 MR |
| Reporter / Guest | Reader | 只读权限 |
这个简化对团队来说其实是好事。GitLab 的角色太多,很多人根本弄不清 Reporter 和 Guest 的区别,Gitea 的分层清晰很多,权限授予也更直观。组织维度上,Gitea 有 Team 的概念,可以给 Team 设置仓库级权限,跟 GitLab 的 Group 类似,够用了。
4. 换了之后的日子:那些让团队真香的功能细节
很多文章讲了 Gitea 怎么省资源,但真正让团队用完回不去的,还是它一些功能性细节。这里讲三个我们最常用的能力,都是 GitLab 有但用起来很笨重、或者配置起来极麻烦的。
4.1 基于 Webhook 的自动化生态
GitLab 虽然支持 webhook,但它的 webhook 配置在项目 Settings -> Webhooks 里,能选的触发事件非常多,看起来功能强大,但实际管理起来比较碎片化。而且 GitLab 默认没有内置的 webhook 调试工具,每次调试都要用第三方工具或者 curl 手动测,体验一般。
Gitea 在这一点上做得非常顺手。项目设置里有一项叫 Webhooks,里面可以一键添加 Gitea、Discord、Slack、钉钉这些通知渠道,或者填自定义 URL。让我特别喜欢的是 Gitea 的 webhook 配置里直接给出了测试按钮,点击后会真实触发一次请求,反馈结果里能看到响应状态码和返回内容,对排查问题特别有用。
在我们的自动化体系里,webhook 主要干这么几件事:
- 推送代码后触发 Jenkins 构建。
- 新 PR 创建时触发自动代码格式检查(一个自建的小服务)。
- MR 合并后触发部署流程。
配置示例(用 Golang 写个简单的 webhook 接收端):
go复制package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type PushEvent struct {
Ref string `json:"ref"`
Before string `json:"before"`
After string `json:"after"`
Repository struct {
FullName string `json:"full_name"`
} `json:"repository"`
}
func handler(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body)
var event PushEvent
if err := json.Unmarshal(body, &event); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
fmt.Printf("[push] repo=%s ref=%s after=%s\n",
event.Repository.FullName, event.Ref, event.After)
w.WriteHeader(http.StatusOK)
}
func main() {
http.HandleFunc("/webhook", handler)
http.ListenAndServe(":8080", nil)
}
设置 Gitea 的 webhook URL 为 http://your-server:8080/webhook,推送代码后这个服务就能收到 POST 请求。
4.2 内置的仓库镜像/迁移功能
Gitea 有个特别实用的小功能:项目设置里的「镜像仓库」(Mirror)。你可以在 Gitea 里设置一个仓库为 GitHub 或 GitLab 上某个仓库的镜像,这样对方仓库每一次 push,Gitea 会自动同步过来。
这个能力在我们跟外部合作方对接时帮了大忙。以前用 GitLab 的时候,要 fork 一大堆外部代码库,手动更新 fetch 上游,烦得不行。现在直接在 Gitea 里设置镜像,1 分钟管一个仓库,更新完全自动,团队成员也不用知道上游仓库在哪。这个功能在 GitLab 免费版里是没有的(企业版才有),换过来反而捡了个福利。
4.3 轻量级 Issue / 看板足够用
GitLab 的 Issue 功能很强大,有 iteration、weight、epic、board 等等,但实际团队用下来,大部分功能都闲置了。Gitea 的 Issue 和 Project Board 比较轻量,支持 Label、Milestone、Multiple assignee、看板拖拽,对 10-30 人的小型研发团队完全够用。尤其是看板视图,操作非常跟手,不用刷新、不用等接口。我们团队现在核心项目的需求管理就放在 Gitea 里。如果你需要更重的项目管理流程(比如 Scrum 的 sprint 规划、燃尽图这些),建议搭配一个专门的工具(如 ClickUp、YouTrack),不要纠结在 Git 服务里实现所有功能。
5. 舍弃的这部分,得提前想明白
老实说,Gitea 不是万能的,有些 GitLab 的能力它没有,或者说实现方式不一样。如果你的团队刚好靠这些功能吃饭,那就要慎重考虑。我把我们舍弃的和替代的方案全部列出来。
5.1 内置 CI/CD 能力对比
GitLab CI 是 GitLab 的一大卖点,.gitlab-ci.yml 直接放在仓库里,Runner 注册后就能跑 pipeline,MR 集成状态、环境部署、流水线可视化全都做得很完整。而 Gitea 自身没有官方的 CI/CD 系统,官方推荐的是对接第三方的 Drone / Woodpecker / Jenkins。
我们的做法是:继续用之前就在用的 Jenkins,通过 webhook 触发构建。Gitea 本身也支持 Jenkins 插件(Gitea Plugin),但实测直接用 webhook 更稳定,少一层插件维护成本。如果你之前就是 GitLab CI 的重度用户,迁移到 Gitea 可能要花点时间改造 pipeline 脚本,把 GitLab CI 的 yaml 改成 Jenkinsfile 或 Woodpecker 的配置。
下面是一个 Jenkins Pipeline 的简单示例:
groovy复制pipeline {
agent any
triggers {
GenericTrigger(
genericVariables: [
[key: 'GITEA_PUSH_REF', value: '$.ref'],
[key: 'GITEA_REPO', value: '$.repository.full_name']
],
token: 'secret-token'
)
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'make build'
}
}
stage('Test') {
steps {
sh 'make test'
}
}
}
post {
success {
echo "Build OK: ${GITEA_REPO}"
}
}
}
这个 Pipeline 配合 Gitea 的 webhook 使用,功能上和 GitLab CI 的基本 pipeline 很接近。当然,GitLab CI 在 Runner 管理和 pipeline 视图的集成度上更强,这是客观事实。
5.2 容器镜像仓库(Registry)的替代
GitLab 内置了 Container Registry,你可以直接把 Docker 镜像推到 GitLab 项目下,MR 流水线里直接引用。Gitea 没有内置 Registry 功能(官方在 roadmap 里提过,但一直没正式实现)。
我们团队用 Docker Hub 私有仓(付费) + 阿里云 ACR 来替代。如果你的数据不能出内网,可以单独部署一个自建 Registry(registry:2 镜像,不到 100MB),轻量而且完全不依赖 Git 服务。这也是顺手的事,因为反正都来自建了,不差一个 Registry。
5.3 代码搜索与仓库浏览体验
GitLab 有全局代码搜索(免费版有基础版),Gitea 则依赖底层 git grep,搜索功能比 GitLab 弱不少。Gitea 也支持在 Web 界面浏览代码,但没有 GitLab 那种「在仓库内做类 IDE 的代码跳转、引用查找」的体验。对于大多数时候「看代码 + 改代码」的开发场景,这个差距不大,但如果你经常需要全局检索一段特定代码、跨仓库搜索,Gitea 会让你有点怀念 GitLab 的搜索。
好在我们日常开发用的 IDE 本来就带本地全仓库代码搜索,Web 端搜索用得多不多,看团队习惯。
6. 迁移后两周内的运维问题清单(含排查思路)
再补一段迁移时的运维经验。任何服务切换,前两周是坑最多的时候。我们遇到了一些问题,提前列出来,你们可以少走弯路。
6.1 「login failed. check api token or gitlab version」是怎么出现的
标题里那句热搜词「login failed. check api token or gitlab version. log in via git if the version is below 14.0」其实是我们迁移后遇到的一个典型报错。当时同事在用 IDE 连接 Gitea 时,发现某些仓库提示 API token 检查失败。我排查了半天,最后发现是 IDE 的 GitLab 插件还在用老的 GitLab API 版本去访问新地址,而 Gitea 的 API 与 GitLab API 兼容层处理方式不同。解决办法很简单:建议在 IDE 里删除旧的 GitLab 连接,重新添加 Gitea 的 SSH/HTTP 方式,不要走兼容模式。另外如果是旧版本 GitLab 的 API token 迁移到 Gitea,基本会失效,需要到 Gitea 设置里重新生成 token。
6.2 大文件提交限制的配置
因为团队之前遇到过大文件误提交的问题,迁移到 Gitea 后我第一时间设置了上传大小限制,避免再次出现「服务器被 1GB 视频撑爆」的事故。配置在 Gitea 的 app.ini 里,需要修改这几个参数:
ini复制[repository]
MAX_SIZE = 1024
ENABLE_PUSH_CREATE_ORG = true
ENABLE_PUSH_CREATE_USER = true
[server]
MULTIPLE_FILE_UPLOAD = true
MAX_FILE_UPLOAD = 20
MAX_SIZE 是限制单个文件上传大小,单位是 MB。这里设置成 1024,就是限制在 1GB,但实际我们希望不要超过 100MB,后来又调成了 100。还要配合 git 服务端的钩子:
bash复制# 在 Gitea 的仓库 hooks 目录下创建 pre-receive 钩子
#!/bin/bash
# 判断 push 的 commit 里是否有超过 50MB 的文件
# 如果有,拒绝 push
zero_commit="0000000000000000000000000000000000000000"
while read oldrev newrev refname; do
if [ "$oldrev" = "$zero_commit" ]; then
# 新分支,检查所有 object
files=$(git ls-tree -r --long $newrev | awk '$4 > 50000 {print $5}' | head -20)
else
# 增量 push,检查变更的 object
files=$(git diff-tree -r --long $oldrev $newrev | awk '$6 > 50000 {print $7}' | head -20)
fi
if [ -n "$files" ]; then
echo "ERROR: 超过 50MB 的大文件禁止 push"
echo "以下文件过大:"
echo "$files"
exit 1
fi
done
这个钩子文件要放到仓库的 custom_hooks/pre-receive 里,或者通过 Gitea 管理后台的「管理钩子」配置,才能对每个仓库全局生效。实际经验:光靠 Gitea 的配置文件限制不够,因为 Gitea 配置限制的是 Web 上传和 LFS 的大小,真正 push 大文件走的是 git 协议,必须配合插件或 git hook 才能真正拦住。
6.3 Backup / Restore 流程的坑
GitLab 自带备份工具 gitlab-backup create,一键备份所有仓库和数据库。Gitea 的备份策略不太一样,它的数据分为两部分:仓库文件(git repos)和数据库(SQLite 或 MySQL)。Docker 安装时,仓库在 /data/git/repositories,数据库在 /data/gitea/gitea.db(如果用 SQLite)。
我们的备份策略就是整目录备份:
bash复制tar czf gitea_backup_$(date +%Y%m%d).tar.gz /data/gitea
恢复时直接解压即可,亲测过好几次,干净利落。但有一点要注意:如果 Gitea 是用 Docker 跑的,备份时最好先把容器停掉,或者用 docker exec 在容器内跑 gitea dump,这样能把数据库文件和仓库文件的一致性保证住。直接热拷贝 SQLite 文件有时会导致数据库损坏,尤其是 push 操作频繁的时候。
Gitea 自带 gitea dump 命令,官方推荐用这个,它会生成一个包含仓库和数据库的压缩包:
bash复制docker exec -u git <container_name> /usr/local/bin/gitea dump \
--config /data/gitea/conf/app.ini \
--file /tmp/gitea_dump.zip
然后把这个 zip 定期同步到异地/备份机。恢复时注意 gitea 版本要和备份时一致,数据库版本不匹配会导致 migration 失败。这就是你要备份恢复时优先考虑 gitea dump 而不是单纯 tar 的原因。
6.4 升级策略:18/19 版本顺序别跳
Gitea 的版本升级也有讲究,虽然是轻量项目,但两个大版本之间有时会有数据库 schema 变化,如果你像我一样喜欢用 Docker 跑最新 tag,那就要注意「不要跨大版本直接升」。GitLab 也一样,官方明确说升级不能跳大版本。比如你当前是 1.21,想升到 1.23,最佳做法是先升 1.22 再升 1.23,而不是直接拉最新。虽然 Gitea 一般没有 GitLab 那么严格,但为了稳妥,还是建议按版本顺序走,尤其是数据库是 MySQL 的场景。
另一个小技巧:升级前先备份 /data/gitea/conf/app.ini 和数据库,因为 Gitea 升级后有时会自动改配置文件格式(比如引号、注释),如果自定义配置丢失,你还能迅速回滚。
7. 什么人适合学我们,什么人别学
最后写一点选型层面的个人思考,这部分可能比部署教程更值钱。
7.1 适合从 GitLab 迁到 Gitea 的团队画像
- 团队规模 5-50 人,仓库数量 10-200 之间。
- 自托管 Git 服务,看重资源消耗和运维成本。
- 主要需求是代码托管、Code Review、Issue 管理、Webhook 触发 CI。
- 不需要复杂的 Kafka 级事件流、审计日志、多集群管理等企业级特性。
- 团队能接受「工具简化、流程自建」的理念。
如果你符合以上条件,我强烈建议试一下 Gitea,哪怕是先在一台闲置机器上跑一周,对比一下感受。按我们团队的经验,从 GitLab 切过来后,不只是省了内存,连带着服务器部署、紧急故障修复的响应速度都快了很多——因为终于不用在一堆组件里找问题了。
7.2 不适合迁移的场景
- 公司有合规审计需求,必须保留详细的用户操作日志、访问审批记录。
- 团队已经深度使用 GitLab CI 的 pipeline 编排、环境管理、MR 集成状态,迁移成本远大于收益。
- 有大量历史 Issue / MR 数据必须完整保留在新平台(因为迁移工具很难完美迁移)。
- 团队人数上百,有复杂的分组权限模型和多级审批流。
- 你正在用 GitLab 的 Free Tier,预算确实有限,但对托管服务的稳定性要求极高——这时 Gitea 反而是一个更合适的选择。
7.3 最后说下我们的现状
切换半年,团队没有任何人提出要换回 GitLab。唯一偶尔的抱怨是「找不到以前那个谁谁谁提过的 Issue」,但那是因为我们旧系统只读归档了,搜不到。代码、MR、Webhook 构建这些核心流程,Gitea 都完全扛得住。服务器内存占用从 10GB 降到不到 1GB,顺手把原来那台 8C16G 的虚拟机规格降到了 4C8G,每年云资源费用省了差不多 40%。对一个小团队来说,这个成本优化是实实在在的。
回想这件事,我对「技术选型」最大的感悟是:很多时候我们坚持用某个重量级工具,不是因为真的需要它的全部功能,而是因为「大家都会」所以「懒得换」。但当你真的花两个下午把迁移做完、把流程跑通之后,会发现所谓的迁移成本,大部分是自己吓自己。轻量、够用、可控,对于我们这种规模的团队来说,比全家桶更有价值。
如果你也在 GitLab 和轻量方案之间纠结,建议直接先拉一个真实项目的镜像仓库在 Gitea 上跑一跑,用两天感受下日常开发流是否有阻塞。没有阻塞,就大胆切;有阻塞,你也清楚地知道阻塞点在哪、能不能绕过。选型这件事,永远是用数据说话,别靠「别人都在用」的惯性。
