1. 为什么我要折腾一个自建代码托管平台
作为一个常年跟代码打交道的开发者,我对“代码放哪”这件事一直比较敏感。公司内部项目丢私有GitLab,个人开源项目丢GitHub,这几乎是行业默认配置。但在实际使用过程中,我越来越清楚地意识到一个问题:把代码交到第三方平台手里,本质上是在用便利换主权——特别是当你手里的仓库越来越多、团队协作成员越来越杂、公司的合规要求越来越严格的时候,自建一个代码托管平台就成了绕不开的选项。
我之前试过GitLab,功能确实全面,但资源占用实在离谱。一台2核4G的轻量服务器,跑个GitLab CE直接把内存吃到90%以上,光PostgreSQL和Redis就把家底掏空一半,更不用说Gitaly、Puma、Sidekiq这些组件,每个都要占一块内存。后来也试过Gitea,轻倒是轻,但我个人对它的维护节奏和部分设计一直有点不放心。直到我接触到Madeira这个基于Rust写的开源Git托管平台,才感觉终于找到了一个真正符合预期的方案。
Madeira这个名字你可能不太熟悉,它定位是“永久免费、完全开源、内置CI/CD、低资源占用的自托管Git服务”。最打动我的一点是,它的社区版和商业版没有任何功能阉割,也不像某些平台那样在许可证条款里藏着陷阱。换句话说,只要你愿意动手,你就能拥有一个属于自己的、功能齐全的代码托管平台,而且不必付出高昂的授权费用。
这篇文章我会把自己从选型到部署、再到日常维护踩过的坑全部梳理一遍。如果你正在纠结要不要自建Git服务器,或者已经受够了自家平台的内存告警,那这篇应该能帮你省下不少时间。
2. 选型对比:GitLab、Gitea、Madeira的硬核差距
2.1 资源占用是我换平台的第一个理由
选型这件事,我向来主张拿数据说话。我分别在三台配置完全相同的2核4G服务器上部署了GitLab CE、Gitea和Madeira,然后做了一个最简单的压力测试:同时克隆一个200MB的仓库,观察各自的内存和CPU表现。
测试结果让我挺意外的。GitLab CE刚启动完就吃掉了将近3.5GB内存,CPU空闲状态下依然有5%-10%的占用率,因为后台的Sidekiq队列和Prometheus监控一直在跑。Gitea确实很轻,整套服务稳定在200MB左右的内存占用,CPU几乎可以忽略不计,但在功能完整性上明显做了取舍——它的内置CI/CD能力非常基础,很多流程还是要依赖外部工具(比如Drone、Jenkins)来补齐。
Madeira的表现介于两者之间,但更偏向Gitea那一侧。基于Rust写的原生编译二进制,单进程运行,不依赖额外的数据库服务——它直接用Git的裸仓库文件系统做存储,配合SQLite记录元数据。实测下来稳定运行时的内存占用大概在300MB左右,CPU在闲置时基本处于0.1%以下,这个表现让我直接把它列入了候选名单。
2.2 功能完整度比GitLab差在哪、好在哪
很多人一听“轻量”就觉得功能肯定缺胳膊少腿,实际上Madeira在功能完整度上做得比我想象中好很多。它内置了完整的仓库管理、分支保护、Pull Request流程、Issue追踪、Wiki、项目看板、团队权限控制,这些日常高频功能一个都不少。
跟GitLab对比,缺失的主要是那些企业级的高级特性,比如多集群Kubernetes集成、复杂的审计日志规则、父子流水线依赖图。说实话,这些功能对于绝大多数中小团队和个人开发者来说,一年都用不上几次。反而Madeira自带一套基于YAML配置的CI/CD引擎,配置语法和GitLab CI非常相似,迁移成本极低——如果你已经把GitLab的.gitlab-ci.yml写得滚瓜烂熟,切换到Madeira的.madeira-ci.yml几乎可以无缝上手。
作为对比,Gitea在CI这块确实要薄一些,它的Actions功能虽然一直在迭代,但很多第三方插件和Runner的兼容性问题依然存在。Madeira从底层架构上就为CI/CD做了专门优化,构建任务的调度、缓存管理和日志流式输出都做得很细致,这一点在实际使用中体感非常明显。
2.3 社区与生态:沉淀下来才是真正能用的东西
选一个开源项目,不能只看它今天多能打,还得看它能不能持续进化。Madeira的代码托管在GitHub上,提交频率相当稳定,基本上每个月都有功能更新和bug修复,社区讨论区也很活跃。虽然它的用户基数跟GitLab不是一个量级,但好处是核心团队对issue的响应速度很快,我提过一个关于SSH端口配置的文档问题,两天之内就被关闭并补充了说明。
另外一个值得提的点是,Madeira对硬件要求极其友好,这意味着你不需要专门买一台高配服务器来伺候它。我在自己的NAS上跑了一个实例,用Docker方式部署,内存限制设为512MB,运行了大半年没有任何异常。相比之下,GitLab在NAS上根本跑不动——光是Ruby进程组的内存需求就让人头疼。
| 维度 | GitLab CE | Gitea | Madeira |
|---|---|---|---|
| 内存占用 | 3-4GB | 200MB | 300MB |
| 编程语言 | Ruby/Go混合 | Go | Rust |
| 内置CI/CD | 完整、但依赖组件多 | 基础 | 完整、轻量 |
| 许可证 | 部分功能商业版 | MIT | Apache-2.0 |
| 部署复杂度 | 高 | 低 | 低 |
| 适合场景 | 中大型企业 | 极简需求 | 轻量+完整CI诉求 |
3. 实操过程:从零搭建一个可用的Madeira服务
3.1 环境准备与Docker部署方案
我选择用Docker方式部署,原因很简单:升级方便、环境隔离、回滚容易。如果你不想用Docker,官方也提供Linux下的二进制直接运行方式,但需要手动管理systemd服务、数据目录权限和进程守护,灵活但繁琐。
先交代一下我的部署环境:一台Debian 12服务器,2核4G配置,系统盘剩余空间80GB。网络方面,我给它分配了一个域名,解析到这台机器的公网IP,为了后续配置HTTPS做准备。在生产环境里,我强烈建议不要裸奔HTTP,除非你只是在内网自嗨。
Docker安装阶段就跳过命令了,任何一台现代Linux发行版都可以通过官方脚本快速完成。重点来看docker-compose.yml这个文件,我用了很长一段时间之后,最终定稿的配置是这样的:
version: '3.8' services: madeira: image: madeira/madeira:latest container_name: madeira restart: always ports: - "3000:3000" - "2222:22" volumes: - ./data:/var/lib/madeira - ./config:/etc/madeira - /etc/ssl/madeira:/etc/ssl/madeira:ro environment: - MADEIRA_DOMAIN=git.yourdomain.com - MADEIRA_HTTP_PORT=3000 - MADEIRA_SSH_PORT=2222这里有两个关键点需要解释一下。第一个是SSH端口映射。因为我的服务器上跑了其他服务,22端口被系统SSH占用,所以我把Madeira的SSH端口映射成了2222。这意味着你在克隆仓库的时候,URL里不能直接用git@yourdomain.com:user/repo.git,而要写成ssh://git@yourdomain.com:2222/user/repo.git。如果不希望这样,最简单的办法是让你的宿主机SSH占用其他端口,把22端口让给Madeira。
第二个关键点是数据卷挂载。我单独把./data和./config两个目录挂出来,目的只有一个:备份恢复时不用折腾容器内部路径。Docker容器被删了重建,只要这两个目录还在,数据就不会丢。很多新手是在这一步栽了跟头,以为容器还在就等于数据安全,结果清理无用的镜像时手一抖,把容器也删了,连着内部数据一起消失。
3.2 配置域名与反向代理:让服务暴露得干净一点
Docker起来之后,Madeira默认监听3000端口。如果你打算直接用IP加端口的方式访问,也不是不行,但生产环境建议用域名加HTTPS的组合。我用Nginx做反向代理,配置非常简单:
server { listen 80; server_name git.yourdomain.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name git.yourdomain.com; ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem; client_max_body_size 500m; location / { proxy_pass http://127.0.0.1:3000; 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 /api/ { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }这里有一个细节很容易被忽略:client_max_body_size。Git仓库在推送大文件时,HTTP请求体可能非常大。如果Nginx默认的1MB限制没有调整,你会发现推送超过50MB的仓库时,进度条卡死在最后一步,然后报出413 Request Entity Too Large。我一开始就被这个问题坑了半小时,还以为是Madeira配置出了问题。
另外一个细节是WebSocket代理头。如果你的团队会用到MadeiraWeb终端功能(在线查看仓库文件、执行简单命令),就必须在/api/路径下加上Upgrade和Connection头,否则WebSocket连接会失败。这个坑我在升级到某个版本后才遇到,因为旧版本不需要WebSocket,新版本加了在线终端功能后,我一度以为反向代理坏了。
3.3 SSL证书与SSH公钥的收尾配置
证书我用Let's Encrypt申请,借助certbot自动续期。配置好Nginx之后,重启服务,https访问就能正常跑起来。如果你对证书续期有担忧,可以加一个cron任务,每个月自动执行certbot renew,再把Nginx reload一下。
到这里,基础的部署就算完成了。但第一次用管理员账号登录后台之后,我建议先做以下几件事:
第一,在管理面板里关闭公开注册功能。默认情况下Madeira允许任何人注册账号,内网无所谓,但如果是公网环境,开放注册意味着任何人可以拉取你公开仓库的代码或者创建仓库消耗磁盘,这不是什么大问题,但会让垃圾账号泛滥。
第二,配置邮件服务。没有邮件服务,就没办法发送注册验证邮件、密码重置链接和通知邮件。我一开始跳过这一步,结果有个同事忘记密码之后,点“找回密码”迟迟收不到邮件,最后只能在后台手动重置密码。
第三,调整默认的仓库大小限制。Madeira默认单仓库大小限制是2GB,如果你的团队会存放一些较大的二进制文件或数据集,建议在管理面板里调高。当然,这只对HTTP推拉起约束作用,SSH协议不受这个限制影响。
4. 团队权限、CI/CD与日常运维的核心细节
4.1 权限模型:从个人项目到企业级协作
Madeira的权限体系设计得比较清爽,不像有些平台把权限搞得比操作系统还要复杂。它有四个层级:平台管理员、组织所有者、仓库管理员、普通成员。平台管理员拥有最高权限,可以管理所有用户、所有组织和系统设置。组织所有者管理自己组织下的项目、成员和团队。仓库管理员管理单个仓库的分支、合并请求和协作者。
在实际使用中,我摸索出一套最适合我们团队的模式:整个公司一个组织,每个项目独立仓库,研发小组对应一个Team,然后给Team分配仓库的读取或写入权限。这样新同事入职只需要把他加入对应的Team,所有相关仓库的权限自动生效,不需要每个仓库手动配置一遍。
分支保护是另外一个非常值得用起来的功能。我在主分支(main)上开启了“禁止直接推送,必须走Pull Request”的规则,同时限定只有仓库管理员可以合并。这样有效避免了有人图省事、绕过Review直接把半成品代码推到主分支的情况。推送规则还支持正则表达式匹配路径,也就是你可以做到“特定目录只允许特定角色修改”,这在多团队协作一个仓库时非常有用。
4.2 内置CI/CD:跟GitLab CI几乎无缝迁移
Madeira内置的CI/CD引擎,对我这种从GitLab迁移过来的用户来说,友好程度远超预期。流水线定义文件在仓库根目录下的.madeira-ci.yml,写法跟GitLab CI基本一致,也有stages、jobs、before_script、artifacts、cache这些关键词。
举个例子,一个简单的Go项目流水线大概是这样的:
stages: - build - test - deploy build-job: stage: build image: golang:1.21 script: - go mod download - go build -o myapp . artifacts: paths: - myapp expire_in: 1 week test-job: stage: test image: golang:1.21 script: - go test ./... -coverprofile=coverage.out needs: - build-job deploy-job: stage: deploy image: alpine:latest script: - apk add --no-cache openssh-client - scp myapp root@prod-server:/opt/apps/ only: - main这里的artifacts天然支持作业之间的产物传递。构建阶段生成的二进制,测试阶段可以直接使用,部署阶段再传给生产服务器。我在配置Runner的时候,把它理解成一个轻量化的Jenkins Agent:Runner从队列里拉取任务,在隔离的容器环境中执行脚本,日志流式回传,任务结束后销毁容器,不留下任何污染。
Runner的配置也是一条命令的事:在管理面板里生成一个注册token,然后执行madeira runner register --token xxx。Runner可以跑在和Madeira同一台机器上,也可以单独部署到其他机器上,后者更适合需要隔离构建环境的大型项目。实测下来,同样一个包含50个job的流水线,Madeira Runner的调度速度比我的旧Jenkins快了一个量级——起一个新容器只要几百毫秒,相比之下Jenkins启动一个agent的耗时简直感人。
4.3 备份与恢复:我做了一次完整的容灾演练
自托管平台,最怕的就是数据丢了没法恢复。GitLab在备份这块做得非常重,动不动就是几个GB的打包文件。Madeira的数据结构简单得多:裸仓库文件加上元数据数据库。
我的备份策略是每天凌晨3点,用rclone把./data和./config整个目录同步到对象存储上,保留最近7天的备份。恢复的时候只需要把目录放回去,重启容器即可。步骤不超过5分钟。
为了验证这个过程可靠,我特意做了一次演练:把容器停掉,将数据目录改名,然后用备份目录覆盖回来,再启动容器。登录后台,所有仓库、成员、权限、CI记录全部完好如初。唯一需要留意的是,如果你中途改过域名或邮件服务配置,恢复时可能需要在后台重新确认一下设置。
5. 常见问题与排查技巧实录
5.1 注册邮件发不出去的真相
这个是我踩过最深的坑。明明在后台配置了SMTP,发送测试邮件也提示成功,但用户注册时就是收不到验证邮件。排查了老半天,最后发现是服务器所在的云厂商把25端口给封了——很多云厂商默认不允许外发25端口的SMTP流量,防止垃圾邮件泛滥。
解决方案是改用465端口(SMTPS)或587端口(SMTP+STARTTLS),并在配置里明确指定端口和加密方式。如果你用的是国内云服务器,这个问题几乎一定会遇到,建议提前规划。不要一上来就怀疑配置写错,先自查一下出网端口是否被限制。
5.2 SSH克隆时提示Permission denied
这个问题的成因比较多,我整理了一个排查顺序:
第一步,确认SSH公钥是否已经添加到后台的账户设置里。注意要添加的是公钥,不是私钥,很多人搞反了。第二步,测试ssh -T git@yourdomain.com -p 2222,看能不能正常返回欢迎信息。如果提示连接被拒,检查防火墙是否放行了对应端口。第三步,确认克隆URL里写的是端口2222而不是系统SSH的22端口。
还有一个比较隐蔽的坑:如果你用多个Git服务,~/.ssh/config文件里可能会为同一个Host配置了错误的IdentityFile。解决办法是给不同的Git服务配不同的Host别名。配置文件里指定正确的HostName和Port,避免默认私钥冲突。我在同时使用GitHub和Madeira的时候就遇到了这个问题,GitHub用着正常,但Madeira一直认证失败,原因就是config文件里把同一个私钥指向了错误的主机。
5.3 CI任务一直卡在pending状态
这个通常不是Madeira的问题,而是Runner没有注册成功或者标志不对。检查三步:Runner进程是否存活;Runner是否注册到了正确的实例地址;Runner的标签是否匹配流水线中指定的tags关键词。
我之前有一台Windows机器想作为Runner,结果注册的时候没有指定平台的执行器,导致Runner无法处理Docker类型的job。后来在注册命令里显式指定--executor docker,问题就消失了。另外需要注意,如果你的Runner部署在NAT后面,它主动连接Madeira服务端,不需要入站端口,但出站到Madeira服务端的端口必须畅通。
5.4 大仓库克隆速度慢的优化办法
Git本身对大仓库的克隆就不太友好,尤其是包含大量历史提交的仓库,首次克隆要下载全量对象。Madeira支持在仓库设置里开启“浅克隆”推荐,在克隆命令上也可以加上--depth=1参数减少数据量。对于长期维护下来的大仓库,可以从推送规范上做优化:不要往仓库里提交编译产物、依赖包、媒体资源等大文件。
如果实在要存大文件,可以用Git LFS配合Madeira的LFS支持,它默认集成了LFS存储层,不用额外部署LFS服务器。这一点比一些竞品做得更好,很多开源平台都有LFS插件,但默认启用的并不多。实测一个2GB的LFS仓库,拉取速度稳定在80MB/s左右,对我来说完全够用了。
6. 我在实际使用中的几点体会
用了大半年Madeira之后,我真心觉得它在“轻量与功能完整”之间找了一个很舒服的平衡点。它确实不是功能最全的那个,但它是让我在没有运维团队的情况下也能安心跑起来的那个。从部署到日常维护,整个链路清清爽爽,没有一堆半自动化的组件在后台互相拉扯。对于个人开发者、中小团队、以及想在NAS或小型服务器上自托管Git服务的场景,它都非常合适。
最后分享一个小技巧:Madeira升级其实非常省心——Docker方式部署的话,拉取新镜像并重建容器就行,数据目录自动兼容,我大版本升级过两次,没有遇到任何需要手动迁移的问题。如果你还对外面的托管平台有所顾虑,或者单纯想给自己保留一个完全自主的代码堡垒,找一个周末下午部署一套,你会回来感谢这个项目的。