一个强大的 sing-box 订阅转换服务,支持多模板配置、自动更新、热重载等功能,通过对远端配置进行二次聚合转换,解决不同 sing-box 版本(如避免使用 P 核等)带来的配置文件碎片化问题,实现一个订阅地址适配多端多版本使用。推荐使用 Sub-Store 做初步节点聚合(需生成 singbox 配置)。
假设有三个客户端要使用 sing-box,但它们的版本不同:
- iOS 客户端:版本是 sing-box 1.11(未更新到最新)
- 软路由(OpenWrt):使用最新版 sing-box 1.12
- 安卓端:使用最新版 sing-box 1.12
因为 sing-box 从 1.12 开始对旧版本不兼容,三个客户端需要各自兼容的远端配置文件,但这些配置文件里要包含相同的订阅节点信息(即节点内容一致,只是配置格式不同)。
为此流程如下:
- 为不同客户端准备不同的 sing-box 模板(例如 ios、linux、android)。
- 使用 Singbox Subscribe Convert 服务,把相同的订阅节点信息套入不同模板,生成三个兼容各自版本的远端配置文件。
- 最终会得到三个不同的配置访问地址,例如:
此外,可以用 Sub-Store 来集中管理所有 sing-box 节点,再用 Singbox Subscribe Convert 根据需要导出不同模板的远端配置。
- 🎯 多模板支持 - 支持配置多个模板,可根据不同场景动态切换
- 🔄 自动更新 - 定时自动获取和更新节点及模板配置
- 🔥 热重载 - 配置文件变更自动检测和重载,无需重启服务
- 🔐 密码认证 - 内置密码认证机制,保护订阅安全
- 📊 健康检查 - 提供健康检查接口,方便监控服务状态
- 🐳 Docker 支持 - 提供完整的 Docker 部署方案
- 🚀 高性能 - 并行处理、文件缓存,响应迅速
- 🎨 自定义过滤器 - 支持节点名称过滤和自定义渲染
- 📦 智能缓存 - 本地缓存机制,离线也能正常服务
- 🔍 文件监控 - 实时监控缓存文件变化并自动重载
- 📈 详细日志 - 完善的日志系统,支持文件和控制台输出
- ⚡ 优雅关闭 - 支持信号处理和优雅退出
- 🌐 跨平台 - 支持 Linux、macOS、Windows、Docker
- ☁️ Cloudflare 集成 - 支持自动清理 Cloudflare CDN 缓存
# 拉取镜像
docker pull haierkeys/singbox-subscribe-convert:latest
# 运行容器
docker run -d \
--name singbox-subscribe-convert \
-p 9000:9000 \
-v /path/to/config:/singbox-subscribe-convert/config \
-v /path/to/storage:/singbox-subscribe-convert/storage \
haierkeys/singbox-subscribe-convert:latest# Docker-Compose.yaml
singbox-subscribe-convert:
image: haierkeys/singbox-subscribe-convert:latest
container_name: sub-convert
ports:
- "7000:9000"
volumes:
- /data/singbox-subscribe-convert/storage/:/singbox-subscribe-convert/storage/
- /data/singbox-subscribe-convert/config/:/singbox-subscribe-convert/config/
networks:
- app-network # 与 image-api 在同一网络1# 启动服务
docker-compose up -d# 克隆仓库
git clone https://github.com/haierkeys/singbox-subscribe-convert.git
cd singbox-subscribe-convert
# 编译项目
go build -o singbox-subscribe-convert .
# 运行服务
./singbox-subscribe-convert run- Go 1.24.1 或更高版本(源码编译)
- Docker 20.10+ 和 Docker Compose 2.0+(Docker 部署)
- 至少 100MB 可用内存
- 至少 50MB 可用磁盘空间
从 Releases 页面下载适合您系统的预编译二进制文件。
支持的平台:
- Linux (amd64, arm64)
- macOS (amd64, arm64)
- Windows (amd64)
# Linux / macOS
chmod +x sb-sub-c
./sb-sub-c run
# Windows
sb-sub-c.exe run# 编译所有平台
make build-all
# 编译特定平台
make build-linux-amd64 # Linux AMD64
make build-linux-arm64 # Linux ARM64
make build-macos-amd64 # macOS Intel
make build-macos-arm64 # macOS Apple Silicon
make build-windows-amd64 # Windows AMD64程序会按以下优先级查找配置文件:
- 命令行指定:
-c或--config参数 config/config-dev.yamlconfig.yamlconfig/config.yaml
# 服务器配置
server:
port: 9000 # 监听端口
read_timeout: 15 # 读取超时(秒)
write_timeout: 15 # 写入超时(秒)
idle_timeout: 60 # 空闲超时(秒)
# 认证配置
auth:
password: "your_secure_password" # 访问密码
# 节点订阅配置(新格式)
subscription:
url: "https://your-subscription-url" # 订阅地址
timeout: 30 # 请求超时(秒)
refresh_interval: 2 # 刷新间隔(分钟)
# 模板配置(新格式)
templates:
default:
url: "https://template-url/default.json"
name: "默认配置"
no_node: "🎯 全球直连"
enabled: true
gaming:
url: "https://template-url/gaming.json"
name: "游戏加速"
no_node: "🎯 全球直连"
enabled: true
# 默认模板
default_template: "default"
# 缓存配置
cache:
directory: "./data/cache"
node_file: "node.json"
template_file: "template.json"
# refresh 接口 同步 Cloudflare 缓存清理 配置
cloudflare:
enabled: false # 是否启用 Cloudflare 缓存清理
purge_url: "" # Cloudflare 缓存清理 API 地址 (例如: https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/purge_cache)
api_token: "" # Cloudflare API Token (推荐) - 在 Cloudflare 控制台创建
api_key: "" # Cloudflare API Key (可选) - 与 api_email 一起使用
api_email: "" # Cloudflare 账户邮箱 (可选) - 与 api_key 一起使用
# 日志配置
logging:
production: true # 生产模式
file: "./data/log/server.log" # 日志文件
level: "info" # 日志级别:debug, info, warn, error
max_size: 10 # 单个日志文件最大大小(MB)
max_backups: 3 # 保留的旧日志文件数
max_age: 7 # 日志文件保留天数| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
port |
int | 9000 | HTTP 服务监听端口 |
read_timeout |
int | 15 | 读取超时时间(秒) |
write_timeout |
int | 15 | 写入超时时间(秒) |
idle_timeout |
int | 60 | 连接空闲超时时间(秒) |
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
password |
string | 是 | API 访问密码 |
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
url |
string | 是 | 节点订阅地址 |
timeout |
int | 否 | 请求超时(秒),默认 30 |
refresh_interval |
int | 是 | 自动刷新间隔(分钟) |
每个模板包含以下字段:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
url |
string | 是 | 模板文件 URL |
name |
string | 是 | 模板显示名称 |
no_node |
string | 是 | 无节点时的默认显示 |
enabled |
bool | 是 | 是否启用该模板 |
| 参数 | 类型 | 说明 |
|---|---|---|
directory |
string | 缓存目录路径 |
node_file |
string | 节点缓存文件名 |
template_file |
string | 模板缓存文件名(旧格式) |
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
enabled |
bool | 是 | 是否启用 Cloudflare 缓存清理功能 |
purge_url |
string | 是* | Cloudflare 缓存清理 API 地址 格式: https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache |
api_token |
string | 否** | Cloudflare API Token(推荐方式) |
api_key |
string | 否** | Cloudflare Global API Key |
api_email |
string | 否** | Cloudflare 账户邮箱(与 api_key 配合使用) |
认证说明:
- * 当
enabled: true时必填- ** 必须配置以下任一认证方式:
- 推荐:
api_token(API Token 方式)- 或:
api_key+api_email(Global API Key 方式)
获取认证信息:
-
获取 API Token(推荐):
- 登录 Cloudflare Dashboard
- 进入 My Profile → API Tokens
- 点击 Create Token → 选择 Custom Token 或 Cache Purge 模板
- 设置权限:Zone → Cache Purge → Purge
- 设置 Zone Resources(选择你的域名)
- 创建并复制 Token
-
获取 Global API Key:
- 登录 Cloudflare Dashboard
- 进入 My Profile → API Tokens
- 找到 Global API Key → 点击 View
-
获取 Zone ID:
- 在 Cloudflare Dashboard 中选择你的域名
- 右侧边栏 API 部分可以看到 Zone ID
配置示例:
# 使用 API Token(推荐)
cloudflare:
enabled: true
purge_url: "https://api.cloudflare.com/client/v4/zones/abc123def456/purge_cache"
api_token: "your_api_token_here"
api_key: ""
api_email: ""
# 或使用 Global API Key
cloudflare:
enabled: true
purge_url: "https://api.cloudflare.com/client/v4/zones/abc123def456/purge_cache"
api_token: ""
api_key: "your_global_api_key"
api_email: "your_email@example.com"工作流程:
当调用 /refresh 接口时,系统会:
- 并发刷新节点文件和模板文件
- 等待所有刷新任务完成
- 如果启用了 Cloudflare,同步调用 Cloudflare API 清理缓存
- 返回刷新结果(包含 Cloudflare 清理状态)
| 参数 | 类型 | 说明 |
|---|---|---|
production |
bool | 是否生产模式(JSON 格式) |
file |
string | 日志文件路径 |
level |
string | 日志级别 |
max_size |
int | 单文件最大大小(MB) |
max_backups |
int | 保留的日志文件数 |
max_age |
int | 日志保留天数 |
# 查看帮助
./singbox-subscribe-convert --help
# 查看版本
./singbox-subscribe-convert version
# 运行服务(使用默认配置)
./singbox-subscribe-convert run
# 指定配置文件
./singbox-subscribe-convert run -c /path/to/config.yaml
# 指定工作目录
./singbox-subscribe-convert run -d /path/to/workdir
# 指定端口(会覆盖配置文件)
./singbox-subscribe-convert run -p 8080以下环境变量可以覆盖配置文件中的设置:
export SERVER_PORT=9000 # 服务器端口
export PASSWORD="your_password" # 认证密码
export SUBSCRIPTION_URL="sub_url" # 订阅地址
export DEFAULT_TEMPLATE="default" # 默认模板
export CACHE_DIR="./data/cache" # 缓存目录
export REFRESH_INTERVAL=2 # 刷新间隔(分钟)请求:
GET /?password=<密码>&template=<模板ID>&type=<类型>
参数:
password(必需): 认证密码template(可选): 模板 ID,不指定则使用默认模板type(可选): 自定义类型参数,传递给模板
示例:
# 使用默认模板
http://localhost:9000/?password=your_password
# 指定模板
http://localhost:9000/?password=your_password&template=gaming
# 带自定义参数
http://localhost:9000/?password=your_password&template=gaming&type=custom
响应:
{
"dns": {...},
"inbounds": [...],
"outbounds": [...],
"route": {...}
}请求:
GET /health
示例:
http://localhost:9000/health
响应:
{
"status": "ok",
"has_data": true,
"has_template": true,
"node_count": 10,
"template_count": 3
}状态码:
200 OK- 服务正常503 Service Unavailable- 服务降级(数据或模板未加载)
请求:
GET /refresh?password=<密码>
参数:
password(必需): 认证密码
示例:
http://localhost:9000/refresh?password=your_password
功能说明: 此接口会执行以下操作:
- 并发刷新所有启用的节点文件和模板文件
- 重新加载配置数据到内存
- 如果配置了 Cloudflare 缓存清理,会同步清理 CDN 缓存
- 返回操作结果
响应成功:
{
"status": "success",
"message": "Files refreshed successfully",
"node_count": 10,
"template_count": 3
}响应失败:
{
"status": "error",
"errors": [
"node file: fetch error",
"template gaming: load error",
"cloudflare cache purge: authentication error"
]
}模板文件支持两个核心变量,用于动态插入节点数据和生成 sing-box 配置。
作用: 将所有订阅节点的完整配置插入到模板中。
使用方式:
{
"outbounds": [
{ "tag": "🚀 节点选择", "type": "selector", "outbounds": ["..."] },
{ "tag": "🎯 全球直连", "type": "direct" },
{{ Nodes }}
]
}效果: 会在指定位置插入所有节点的详细配置(包括服务器地址、端口、加密方式等完整信息)。
作用: 根据关键词筛选节点名称,生成节点列表。
基本语法: {{ "关键词" | NotesName }}
场景 1:获取所有节点
{
"tag": "🐸 手动切换",
"type": "selector",
"outbounds": [ {{ "" | NotesName }} ]
}空字符串表示不过滤,返回所有节点名称
场景 2:筛选特定地区节点
{
"tag": "🇭🇰 香港节点",
"type": "selector",
"outbounds": [ {{ "香港" | NotesName }} ]
}只返回节点名包含"香港"的节点,如:
🇭🇰 香港 01、香港专线
场景 3:筛选多个地区(OR 逻辑)
{
"tag": "🇭🇰 港新节点",
"type": "selector",
"outbounds": [ {{ "香港|新加坡" | NotesName }} ]
}使用
|分隔多个关键词,返回包含"香港"或"新加坡"的节点
场景 4:配合自动测速
{
"tag": "♻️ 🇭🇰 港新自动",
"type": "urltest",
"outbounds": [ {{ "香港|新加坡" | NotesName }} ],
"url": "http://www.gstatic.com/generate_204",
"interval": "10m",
"tolerance": 50
}从筛选出的节点中自动选择延迟最低的
{
"outbounds": [
{
"tag": "🚀 节点选择",
"type": "selector",
"outbounds": ["🐸 手动切换", "♻️ 自动选择", "🇭🇰 香港节点", "🇯🇵 日本节点", "🎯 全球直连"]
},
{
"tag": "🐸 手动切换",
"type": "selector",
"outbounds": [ {{ "" | NotesName }} ]
},
{
"tag": "♻️ 自动选择",
"type": "urltest",
"outbounds": [ {{ "" | NotesName }} ],
"url": "http://www.gstatic.com/generate_204",
"interval": "10m"
},
{
"tag": "🇭🇰 香港节点",
"type": "selector",
"outbounds": [ {{ "香港" | NotesName }} ]
},
{
"tag": "🇯🇵 日本节点",
"type": "selector",
"outbounds": [ {{ "日本" | NotesName }} ]
},
{ "tag": "🎯 全球直连", "type": "direct" },
{{ Nodes }}
]
}- 关键词匹配:支持节点名称的模糊匹配,例如
"香港"可以匹配🇭🇰 香港 01、香港-IPLC等 - 多关键词:使用
|分隔,例如"香港|HK|Hong Kong"可以匹配多种命名方式 - 无匹配处理:如果筛选后没有任何节点,会自动使用配置中的
no_node值(如🎯 全球直连) - 区分大小写:关键词匹配区分大小写,注意与实际节点名称保持一致
templates:
# OpenWRT singbox1.12 配置
default:
url: "https://example.com/templates/default.json"
name: "OpenWRT"
no_node: "🎯 全球直连"
enabled: true
# IOS singbox1.10 配置
ios:
url: "https://example.com/templates/gaming.json"
name: "IOS"
no_node: "🎯 全球直连"
enabled: true
default_template: "default"# 默认模板
curl "http://localhost:9000/?password=xxx"
# OPENWRT singbox1.12 配置
curl "http://localhost:9000/?password=xxx&template=default"
# IOS singbox1.10 配置
curl "http://localhost:9000/?password=xxx&template=ios"- ✅ 独立缓存 - 每个模板有独立的缓存文件
- ✅ 并行更新 - 多个模板同时更新,提高效率
- ✅ 动态加载 - 可通过配置启用/禁用模板
- ✅ 热重载 - 模板文件变更自动重新加载
问题: 服务启动后立即退出
解决方案:
- 检查配置文件是否正确
- 检查端口是否被占用
- 查看日志文件
data/log/server.log
问题: 订阅地址无法访问
解决方案:
- 检查订阅 URL 是否正确
- 检查网络连接
- 检查防火墙设置
- 使用
/refresh?password=xxx手动刷新
问题: 请求返回 "Template not found"
解决方案:
- 确认模板 ID 在配置中存在
- 确认模板的
enabled为true - 使用
/health检查已加载的模板数量 - 检查模板 URL 是否可访问
问题: 返回 "Password Error"
解决方案:
- 检查 URL 参数中的
password是否正确 - 检查配置文件中的
auth.password设置 - 确保密码没有特殊字符需要 URL 编码
问题: 容器运行但无法访问服务
解决方案:
- 检查端口映射是否正确
- 确认容器状态:
docker ps - 查看容器日志:
docker logs singbox-subscribe-convert - 检查防火墙和网络设置
问题: 修改配置后未生效
解决方案:
- 配置文件变更会自动重载(需等待几秒)
- 或手动重启服务
- 检查配置文件语法是否正确
问题: 返回 "Missing X-Auth-Key, X-Auth-Email or Authorization headers"
解决方案:
- 确认已配置
api_token或 (api_key+api_email) - 检查 API Token 是否有效(未过期)
- 确认 API Token 具有 Cache Purge 权限
- 验证 Zone ID 是否正确
问题: 返回 "Authentication error" 或状态码 403
解决方案:
- API Token 权限不足,需要添加 Zone - Cache Purge - Purge 权限
- 确认 Zone Resources 包含了目标域名
- 如使用 Global API Key,确认 email 地址正确
问题: Cloudflare 缓存清理超时
解决方案:
- 检查网络连接到 Cloudflare API 是否正常
- 增加
subscription.timeout配置值(默认 30 秒) - 查看详细错误日志
问题: 需要查看 Cloudflare 清理详细日志
解决方案:
- 将日志级别设置为
debug:logging: level: "debug"
- 重启服务后会显示详细的请求和响应信息
欢迎提交 Issue 和 Pull Request!
- Fork 本仓库
- 创建特性分支 (
git checkout -b feature/amazing-feature) - 提交更改 (
git commit -m 'Add some amazing feature') - 推送到分支 (
git push origin feature/amazing-feature) - 开启 Pull Request
本项目采用 Apache License 2.0 许可证。
HaierKeys
- Email: haierkeys@gmail.com
- GitHub: @haierkeys
感谢所有为本项目做出贡献的开发者!
如有问题或建议,欢迎提交 Issue。