在日常开发中,把代码托管到 GitHub、Gitee 等公网平台已经很普遍,但在内网环境、个人服务器或需要代码私有的场景下,自己在一台 Linux 服务器上搭一个 Git 远程仓库依然非常实用。Git 原生就支持通过 SSH 协议读写仓库,不需要任何额外服务,几分钟就能搞定。
本文整理两种最常见的方案:
- 裸仓库(bare repo)+ SSH:轻量、零依赖,适合个人或小团队;
- GitLab:带 Web 界面、权限管理、CI 的完整平台,适合团队长期使用。
一、为什么远程仓库要用「裸仓库」
先解释一个概念。普通 git init 出来的仓库带有工作区(能看到签出的文件),而远程仓库应该用 git init --bare 创建,称为裸仓库:
- 裸仓库只有
.git目录里的那些内容(objects、refs、HEAD 等),没有工作区文件; - 如果往非裸仓库 push,Git 会拒绝并提示
refusing to update checked out branch,因为推送会导致工作区和分支状态不一致; - 裸仓库的目录名习惯上以
.git结尾,如my-project.git,一眼就能识别。
所以远程仓库的标准姿势是:服务器上放裸仓库,开发者本地 clone / push。
二、方案一:裸仓库 + SSH(推荐)
这是最简单也最常用的方案,本质是:服务器上建一个用户,裸仓库放在这个用户目录下,开发者通过 SSH 读写。
1. 安装 Git
# Debian / Ubuntu
sudo apt update
sudo apt install git
# CentOS / RHEL
sudo yum install git
安装完用 git --version 确认。
2. 创建专用用户 git
不建议用 root 或已有用户托管仓库,单独建一个 git 用户更干净,也能顺便限制它只能做 Git 操作:
# -m 创建家目录,-s 指定 shell(下一步会换成 git-shell)
sudo adduser git
设置密码(如果要用密码登录),或者直接跳过、只用密钥登录:
sudo passwd git
3. 初始化裸仓库
切到 git 用户,创建仓库:
sudo su git
cd ~
mkdir my-project.git
cd my-project.git
git init --bare
也可以一步到位:
git init --bare ~/my-project.git
完成后 /home/git/my-project.git 目录里能看到 HEAD、objects、refs 等,没有工作区文件,说明创建成功。
4. 配置 SSH 登录
方式 A:密钥登录(推荐)在每台开发机上把公钥放到服务器的 git 用户下。最方便的做法是开发机上有 ssh-copy-id(Linux/macOS 自带):
# 开发机上执行,如果还没有密钥先 ssh-keygen -t ed25519
ssh-copy-id git@服务器IP
没有 ssh-copy-id 的话(比如 Windows PowerShell),手动追加:
# 开发机先查看公钥内容
cat ~/.ssh/id_ed25519.pub
# 服务器上将内容追加到 authorized_keys
sudo su git
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ssh-ed25519 AAAA... 备注" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
注意 .ssh 目录权限必须是 700、authorized_keys 必须是 600,否则 SSH 会拒绝使用密钥。
确认 /etc/ssh/sshd_config 中 PasswordAuthentication yes,改完重启 sshd:
sudo systemctl restart sshd
5. 本地使用远程仓库
克隆已有仓库:git clone git@服务器IP:~/my-project.git
用户名是 git,冒号后面是服务器上仓库的路径,相对路径基于 git 用户家目录,所以 ~/my-project.git 和 my-project.git 等价。
cd my-project
git remote add origin git@服务器IP:my-project.git
git push -u origin master
之后日常 git pull / git push 和用 GitHub 没有区别。也可以配置域名和 SSH 别名,写成 git@git.example.com:my-project.git,甚至用 ssh -p 2222 自定义端口。
6. (可选)把 git 用户锁进 git-shell
默认情况下,拿到 git 用户 SSH 权限的人可以登录进服务器 shell,这在多人共享仓库时是个安全隐患。Linux 自带 git-shell,限制该用户只能执行 git-receive-pack(push)、git-upload-pack(pull)、git-upload-archive 三种操作:
# 确认系统里有 git-shell
cat /etc/shells
# 若没有 git-shell 这一行,手动加进去
echo /usr/bin/git-shell | sudo tee -a /etc/shells
# 修改 git 用户的登录 shell
sudo usermod -s /usr/bin/git-shell git
配置后,有人试图 ssh git@服务器IP 登录会看到提示:
fatal: This operation must be done over a binary transport
而正常的 clone / push 不受影响。
7. (可选)配合 Nginx 实现 HTTP 匿名只读
裸仓库 + SSH 只能走 SSH 协议。想让别人像访问 GitHub Pages 一样用 git clone http://... 只读克隆,可以装 git-http-backend(随 git 一起提供),用 Nginx 反代到它。这里给出核心配置:
location /git/ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
fastcgi_param GIT_PROJECT_ROOT /home/git;
fastcgi_param GIT_HTTP_EXPORT_ALL;
# 只读:禁止 push
fastcgi_param REMOTE_USER $remote_user;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
个人服务器上如果只需要自己推拉,这一步可以省略,SSH 方案已经够用。
三、方案二:GitLab(带 Web 界面的完整平台)
如果需要 Web 端管理仓库、分配权限、Review 代码、跑 CI,可以部署 GitLab CE(社区版,免费)。推荐用 Docker 方式部署,省事且方便升级:
# 拉起 GitLab CE,映射 SSH 端口 2222 避免和宿主 sshd 冲突
sudo docker run -d \
--name gitlab \
--restart always \
-p 80:80 -p 443:443 -p 2222:22 \
-v /srv/gitlab/config:/etc/gitlab \
-v /srv/gitlab/logs:/var/log/gitlab \
-v /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
几点注意:
- GitLab 比较吃内存,建议服务器至少 4GB 内存;
- 首次启动初始化需要几分钟,用
docker logs -f gitlab观察; - 初始 root 密码在容器内
/etc/gitlab/initial_root_password文件中,登录后尽快修改; - 克隆地址形如
ssh://git@服务器IP:2222/group/project.git,要在 Web 界面「Preferences → SSH Keys」里添加公钥。
后续创建项目和推送流程就和管理 GitHub 一样:Web 上新建项目 → 本地 git remote add origin <地址> → git push。
四、常见问题排查
| 现象 | 原因与解决 |
|---|---|
Permission denied (publickey) | 公钥没加到 git 用户的 authorized_keys,或 .ssh / authorized_keys 权限不对(700 / 600) |
push 时报 refusing to update checked out branch | 远程目录不是裸仓库,用 git init --bare 重建 |
能 SSH 登录但 clone 报 repository does not exist | 仓库路径写错,冒号后面相对 git 用户家目录,先用 ls 确认路径 |
换了 git-shell 后 push 失败 | /etc/shells 里必须登记 git-shell 的路径,否则 sshd 认为该 shell 非法 |
| 多用户共用仓库权限混乱 | 让大家都用 git 用户 + 各自的 SSH 公钥 push,文件天然同属 git;或改用方案二做权限管理 |
五、总结
- 个人或小团队内网协作,裸仓库 + SSH 是最省心的方案:
git init --bare建仓库,公钥进authorized_keys,本地直接 clone/push; - 加上
git-shell可以把托管用户锁成只能跑 Git 操作,更安全; - 需要 Web 界面、权限体系、代码审查和 CI 时,再上 GitLab,Docker 一条命令即可部署。
两种方案不冲突,很多团队的演进路径就是先用裸仓库顶着,成员多了再迁到 GitLab——反正 Git 仓库本身可以无损迁移。