自建 Git 仓库:裸仓库与 Gitea 两条路

GitHub 已经很好用了,但私有仓库、不想依赖第三方、或者就是想折腾的时候,一台小服务器就能搞定。

一、先问要不要自建

情况建议
只是要私有仓库GitHub/GitLab 免费私有仓库够用
不信任第三方、数据要自控自建
要 CI/CD 全套自建 Gitea + Actions,或直接托管
只是自己两台机器同步裸仓库就够,别装 Gitea

我的判断:个人用裸仓库,小团队用 Gitea。别一上来就装全套。

二、方案 A:裸仓库(零依赖)

不需要装任何软件,有 SSH 就行。

服务端

bash
sudo apt install -y git

# 建一个 git 用户(可选,隔离用)
sudo adduser --disabled-password git
sudo su - git
mkdir -p ~/.ssh && chmod 700 ~/.ssh

# 建仓库(注意 --bare)
mkdir -p ~/repos/baize-site.git
cd ~/repos/baize-site.git
git init --bare

裸仓库没有工作区,只有 .git 里的内容,专供推送。

客户端

bash
# 已有仓库就加个 remote
git remote add origin git@example.com:repos/baize-site.git
git push -u origin main

# 或者克隆
git clone git@example.com:repos/baize-site.git

自动部署(post-receive 钩子)

这是裸仓库最有价值的用法:push 上去自动更新网站目录

bash
# ~/repos/baize-site.git/hooks/post-receive
#!/usr/bin/env bash
set -e

TARGET="/var/www/baize"
GIT_DIR="/home/git/repos/baize-site.git"
BRANCH="main"

while read -r oldrev newrev ref; do
  if [[ "$ref" = "refs/heads/$BRANCH" ]]; then
    echo "==> 部署 $BRANCH 到 $TARGET"
    git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f "$BRANCH"

    # 部署后要做的事
    cd "$TARGET" && npm run build 2>/dev/null || true
    sudo systemctl reload nginx
    echo "==> 完成"
  fi
done
bash
chmod +x hooks/post-receive

# 目标目录要能让 git 用户写
sudo chown -R git:www-data /var/www/baize
sudo chmod -R g+w /var/www/baize

三、方案 B:Gitea(有界面)

Gitea 是轻量的自托管 Git 服务,Go 写的,资源占用很低(几十 MB 内存)。

bash
# 1. 建用户和目录
sudo adduser --disabled-password --home /var/lib/gitea git

# 2. 下载二进制(一行命令,无需编译)
sudo mkdir -p /usr/local/bin
sudo wget -O /usr/local/bin/gitea https://dl.gitea.com/gitea/1.22.0/gitea-1.22.0-linux-amd64
sudo chmod +x /usr/local/bin/gitea

# 3. 目录权限
sudo mkdir -p /var/lib/gitea/{custom,data,log}
sudo chown -R git:git /var/lib/gitea
sudo chmod -R 750 /var/lib/gitea

systemd 服务:

nginx
# /etc/systemd/system/gitea.service
[Unit]
Description=Gitea
After=network.target

[Service]
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/var/lib/gitea GITEA_WORK_DIR=/var/lib/gitea

[Install]
WantedBy=multi-user.target
bash
sudo systemctl enable --now gitea
curl -I http://127.0.0.1:3000

四、Nginx 反代

nginx
server {
    listen 443 ssl http2;
    server_name git.bzii.cn;

    ssl_certificate     /etc/letsencrypt/live/git.bzii.cn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/git.bzii.cn/privkey.pem;

    # 大文件推送(仓库可能很大)
    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-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Git 大推送需要长超时
        proxy_read_timeout 720s;
        proxy_send_timeout 720s;
    }
}

首次访问 Web 界面完成安装向导,数据库选 SQLite(小团队足够)。

关键配置项 /etc/gitea/app.ini

nginx
[server]
DOMAIN           = git.bzii.cn
ROOT_URL         = https://git.bzii.cn/
SSH_DOMAIN       = git.bzii.cn
SSH_PORT         = 2222
DISABLE_SSH      = false

[service]
DISABLE_REGISTRATION = true      # 关闭公开注册
REQUIRE_SIGNIN_VIEW  = true      # 必须登录才能看

[security]
INSTALL_LOCK = true

[repository]
DEFAULT_BRANCH = main

五、备份

Gitea 自带 dump 命令,一条搞定:

bash
sudo -u git /usr/local/bin/gitea dump -c /etc/gitea/app.ini -w /var/lib/gitea -f /backup/gitea-$(date +%F).zip

加到定时任务:

bash
0 4 * * * sudo -u git /usr/local/bin/gitea dump -c /etc/gitea/app.ini -w /var/lib/gitea -f /backup/gitea-$(date +\%F).zip
# 清理 30 天前的
0 5 * * * find /backup -name "gitea-*.zip" -mtime +30 -delete

裸仓库的备份更简单,直接打包:

bash
tar czf repos-$(date +%F).tar.gz /home/git/repos

六、迁移

从别处搬到自建:

bash
# 镜像克隆(包含所有分支、标签)
git clone --mirror https://github.com/you/repo.git
cd repo.git
git push --mirror git@example.com:repos/repo.git

反向(自建 → 别处)同理。

七、SSH 端口冲突

服务器 22 端口已被系统 SSH 占用,Gitea 用 2222:

bash
git remote add origin ssh://git@git.bzii.cn:2222/you/repo.git

嫌难看可以在 ~/.ssh/config 里配:

bash
Host gitea
    HostName git.bzii.cn
    Port 2222
    User git
    IdentityFile ~/.ssh/id_ed25519
bash
git clone gitea:you/repo.git

八、安全

  • 关闭公开注册(除非你想要公开服务)
  • 只用 SSH 密钥或强密码
  • 定期更新 Gitea(wget 换新版二进制即可)
  • 仓库目录权限正确
  • 有备份并验证过能恢复
  • 后台管理页面不要暴露到公网(或加 IP 白名单)
bash
# 升级 Gitea:停服务 → 换二进制 → 启服务
sudo systemctl stop gitea
sudo wget -O /usr/local/bin/gitea.new <新版URL>
sudo mv /usr/local/bin/gitea.new /usr/local/bin/gitea
sudo chmod +x /usr/local/bin/gitea
sudo systemctl start gitea

九、成本对比

方案成本运维适用
裸仓库0极低个人、自动化部署
Gitea一台小服务器低(定期备份 + 升级)小团队
GitLab CE4GB 内存起步需要完整 DevOps
托管平台00绝大多数情况

如果只是想在自己服务器上放代码,一个裸仓库 + post-receive 钩子就够了,两分钟配完,还能顺带实现自动部署。