在多人协作的 Rust 开发中,搭建私有的 Cargo 包注册中心是保护代码资产和管理内部依赖的重要需求。这不闲着没事,想着把自己写的几个 Rust 小工具整理一下,搭个私有的包注册中心。选来选去,最后决定用 Forgejo,主要是因为 Forgejo 提供了完整的包注册中心功能,支持包括 Cargo 在内的多种包管理器。
其实一开始我是想用 crates.io 的,毕竟省事。但是有些个人项目不太适合开源,而且自己搭一个注册中心感觉挺酷的,就像拥有了自己的小小生态系统。
0x00 前提条件
在开始之前,确保您已经:
- 安装并运行 Forgejo 实例
- 安装 Rust 和 Cargo 工具链 (rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh/ mise:mise use [email protected]) - 了解基本的 Git 操作
0x01 创建 Cargo 索引仓库
Forgejo 的文档说 Cargo 注册中心需要一个特殊的索引仓库,名字必须是 _cargo-index。这个设计挺有意思的,相当于用 Git 仓库来存储包的元数据,充分利用了 Git 的版本控制和分布式特性。
我们这里通过 WebUI 创建索引仓库:
- 进入用户或组织的设置页面(
git.w/user/settings/packages) - 在左侧目录找到「软件包」选项卡
- Forgejo 提供了一键创建
_cargo-index仓库的功能,所以我们只需要点击「创建索引仓库」按钮,系统会自动创建仓库并生成必要的配置文件
0x02 镜像外部仓库
如果你已经将项目推送到了您的 Forgejo 实例,或者不打算关联仓库和软件包,则可以跳过此步骤。
- 在 Forgejo 右上角点击「+」按钮,选择 「开始迁移」 选项
- 选择来源仓库的类型,如 GitHub、GitLab 等,这样可以利用平台特定配置以获得更好的兼容性
- 输入源仓库 URL
- 设置同步周期并配置认证信息(如需要)
- 点击「创建仓库」,Forgejo 将自动开始首次同步
第三步:配置本地 Cargo 客户端
更新 Cargo 配置文件
配置文件看起来很简单:
# ~/.cargo/config.toml
[registry]
default = "forgejo"
[registries.forgejo]
# 推荐使用稀疏索引(Rust 1.68+ 默认)
index = "sparse+https://git.w/api/packages/liuzhen932/cargo/"
# 或使用传统 Git 索引
# index = "https://git.w/liuzhen932/_cargo-index.git"
# 如果使用 Git 索引,可能需要启用 CLI Git 获取
# [net]
# git-fetch-with-cli = true
配置过程中我们可以注意到有两种索引格式:传统的 Git 索引和新的稀疏索引。
传统格式是这样的:
# ~/.cargo/config.toml
index = "https://git.w/liuzhen932/_cargo-index.git"
稀疏索引是这样:
# ~/.cargo/config.toml
index = "sparse+https://git.w/api/packages/liuzhen932/cargo/"
好奇心驱使下,我两种都试了试。稀疏索引明显快很多,特别是在更新索引的时候。看了资料才知道,稀疏索引是 Rust 1.68 后的新特性,只下载需要的包信息,不用拉取整个索引仓库。
配置认证凭据
创建或编辑 ~/.cargo/credentials.toml 文件,然后配置认证:
# ~/.cargo/credentials.toml
[registries.myregistry]
token = "Bearer xxxxxxxxxxxxxxxxxxxxx"
请注意这里的 API Token 需要有 write:packages 权限,在生成的时候只勾选了基本权限是行不通的。
如何获取 API Token?
- 登录 Forgejo
- 进入”设置” → “应用” → “管理访问令牌”
- 创建新令牌,确保包含
write:packages权限 - 复制生成的令牌,在前面添加
Bearer前缀

获取 API Token
0x04 发布你的第一个 Crate 包!
创建示例项目
# 创建新的 Rust 项目
cargo new my-private-crate
cd my-private-crate
配置 Cargo.toml
编辑 Cargo.toml 文件:
# Cargo.toml
[package]
name = "my-private-crate"
version = "0.1.0"
edition = "2021"
description = "A private crate for internal use"
license = "MIT"
repository = "https://git.w/liuzhen932/my-private-crate"
documentation = "https://git.w/liuzhen932/my-private-crate"
publish = ["forgejo"]
[dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
编写示例代码
更新 src/main.rs:
// src/main.rs
//! 我的私有 Crate 示例
//!
//! 这是一个演示如何发布到私有注册中心的示例包。
use serde::{Deserialize, Serialize};
/// 配置结构体
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub name: String,
pub version: String,
pub debug: bool,
}
impl Config {
/// 创建新的配置实例
pub fn new(name: String, version: String) -> Self {
Self {
name,
version,
debug: false,
}
}
/// 启用调试模式
pub fn with_debug(mut self) -> Self {
self.debug = true;
self
}
}
/// 格式化配置信息
pub fn format_config(config: &Config) -> String {
format!("{} v{} (debug: {})", config.name, config.version, config.debug)
}
fn main() {
let config = Config::new("MyApp".to_string(), "1.0.0".to_string()).with_debug();
println!("Config: {:#?}", config);
println!("{}", format_config(&config));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_creation() {
let config = Config::new("test".to_string(), "1.0.0".to_string());
assert_eq!(config.name, "test");
assert_eq!(config.version, "1.0.0");
assert!(!config.debug);
}
#[test]
fn test_config_with_debug() {
let config = Config::new("test".to_string(), "1.0.0".to_string())
.with_debug();
assert!(config.debug);
}
}
发布到私有注册中心
# 检查包配置
cargo check
# 运行测试
cargo test
# 构建包
cargo build --release
# 发布到指定的私有注册中心
cargo publish --registry forgejo
提示发布完成后,我们可以在 Forgejo 的 WebUI 中看到新发布的包,并且可以通过 cargo search 命令来验证包是否可用。
在 WebUI 中,进入「软件包」部分,可以看到 my-private-crate 包的详细信息,包括版本、依赖关系和文档链接;我们可以点击右下角的「包设置」,将其关联到正确的仓库(如果没有推送源码,可略过):

