代码放哪里,决定了后面构建、发布、权限、审计往哪走。把私有仓库托付给国外平台,动不动就遇到网络不畅、隐私顾虑、功能要付费;而 Gitea 是一个只有几十 MB、启动极快的自托管 Git 服务,再配合 Gitea Actions,它把"代码托管 + CI/CD + 产物发布"整合成一件在你 VPS 上就能跑起来的事。这篇讲全链路,从建仓到第一个自动化流水线。
一、Gitea 的定位:轻得不像一个"GitHub 替代品"
Gitea 的卖点不是功能堆砌,而是把"够用的 Git 体验"压到极小的资源成本里:单容器内存通常 200MB 以内,配合 SQLite 可行单机部署。它支持的仅是 GitHub 的常用子集(Issues、PR、Webhook、Package registry、Actions、LFS、wiki),但恰恰能覆盖绝大多数个人与团队的自托管需求。先把服务用 Compose 起起来:
services:
gitea:
image: gitea/gitea:1.22
environment:
- GITEA__server__DOMAIN=git.example.com
- GITEA__server__SSH_PORT=2222
- GITEA__database__DB_TYPE=sqlite3
volumes:
- ./data:/data
ports:
- "2222:22"
镜像内数据全落在 /data 卷里,升级与恢复都围绕这一份卷。若前面容器走的是 Traefik,这里不需要对外发布 80/443,只要把这台容器的 HTTP 交给网关即可;SSH 端口单独暴露,因为 SSH (2222) 是 Git 操作的另一条通道。
二、注册一个专属 Runner:让流水线有执行环境
Gitea Actions 沿用 Workflow 的概念,但执行者是 act_runner:一个独立的守护进程,向 Gitea 轮询待执行任务,在本地(或独立容器里)跑 job。先在后台创建 token 再启动 runner:
# 在 Git 仓库的 Settings→Actions→Runners 生成 token
gitea actions generate-runner-token
# 用官方 act_runner 镜像自注册
docker run -d --name gitea-runner \
-e GITEA_INSTANCE_URL=https://git.example.com \
-e GITEA_RUNNER_REGISTRATION_TOKEN=$TOKEN \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $PWD/runner:/data \
gitea/act_runner:latest
共享 Docker socket 让 runner 能启动容器化 job(services 依赖),代价是给了它较高特权——只让信任的 runner 挂 socket,更多考虑是把 socket 换成 dind(Docker-in-Docker)等更隔离的方式,权衡与这一节后面会讲到。
三、写第一个 workflow:构建镜像并推送
workflow 是一个 .gitea/workflows/*.yml 文件,触发器写在你关心的分支操作上。一个最小且实用的流程是"代码推到 main 就自动构建并推送镜像到 Gitea Registry":
name: build
on:
push:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.example.com -u ${{ github.actor }} --password-stdin
- name: Build and push
run: |
docker build -t git.example.com/${{ github.repository }}:${{ github.sha }} .
docker push git.example.com/${{ github.repository }}:${{ github.sha }}
这里把镜像 tag 定为 commit 的 SHA,是让"回滚到某个版本"和"某个提交对应哪个镜像"都能精确对上的关键做法。重点看两点:secrets 存敏感信息(Registry 密码、部署密钥),不要写死在文件里;uses 里的 action 默认从仓库拉,自托管环境下要先把常用 action 缓存或改为本地路径。第一次跑 workflow 后去 Actions 页看日志是最直观的排障入口,不要凭直觉试。
四、把 CI 从"构建"再往前推一步:CD 部署联动
CI 的终点是可信产物,CD 才是把新版本真正送上线。Gitea Actions 通常不会直接远程登录目标机去重启服务,更稳妥的做法是让流水线把镜像推到仓库后,触发目标主机上的一个更新动作:用 Webhook 或 SSH 到你运行 docker compose up -d 的机器。为了不在 pipeline 里暴露整台机器的登录密钥,更清的做法是"信任链":流水线只负责"构建并推镜像 + 通知",部署由目标机上一个受保护的用户通过绝对可控的一条命令完成。把这两层分开,会比让每个 job 都能 SSH 各台机器安全得多。
五、别把自己锁在单机 Runner:并发与隔离
一台 Runner 同时只会跑有限个 job,而 job 之间的环境隔离,要么靠 run 容器(每 job 一个新容器),要么靠标签(label)把不同的 job 分给不同 Runner。自托管规模不大时,最省心的是让每个 job 在独立容器里跑(runs-on 对应的 label 走容器 executor),避免多个 build 互相污染缓存。镜像缓存:在 build step 里用 --cache-from --cache-to(BuildKit)把层缓存写回 registry,能显著缩短第二个 job 的构建时间——这是一个本地 CI 最容易被人忽略的优化点。
六、备份与安全:自己的 Git 也要自己的备份
Gitea 的数据核心是 /data 卷:仓库 + 数据库 + 附件。备份用 gitea dump(导出 zip)或直接快照整个卷都行,但要按上一轮 restic 的思路异地保存。安全上第一优先级是给 admin 账户开强制两步验证,并关闭允许公网注册(除非这是刻意开放的实例);如果还暴露 SSH,考虑禁 root、限制仅已知用户。Git 服务一旦被攻破,等于账户的代码和密钥库都露出去了,它的加固等级要比普通 Web 应用更高一档。
结语
Gitea 加 Gitea Actions 的价值,是把"私有代码、自动构建、自动发布"这串过去要串三个外部服务才能完成的事,收敛成一台 VPS 上的一个轻量服务。轻体积、低内存、Compose 可编排,让它适合作为自建技术栈的"编排中枢"。配好一个"推 main → 构建 → 推镜像 → 通知部署"的最小闭环后,每次合代码带来的不再是"手动登服务器拽镜像",而是一条可见、可重放、可回滚的流水线。这套东西上手不难,但把"托管 + 构建 + 发布 + 备份"四个层面想清楚,才是它真正值回票价的地方。


