Gitea 是一个用 Go 编写的轻量级 Git 托管服务,一个约 100MB 的二进制文件就能跑起完整的代码托管:仓库、Issue、Pull Request、Webhook、Actions、包仓库、看板全都内置,空载内存占用不到 200MB——一台 1 核 1G 的小 VPS 就能长期跑。 它和自建 Nexcloud 那类"重"服务不同,部署形态极简:Docker Compose 两个挂载点、一个 SQLite 数据库文件,数据全在你自己的盘里。我在 2026 年 10 月用它把几个私有仓库从公共平台搬回自己的服务器,迁移只涉及 git remote set-url 和一次 git push。
上面这段是结论块。下面是部署、SSH 配置、反代,以及两个真会踩的坑。

为什么要自建 Git 而不是用公共平台?
结论:自建 Gitea 换来的是「数据自主 + 零额度限制 + 内网可用」,代价是你要自己维护备份和可用性。 对账:
| 维度 | 公共平台 | 自建 Gitea |
|---|---|---|
| 私人仓库数量 | 免费版多有上限 | 无限制 |
| CI 分钟数 | 有免费额度,超了要钱 | 用自己的机器,想跑多久跑多久 |
| 数据位置 | 平台的服务器 | 你自己的盘 |
| 内网/离线访问 | 不行 | 可以 |
| 维护成本 | 零 | 备份 / 升级 / 证书自己管 |
| 备份责任 | 平台 | 你(这也是最大风险) |
判断标准:仓库里有敏感代码、或者你经常撞到免费额度、或者需要在内网拉代码——自建就值。纯粹托管开源项目,公共平台的曝光和生态仍不可替代,两者可以并存:公开项目放平台,私有项目放自建。
Gitea 部署五个步骤

第一步:写 compose.yml
services:
gitea:
image: gitea/gitea:1.22
restart: always
ports:
- "127.0.0.1:3000:3000" # Web,只给反代用
- "2222:22" # Git SSH,映射到宿主机 2222
environment:
USER_UID: "1000"
USER_GID: "1000"
GITEA__database__DB_TYPE: sqlite3
GITEA__server__SSH_PORT: "2222"
GITEA__server__DOMAIN: git.example.com
GITEA__server__ROOT_URL: https://git.example.com/
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
几个关键点:Web 端口绑在 127.0.0.1 只给反代用;Git SSH 映射到宿主机 2222(避开系统 22 端口);SSH_PORT 和 ROOT_URL 必须和实际访问方式一致——这决定了 Gitea 生成的 clone 地址对不对。

第二步:启动并初始化
$ docker compose up -d
# 浏览器开 http://你的IP:3000 进安装页
安装页里把数据库选 SQLite3(个人/小团队足够,路径填 /data/gitea/gitea.db),建第一个管理员账号,顺手把「允许注册」关掉。这一步的操作模式和 Docker Compose 多服务编排 里总结的「先校验再启动、初始化后立刻加固」完全一致。

第三步:建仓库并推代码
$ git init && git add . && git commit -m "init"
$ git remote add origin http://你的IP:3000/用户名/demo.git
$ git push -u origin main
# SSH 方式(走映射出来的 2222 端口)
$ ssh -T -p 2222 git@你的IP
$ git remote add origin ssh://git@你的IP:2222/用户名/demo.git
SSH 方式一定要在地址里显式带端口 :2222,因为你的宿主机 22 号口是系统 SSH,不是 Gitea 的。这一步是"clone 不下来"最常见的根因。

