# Ferrit 部署手册
适用版本:Ferrit 0.1.0
覆盖平台:**Windows** · **CentOS (7 / Stream)** · **Ubuntu (20.04+)**
---
## 0. 通用说明
### 0.1 运行原理 / 依赖
Ferrit 是一个纯 Rust 编写的自托管 Git 服务。运行时它只依赖两样东西:
1. **编译好的 `ferrit` 可执行文件**(无任何动态库依赖,单文件即可运行);
2. 系统中已安装的 **`git`**(克隆/推送通过调用 git 自带的 `git http-backend` 完成,浏览代码也依赖 `git`)。
> ⚠️ 运行环境必须能在 `PATH` 中找到 `git`,否则建仓、克隆、推送都会失败。
### 0.2 配置项(环境变量)
| 变量名 | 默认值 | 说明 |
| -------------- | ------------------- | ------------------------------------------ |
| `FERRIT_ADDR` | `127.0.0.1:3000` | 监听地址。对外直连需改为 `0.0.0.0:3000` |
| `FERRIT_DATA` | `data` | 数据目录(相对启动目录或写绝对路径) |
### 0.3 数据目录结构
```
<FERRIT_DATA>/
db.json # 用户 + 仓库元数据(JSON)
repos/<用户名>/<仓库名>.git/ # 裸 git 仓库(真正的代码对象)
```
备份只需备份整个 `FERRIT_DATA` 目录即可(见第 5 节)。
### 0.4 安全建议
- **默认只监听 `127.0.0.1`**,生产环境建议保持如此,前面挂 Nginx 反向代理并启用 HTTPS(见第 4 节),不要把 3000 端口直接暴露公网。
- 口令使用加盐多轮 SHA-256 存储,不保存明文。
- 推送(push)与私有仓库访问强制要求 HTTP Basic 认证,且仅仓库属主可写。
---
## 1. Windows 部署
### 1.1 安装依赖
1. **Git for Windows** — https://git-scm.com/download/win
安装后确认:
```powershell
git --version
```
2. **Rust 工具链**(仅编译时需要)— https://rustup.rs 下载 `rustup-init.exe`,或:
```powershell
winget install --id Rustlang.Rustup -e
```
3. **MSVC C++ 生成工具**(Rust 在 Windows 上链接需要 `link.exe`):
```powershell
winget install --id Microsoft.VisualStudio.2022.BuildTools -e
```
安装时勾选 “使用 C++ 的桌面开发”。
### 1.2 编译
```powershell
cd E:\work\tools\git
cargo build --release
# 产物:E:\work\tools\git\target\release\ferrit.exe
```
### 1.3 手动运行(验证)
```powershell
$env:FERRIT_ADDR = "127.0.0.1:3000"
$env:FERRIT_DATA = "E:\ferrit-data"
.\target\release\ferrit.exe
```
浏览器打开 http://127.0.0.1:3000 ,Ctrl+C 停止。
### 1.4 注册为 Windows 服务(开机自启,使用 NSSM)
1. 下载 NSSM:https://nssm.cc/download ,解压取 `win64\nssm.exe`。
2. 以**管理员** PowerShell 安装服务:
```powershell
# 拷贝二进制到固定目录
New-Item -ItemType Directory -Force C:\ferrit\bin, C:\ferrit\data | Out-Null
Copy-Item .\target\release\ferrit.exe C:\ferrit\bin\
# 安装服务
nssm install Ferrit "C:\ferrit\bin\ferrit.exe"
nssm set Ferrit AppDirectory "C:\ferrit"
nssm set Ferrit AppEnvironmentExtra FERRIT_ADDR=127.0.0.1:3000 FERRIT_DATA=C:\ferrit\data
nssm set Ferrit AppStdout C:\ferrit\ferrit.log
nssm set Ferrit AppStderr C:\ferrit\ferrit.log
nssm set Ferrit Start SERVICE_AUTO_START
# 启动
nssm start Ferrit
```
3. 常用管理:
```powershell
nssm restart Ferrit
nssm stop Ferrit
nssm remove Ferrit confirm # 卸载服务
```
> 不想装 NSSM 也可用任务计划程序(Task Scheduler)创建“计算机启动时运行”的任务,操作设为运行 `ferrit.exe`,并在“起始于”里填工作目录。
### 1.5 防火墙(仅当需要局域网/外网直连时)
```powershell
New-NetFirewallRule -DisplayName "Ferrit 3000" -Direction Inbound -Protocol TCP -LocalPort 3000 -Action Allow
```
直连还需把 `FERRIT_ADDR` 改成 `0.0.0.0:3000`。
---
## 2. CentOS 部署(CentOS 7 / CentOS Stream 8/9)
### 2.1 安装依赖
```bash
sudo yum install -y git gcc # CentOS 7
# 或 CentOS Stream:sudo dnf install -y git gcc
git --version
# 安装 Rust(仅编译用)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
```
> 也可在一台开发机上编译好 `ferrit` 二进制,再拷到生产服务器(同为 x86_64-linux 即可),生产机只需装 `git`。
### 2.2 编译
```bash
cd /path/to/ferrit-source
cargo build --release
# 产物:target/release/ferrit
```
### 2.3 部署文件与专用用户
```bash
sudo useradd --system --create-home --home-dir /var/lib/ferrit --shell /sbin/nologin ferrit
sudo install -m 0755 target/release/ferrit /usr/local/bin/ferrit
sudo mkdir -p /var/lib/ferrit/data
sudo chown -R ferrit:ferrit /var/lib/ferrit
```
### 2.4 创建 systemd 服务
```bash
sudo tee /etc/systemd/system/ferrit.service >/dev/null <<'EOF'
[Unit]
Description=Ferrit Git Service
After=network.target
[Service]
User=ferrit
Group=ferrit
WorkingDirectory=/var/lib/ferrit
Environment=FERRIT_ADDR=127.0.0.1:3000
Environment=FERRIT_DATA=/var/lib/ferrit/data
ExecStart=/usr/local/bin/ferrit
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/var/lib/ferrit
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now ferrit
sudo systemctl status ferrit
journalctl -u ferrit -f # 查看日志
```
### 2.5 SELinux(CentOS 默认开启)
若让 Nginx 反代到本机 3000,需放行:
```bash
sudo setsebool -P httpd_can_network_connect 1
```
### 2.6 防火墙(firewalld)
```bash
# 仅当对外直连 3000(一般不建议,推荐走 80/443 反代)
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload
```
---
## 3. Ubuntu 部署(20.04 / 22.04 / 24.04)
### 3.1 安装依赖
```bash
sudo apt update
sudo apt install -y git build-essential
git --version
# 安装 Rust(仅编译用)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
```
### 3.2 编译
```bash
cd /path/to/ferrit-source
cargo build --release
```
### 3.3 部署文件与专用用户
```bash
sudo useradd --system --create-home --home-dir /var/lib/ferrit --shell /usr/sbin/nologin ferrit
sudo install -m 0755 target/release/ferrit /usr/local/bin/ferrit
sudo mkdir -p /var/lib/ferrit/data
sudo chown -R ferrit:ferrit /var/lib/ferrit
```
### 3.4 创建 systemd 服务
> 与 CentOS 完全一致(注意 Ubuntu 的 nologin 路径是 `/usr/sbin/nologin`)。
```bash
sudo tee /etc/systemd/system/ferrit.service >/dev/null <<'EOF'
[Unit]
Description=Ferrit Git Service
After=network.target
[Service]
User=ferrit
Group=ferrit
WorkingDirectory=/var/lib/ferrit
Environment=FERRIT_ADDR=127.0.0.1:3000
Environment=FERRIT_DATA=/var/lib/ferrit/data
ExecStart=/usr/local/bin/ferrit
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/var/lib/ferrit
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now ferrit
sudo systemctl status ferrit
journalctl -u ferrit -f
```
### 3.5 防火墙(ufw)
```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 直连 3000(不推荐):sudo ufw allow 3000/tcp
```
---
## 4. 反向代理 + HTTPS(CentOS / Ubuntu 推荐)
Ferrit 自身只提供 HTTP,生产环境建议用 Nginx 终止 TLS。
### 4.1 安装 Nginx
```bash
# Ubuntu
sudo apt install -y nginx
# CentOS
sudo dnf install -y nginx && sudo systemctl enable --now nginx
```
### 4.2 站点配置
```bash
sudo tee /etc/nginx/conf.d/ferrit.conf >/dev/null <<'EOF'
server {
listen 80;
server_name git.example.com;
# git push 可能上传较大的 pack,放开请求体大小限制
client_max_body_size 2048m;
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;
# 大仓库 push/clone 友好设置
proxy_request_buffering off;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}
EOF
sudo nginx -t && sudo systemctl reload nginx
```
### 4.3 申请 HTTPS 证书(Let's Encrypt)
```bash
# Ubuntu
sudo apt install -y certbot python3-certbot-nginx
# CentOS
sudo dnf install -y certbot python3-certbot-nginx
sudo certbot --nginx -d git.example.com
```
certbot 会自动改写上面的 server 块为 443 + 自动续期。
> **已知限制**:当前版本仓库页展示的 `git clone` 地址固定以 `http://` 拼接(取自 `Host` 头)。在 HTTPS 反代下,页面显示的 clone 地址前缀会是 `http://`,但通过反代实际克隆/推送均正常。介意的话可在前端手动改成 `https://`,或后续在 `src/web.rs` 的 `host_base()` 里读取 `X-Forwarded-Proto` 做适配。
---
## 5. 备份与恢复
整个服务的状态都在 `FERRIT_DATA` 目录里。
**备份:**
```bash
# Linux
sudo systemctl stop ferrit # 可选:停服保证一致性
sudo tar czf ferrit-backup-$(date +%F).tar.gz -C /var/lib/ferrit data
sudo systemctl start ferrit
```
```powershell
# Windows
Compress-Archive -Path C:\ferrit\data\* -DestinationPath C:\ferrit\backup-$(Get-Date -Format yyyyMMdd).zip
```
**恢复:** 停服 → 用备份覆盖 `FERRIT_DATA` 目录 → 启服。
---
## 6. 升级
```bash
# 1. 拉取新代码并编译
cargo build --release
# 2. 替换二进制
sudo systemctl stop ferrit
sudo install -m 0755 target/release/ferrit /usr/local/bin/ferrit
sudo systemctl start ferrit
```
Windows:`nssm stop Ferrit` → 覆盖 `ferrit.exe` → `nssm start Ferrit`。
数据格式(`db.json` + 裸仓库)保持兼容,升级无需迁移。
---
## 7. 验证部署
部署完成后做一次端到端自测:
```bash
# 1. 服务存活
curl -I http://127.0.0.1:3000/ # 期望 200
# 2. 网页注册一个用户、建一个仓库(浏览器操作),然后:
git clone http://用户名@<服务器地址>/用户名/仓库名.git
cd 仓库名
echo "# hello" > README.md
git add README.md && git commit -m "init"
git push -u origin main # 输入 Ferrit 口令
# 刷新仓库页面应能看到文件与提交记录
```
---
## 8. 常见问题排查
| 现象 | 排查方向 |
| ---- | -------- |
| 建仓/克隆/推送报错 `cannot start git` | 服务进程的 `PATH` 找不到 `git`;确认已安装 git,Linux 服务用绝对路径或保证 `/usr/bin/git` 存在 |
| push 返回 401 | 正常的认证要求;用 `http://用户名@host/...` 形式克隆并输入口令;只有仓库属主可推送 |
| push 返回 403 | 认证用户与仓库属主不一致 |
| 私有仓库网页 404 | 预期行为:未登录或非属主不可见 |
| Nginx 502 | 后端未启动或 SELinux 拦截(CentOS 执行 `setsebool -P httpd_can_network_connect 1`) |
| 大仓库 push 被中断 | 调大 Nginx `client_max_body_size` 与 `proxy_read_timeout` |
| 外网无法访问 | 确认 `FERRIT_ADDR=0.0.0.0:3000`(或走反代)并放行防火墙 |
---
*Ferrit — 一个用 Rust 编写的极简自托管 Git 服务。*