Forgejo 包设置示意图
版本管理的小细节
发布了第一个版本后,我想发布一个测试版本。随手在 Cargo.toml 里写了 version = "0.1.1-test",结果 Cargo 拒绝了这个版本号。
原来 Cargo 严格遵循语义化版本规范,预发布版本必须是 0.1.1-alpha.1 这样的格式。看起来是小事,但这种一致性对生态系统很重要。
修正版本号后,发布过程变得很顺利:
- version = "0.1.1-test"
+ version = "0.1.1-alpha.1"
0x05 使用私有包
在其他项目中使用
在新项目的 Cargo.toml 中添加依赖:
# Cargo.toml
[dependencies]
my-private-crate = { version = "0.1.0", registry = "forgejo" } # 如果没有设置默认注册中心,则需要指定 registry
安装和更新
# 添加依赖
cargo add my-private-crate --registry forgejo
# 更新索引
cargo update
# 构建项目
cargo build
0x06 意外的收获

使用 cargo publish 上传的 [email protected]
对包管理的新理解
这次折腾让我对 Rust 的包管理系统有了更深的理解。Cargo 的设计确实很巧妙,注册中心、索引、包仓库的分离让整个系统既灵活又高效。
相比之下,Python 的 PyPI 就显得有点臃肿,所有信息都集中在一个地方。Cargo 的分布式设计让私有部署变得相对简单。
文档的重要性
为了让包能被正确使用,我不得不认真写文档和示例。这个过程中我发现,好的 API 设计和好的文档是相辅相成的。如果文档很难写,往往说明 API 设计有问题。通常来说链式调用的 API 不仅使用起来更舒服,文档和示例也更容易写。
备份的重要性
有了自己的注册中心后,备份变成了必须考虑的问题。包数据丢失的话,依赖这些包的项目就无法正常构建了。就像我在之前的文章提到的,我使用 R2 储存软件包,这样可以确保数据的持久性和可靠性。
设置清理规则
在 Forgejo WebUI 中我们可以配置自动清理规则:
-
访问包设置
- 进入组织或用户设置
- 选择”包管理” → “清理规则”
-
创建清理规则
规则类型:Cargo 保留最新版本数:10 保留版本匹配模式:v\d+\.\d+\.\d+$ 删除版本早于:90 天 删除版本匹配模式:.*-alpha.*
0x07 故障排除
-
认证失败
# 检查 API Token 是否正确 curl -H "Authorization: Bearer your-token" \ https://git.w/api/packages/liuzhen932/cargo/ -
索引同步问题
- 在 Forgejo WebUI 中重建索引仓库
- 检查
_cargo-index仓库的访问权限
-
网络连接问题
# 测试连接 cargo search --registry forgejo --limit 1 -
版本冲突
# 检查已存在的版本 cargo search your-crate --registry forgejo -
包发布失败
- 确保
Cargo.toml中的版本号符合语义化 - 检查网络连接和 API Token 权限
- 确保
0x08 结论
技术本身其实不复杂,关键是理解背后的设计思路。一旦理解了 Cargo 索引的工作机制,很多看似复杂的配置就变得合理了。
下次如果再有人想搭建私有 Cargo 注册中心,我会建议:
- 先花点时间理解 Cargo 的包管理机制
- 从最简单的配置开始,别一上来就想做复杂的定制
- 重视文档和测试,这些投入在后期会有回报
- 考虑好备份和安全策略
总的来说,这次折腾还是挺有收获的。现在我有了自己的包注册中心,可以更好地管理个人项目的依赖关系了。虽然过程有点曲折,但结果还是很满意的。记得定期更新 Forgejo 实例,关注安全更新,并根据使用情况调整配置参数。通过合理的规划和维护,私有 Cargo 注册中心将成为您 Rust 开发工具链中的重要组成部分。