第四步:反代 + HTTPS
server {
listen 443 ssl;
server_name git.example.com;
client_max_body_size 512m;
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-Proto $scheme;
# WebSocket(通知流需要)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
证书用 certbot --nginx -d git.example.com。client_max_body_size 一定要放大(默认 1M),否则推送稍大的仓库或二进制文件就被 413 拦下。Upgrade / Connection 两行也别漏——Gitea 的实时通知走 WebSocket,缺了这两行界面上会一直转圈。这两行 header 的坑和 Nginx 反代 里遇到的完全一样。

第五步:备份与升级
$ docker exec -u git gitea /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini
# 产出 gitea-dump-*.zip,含仓库数据、配置、数据库
更省事的做法是直接备份 ./gitea 整个目录(含仓库裸库、附件、app.ini 和 SQLite 文件),停机几秒打包即可。升级只改 compose 里的镜像 tag:
$ sed -i 's/gitea:1.22/gitea:1.23/' compose.yml
$ docker compose pull && docker compose up -d
升级前必须先 dump——Gitea 的数据库迁移是单向的,降级几乎不可能。

Gitea 自带的 Actions,能不能当免费 CI?
结论:能。Gitea 1.20+ 内置了 Actions(基于 act_runner),构建、测试、部署都能跑,用的是你自己机器的算力,不消耗任何平台的分钟数。 最小可用配置——在仓库里放一个 .gitea/workflows/build.yml:
name: build
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm run build
再到 Gitea 后台 Site Administration → Actions → Runners 拿注册 token,在任意一台机器上起 runner:
$ docker run -d --restart=always \
-e GITEA_INSTANCE_URL=https://git.example.com \
-e GITEA_RUNNER_REGISTRATION_TOKEN=你的token \
-v /var/run/docker.sock:/var/run/docker.sock \
gitea/act_runner:latest
代价是 runner 自己维护、镜像自己拉——省下的是平台分钟数,花掉的是机器资源。对个人和小团队,这通常比掏钱买 CI 分钟数划算,而且流水线完全在内网可控。
两个真会踩的坑
-
clone URL 显示成
git@你的IP:仓库而漏了:2222。 根因是app.ini里的SSH_PORT没设或和实际映射端口不一致。Gitea 是照着SSH_PORT去拼 clone 地址的,设错就会给出一个连不上的地址。改 app.ini 的[server] SSH_PORT = 2222,重启容器即可。同理,ROOT_URL也要写成最终的 HTTPS 域名,否则 Web 端的跳转和 Webhook 会指到内网 IP。 -
推送大文件报 413。 两个地方要放开:Nginx 的
client_max_body_size(见上一步)和 Gitea 自己的LFS/MAX_FILE_SIZE限制。超过 100MB 的二进制文件应该走 Git LFS,在 Gitea 后台开启 LFS 后,客户端git lfs track "*.psd"即可——直接把大文件塞进普通 Git 历史会让仓库永远臃肿,这是不可逆的。
备份要和其他服务一起规划
Gitea 的备份不能独立看:它和数据库、代码、附件是同一套"要保住"的数据。 我的做法是把它并入统一的 rsync 异地增量备份 任务,每天同步 ./gitea 目录到备份机;如果 Gitea 用的是 MySQL 而不是 SQLite,那么它还应该被 MySQL 备份与 binlog 恢复 覆盖一次。代码托管服务的备份优先级高于绝大多数服务——仓库丢了是真的找不回来。
自建 Gitea 常见问题(2026)
Q:Gitea 和 GitLab 怎么选?
A:Gitea 轻量(Go 单体,几百 MB 内存),GitLab 功能重(多组件,至少 4GB 内存起)。个人和小团队用 Gitea,需要内置更完整的 DevOps 流水线时再考虑 GitLab 或其精简版。
Q:Gitea 完全免费吗?
A:开源免费(MIT),包括 Actions 之类的内建功能。唯一的成本是你自己的服务器。
Q:能不能只在内网访问?
A:能。不配反代和公网端口,只在局域网 http://内网IP:3000 访问即可;需要外部访问时再叠加反代或 Cloudflare Tunnel。
Q:SQLite 撑得住吗?
A:个人和十几个人的小团队完全够。并发写入很高、或仓库数量上百时,换 PostgreSQL / MySQL 更稳,迁移过程 Gitea 官方有文档。
Q:怎么让 SSH 走标准 22 端口?
A:把宿主机的 22 让给 Gitea(Docker 映射 22:22),但这样系统自身的 SSH 就得改端口——生产环境不推荐,因为会同时影响你的登录通道。保持 Gitea 用 2222 更安全。
一句总结
自建 Gitea 的本质是把「代码的家」搬回自己的盘:部署轻、资源省、功能全,剩下要做的就是两件事——把 SSH 端口和 ROOT_URL 配对,把备份做扎实。这两件做好了,它比任何公共平台都更让人安心。