Skip to content

Repository files navigation

m-ui

m-ui

内嵌 sing-box 的控制面,面向自托管与多服务器部署 · 一个 Go 二进制 · 线路代替 inbound/outbound/route · 每次应用先干跑、失败即回滚 · 一台主机管所有副机

m-ui · Maoyangui 的 sing-box 面板 · Go · Linux · GPL-3.0 —— 与其它同名 “m-ui” 项目无关

中文 · English · 文档站 · 在线演示 · 架构 · 下载

tests install smoke test latest release GPL-3.0


一行安装(Debian / Ubuntu,root 运行):

bash <(curl -fsSL https://raw.githubusercontent.com/Maoyangui/m-ui/main/deploy/install.sh)

装完打开 http://<服务器IP>:2053/app/,默认账号 admin / admin(登录后到「管理员」页改掉)。没有域名也能用:一键自签证书 → 建线路 → 建用户 → 把订阅发出去,五分钟内完成。详见 安装 与 快速上手。

先看看再装? 在线演示 就是真实的面板界面跑在示例数据上:不用登录、没有后端,随便点,刷新即恢复。安装命令后面加 --dry-run 可以先看它会写哪些文件。

m-ui 概览:流量走势、数据面状态、在线用户与流量排行

这是什么

m-ui 是一个自托管的代理面板:一个二进制 + 一个数据库文件,sing-box 直接内嵌其中。线路、用户、订阅、证书、备份、多台服务器,全在网页里管。

和常见面板的四点不同:

  • 一条线路就是一件事 —— 入口协议 + 端口 → 从哪出去。不用再分别维护入站、出站、路由三张表。
  • 保存不怕改错 —— 整份配置先交给 sing-box 干跑,过不了就进不了库;改用户、改上游是热更新,只有改线路才重启数据面,新配置万一起不来(比如端口被同机别的程序抢走)会自动回滚到上一份。
  • 一主多副 —— 主机把线路、用户推给任意多台副机,收回流量、统一判定配额;订阅里每条线路按服务器各出一个节点,客户端自己挑延迟低的入口。
  • 运维不用回终端 —— 证书签发续期、Cloudflare WARP、内核调优、备份还原、换机搬迁、Telegram 通知、两步验证、外部 API,都在面板里。

自己用、小团队用,或者要在好几台机器上统一管人,都合适。

功能总览

模块 内容
线路 Hysteria2、AnyTLS、TUIC、Trojan、VLESS(Reality / Vision)、VMess、Shadowsocks(含 2022)、SOCKS、HTTP、Mixed;传输层 WS / gRPC / HTTPUpgrade / HTTP;Hysteria2 端口跳跃;按服务器选择部署位置
上游 VLESS / VMess / Trojan / TUIC / Hysteria2 / Shadowsocks / SOCKS 落地,粘贴分享链接导入;一键测延迟、定时巡检、故障 / 恢复告警;WARP 一键接入
用户 配额、到期、周期重置、同时在线设备数(按源 IP,跨服务器并集)、上下行限速;超量 / 到期自动禁用并踢线;批量生成、批量操作、CSV 导出。代理的用户归代理管,不混进主面板用户页
套餐 配额 / 时长 / 设备 / 限速 / 线路的模板,建号、续费、延期一键套用
代理 给代理分配线路、流量 / 设备 / 带宽额度与到期日,他在自己的面板(默认 2054,路径 /dl)里建号、建套餐;用量汇总到代理,超额 / 到期 / 停用即刻断其全部用户
订阅 通用链接、Clash、sing-box 三种格式;浏览器打开是落地页(用量、到期、一键导入、客户端下载、二维码、临时共享);订阅失效时给一页说明而不是空白 404;外部节点 / 外部订阅聚合;自签证书自动带 insecure
多服务器 主机推送配置、回收流量、集中判定;每台服务器可设流量倍率;副机失联 / 恢复告警
证书 Let's Encrypt HTTP-01 / Cloudflare DNS-01 签发,到期前自动续期,热更新不重启;无域名时一键自签
备份 一个 zip 含数据库一致快照与证书;上传即整机还原;定时备份、轮转、推送 Telegram;换机向导
运维 WARP 安装 / 启用 / 停用 / 卸载并验证出口;swap、内核参数 + BBR、文件句柄、NTP;限制系统日志(journald)占用
通知 Telegram:登录、用户到期 / 超量 / 禁用、上游故障、数据面停止、每日报告(发送时点与日期都按面板时区)
观测 流量走势(按时段柱状,1h / 6h / 24h / 7d / 30d)、流量排行、在线用户与 IP、最近入站连接诊断、审计日志、订阅访问日志(后两者可各自设定自动清理天数)
安全 bcrypt 密码、CSRF 防护、登录冷却(同一来源连续失败即暂停尝试)、两步验证(TOTP)、外部 API 令牌;代理面板与主面板会话严格隔离
更新 面板自己发现新版本,侧栏版本号旁提示;一键更新 = 校验 SHA256 → 替换程序 → 重启,数据库、证书与所有设置不动
集成 外部 API(建号、套餐、启停、续费、订阅地址),见 docs/API.md
用户详情:订阅地址、二维码、用量与流量走势 服务器:一主两副,配置推送、同步状态与在线数
用户详情:订阅地址、二维码、用量、在线设备 服务器:一主多副,推送同步、在线状态一眼看完

更多截图(线路、落地页、英文界面)在 docs/screenshots。演示数据全部是 example.com 与文档保留 IP。

架构

一个二进制里跑着四件事:面板(管理界面与 API)、订阅服务(客户端来拉配置)、数据面(内嵌 sing-box,真正过流量)、后台(统计、配额判定、上游巡检、日志清理、主副同步)。它们共用同一个 SQLite 文件,所以面板里点一下保存,订阅和数据面立刻看到同一份数据。

一台服务器里有什么

flowchart TB
  subgraph WHO["谁在连"]
    direction LR
    ADM["管理员"]
    DLR["代理"]
    APP["用户客户端"]
  end

  subgraph PROC["一个 m-ui 进程"]
    direction LR
    WEB["面板"]
    SUB["订阅服务"]
    CORE["数据面 · sing-box"]
    BG["后台任务"]
  end

  ADM -->|":2053 /app/"| WEB
  DLR -->|":2054 /dl/"| WEB
  APP -->|":2056 /sub/"| SUB
  APP ==>|"线路端口 · 真流量"| CORE
  WEB -.->|"热更新 / 重启"| CORE
  CORE -.->|"流量 · 在线 IP"| BG
  WEB --> DB[("m-ui.db")]
  SUB --> DB
  BG --> DB
  CORE ==> OUT["落地出口"]

  classDef svc fill:#1d4ed8,stroke:#1e3a8a,color:#ffffff,stroke-width:1px
  classDef who fill:#e2e8f0,stroke:#94a3b8,color:#0f172a,stroke-width:1px
  classDef data fill:#334155,stroke:#0f172a,color:#ffffff,stroke-width:1px
  classDef exit fill:#047857,stroke:#065f46,color:#ffffff,stroke-width:1px
  classDef warn fill:#b91c1c,stroke:#7f1d1d,color:#ffffff,stroke-width:1px
  class ADM,DLR,APP who
  class WEB,SUB,CORE,BG svc
  class DB data
  class OUT exit
Loading

端口和路径都能改;上面写的是默认值。面板与代理面板是同一套前端,靠会话里的作用域区分能看到什么。

保存一次改动,发生了什么

面板从不直接去改运行中的 sing-box —— 先让 sing-box 自己把整份新配置解析一遍,通过了才落库、才动数据面。

sequenceDiagram
  autonumber
  participant B as 浏览器
  participant W as 面板
  participant D as 库
  participant S as sing-box

  B->>W: 保存线路 / 用户
  W->>W: 校验字段与端口
  W->>D: 开事务写入
  W->>S: 全量配置干跑
  alt 干跑不过
    S--)W: 报错
    W->>D: 回滚
    W--)B: 拒绝,线上没动
  else 干跑通过
    S--)W: OK
    W->>D: 提交
    W->>S: 分级重载
    Note over W,S: 改用户 → 换用户表<br/>改上游 → 热换出站<br/>改线路 → 重启并可回滚
    W--)B: 已保存
  end
Loading

一次订阅请求怎么走

flowchart TD
  Q["GET /sub/&lt;地址&gt;"] --> WHO{"这个地址是谁"}
  WHO -->|"用户名 / 随机令牌"| U["命中用户"]
  WHO -->|"临时共享令牌"| S["同一个用户<br/>另一套凭据"]
  WHO -->|"对不上 · 已停用"| E["404"]
  U --> UA{"看 User-Agent"}
  S --> F
  UA -->|"浏览器"| P["落地页"]
  UA -->|"代理客户端"| F{"要哪种格式"}
  F -->|"默认"| F1["通用链接"]
  F -->|"?format=clash"| F2["Clash YAML"]
  F -->|"?format=json"| F3["sing-box 配置"]

  classDef svc fill:#1d4ed8,stroke:#1e3a8a,color:#ffffff,stroke-width:1px
  classDef who fill:#e2e8f0,stroke:#94a3b8,color:#0f172a,stroke-width:1px
  classDef data fill:#334155,stroke:#0f172a,color:#ffffff,stroke-width:1px
  classDef exit fill:#047857,stroke:#065f46,color:#ffffff,stroke-width:1px
  classDef warn fill:#b91c1c,stroke:#7f1d1d,color:#ffffff,stroke-width:1px
  class U,S exit
  class E warn
  class P,F1,F2,F3 svc
  class Q who
Loading

三种格式里的节点都是同一套来源:用户已分配的线路 × 该线路已部署的服务器,再拼上外部节点与外部订阅。所以加一台服务器,所有人的订阅里自动多出对应节点。分配也可以细到入口:一条线路部署在几台服务器上就是几个入口,给用户 / 套餐 / 代理授权时可以只勾其中某台的,订阅里就只出拿到的那些。

主服务器与副服务器

主机算账、推配置;副机只管转发,不自己判定配额,所以断联时也不会误伤用户。

sequenceDiagram
  autonumber
  participant M as 主服务器
  participant N as 副服务器

  Note over M,N: 每 5 秒一轮
  M->>N: 推快照:线路 / 上游 / 用户 / 凭据 + 修订号
  N->>N: 修订号没变就什么都不做
  M->>N: 拉报告
  N--)M: 流量增量 · 在线 IP · 上游巡检结果
  M->>M: 汇总用量 · 设备数跨机取并集 · 判定配额
  M->>N: 超量 / 到期的用户,下一轮快照里就没了
  Note over M,N: 副机掉线期间照常转发,<br/>恢复后按游标补齐,不会重复计费
Loading

数据模型

erDiagram
  RESELLER ||--o{ USER : "名下用户"
  RESELLER ||--o{ PLAN : "自己的套餐"
  RESELLER }o--o{ LINE : "被授权的线路(可收窄到某几台)"
  USER }o--o{ LINE : "分配(可细到某台服务器上的入口)"
  PLAN ||..o{ USER : "建号时套用"
  LINE }o--|| UPSTREAM : "从哪出去"
  LINE }o--o{ NODE : "部署在哪几台"
  USER ||--o{ SUBLOG : "订阅拉取记录"

  USER {
    string name "订阅地址 · 入站凭据名"
    json credentials "各协议的密码 / UUID"
    int64 volume_used "配额与已用"
    int64 expiry "到期时间"
    int device_limit "同时在线设备数"
  }
  LINE {
    string protocol "hysteria2 / vless / ..."
    int port "监听端口"
    json tls_transport "TLS 与传输层"
    json node_ids "部署到哪些服务器"
  }
  RESELLER {
    int64 quota "流量 / 带宽 / 设备额度"
    int user_limit "最多能建多少用户"
    json page "自己的订阅标题与落地页文案"
  }
Loading

后台节奏

周期 做什么 代码
10 秒 从数据面取流量与在线连接,按用户 / 线路 / 上游写入统计 jobs/
10 秒 判定限速规则(时段 / 突发),命中的生效状态随下一次快照推给副机 rules/ jobs/
1 分钟 判定配额、到期、周期重置,该禁的禁、该踢的踢;数据面看门狗 jobs/ monitor/
5 秒 主机推快照给副机、拉回流量与在线 IP hub/
10 分钟 WAL 检查点,让运行中的 .db 随时可安全复制 runner/
6 小时 查一次有没有新版本(只查,不自己更新) selfupdate/
1 小时 按各自的保留天数清理流量时序、订阅访问日志、审计日志;48 小时前的分钟级流量样本并成小时桶 jobs/
按设置(默认 10 分钟) 巡检上游:每台只测自己线路用到的那些,结果汇总回主机 monitor/
12 小时 证书续期检查(续期后 sing-box 自动换证书,不重启) runner/
每天 日报 monitor/

代码结构

目录 职责
web/ 面板与 API、代理面板与作用域、外部 API、内嵌前端(零构建 ES 模块)
sub/ 订阅三种格式、落地页、客户端下载页、临时共享
render/ core/ 线路 → sing-box 配置的渲染与干跑校验;内嵌数据面、流量与连接跟踪
hub/ 主副机快照推送、流量回收、在线聚合
runner/ 进程编排、三级重载与回滚、证书、备份、外部节点
jobs/ monitor/ 统计、配额判定、清理、上游巡检与告警
rules/ 限速规则:时段 / 突发判定、生效状态、与用户限速的叠加计算
database/ 模型、SQLite 打开与迁移
selfupdate/ deploy/ 版本检查与就地更新、安装脚本

安装

系统要求

  • Linux amd64 或 arm64,systemd(Debian / Ubuntu / Rocky / CentOS 等均可,推荐 Debian 11+ / Ubuntu 20.04+;安装脚本依赖 systemd,Alpine 这类没有 systemd 的系统不支持)
  • root 权限
  • 放行端口:面板 2053/tcp、订阅 2056/tcp、启用代理面板时再加 2054/tcp,以及你创建的线路端口(TCP 或 UDP 视协议而定)。签发证书用 HTTP-01 时还需要 80/tcp 可达

一键安装(推荐)

bash <(curl -fsSL https://raw.githubusercontent.com/Maoyangui/m-ui/main/deploy/install.sh)

不是 root 用户时用管道形式:

curl -fsSL https://raw.githubusercontent.com/Maoyangui/m-ui/main/deploy/install.sh | sudo bash

脚本会自动识别架构,从 GitHub Releases 下载最新版,装到 /usr/local/bin/m-ui,数据放在 /etc/m-ui/,并注册 m-ui.service 开机自启。安装结束会打印面板地址与默认账号。

指定版本或离线安装:

bash install.sh v0.2.0                          # 指定版本
bash install.sh ./m-ui-linux-amd64.tar.gz       # 本地文件(Releases 里的压缩包或裸二进制)
bash install.sh https://.../m-ui-linux-arm64.tar.gz   # 任意下载地址

首次登录

  • 面板地址:http://<服务器IP>:2053/app/
  • 默认账号:admin / admin

登录后顶部会一直提示修改默认密码,请到 管理员 页立即修改。改端口、路径、账号也可以在 SSH 里执行 m-ui 用菜单完成。

升级

m-ui            # 菜单选 10 "更新到最新版"

或重新执行一键安装脚本。升级只替换二进制并重启,数据库、证书、备份都不动。

卸载

m-ui 菜单选 14,可选择是否同时删除 /etc/m-ui 数据目录。

快速上手

登录后概览页有一份快速开始清单,按顺序做完就能用:

  1. 域名(可选):设置 → 面板 → 域名。把域名 A 记录解析到服务器 IP。
  2. 证书:证书页填域名,一键签发 Let's Encrypt;没有域名就点"生成自签证书"填服务器 IP。面板、订阅、数据面共用这张证书,以后自动续期。
  3. 线路:线路页新增,选协议(推荐 Hysteria2 或 AnyTLS)、端口、上游(默认直连),保存。
  4. 用户:用户页新增,勾选可用线路,或直接套用套餐。
  5. 分发订阅:用户列表复制订阅链接发给用户,或让用户在浏览器打开落地页扫码 / 一键导入。

没有域名也能用:自签证书 + IP 地址,订阅会自动带上"允许不安全证书",客户端无需额外设置;之后有了域名再签发正式证书,用户刷新一次订阅即可。

功能说明

线路

  • 一条线路对应一个入口端口。端口在所有服务器上一致,主机负责渲染并下发到每台副机。
  • 上游决定流量从哪出去:直连、WARP、或任意一个已添加的落地节点(中转)。
  • 部署到:默认所有服务器;也可以只部署到某几台,例如给某台副机建一条它专属的线路。
  • Hysteria2 可填端口跳跃范围(如 20000-30000),服务器用 nftables / iptables 把这段 UDP 转到线路端口,客户端在范围内随机换端口,规避运营商对单端口 UDP 的限速。
  • 保存时整份 sing-box 配置先干跑校验,失败的改动不会落库。

新建线路时端口会自动填一个可用的五位端口(仍可改):它避开已有线路与面板 / 订阅 / 代理面板端口,并在服务器上真的监听一次确认空闲。手填的端口同样会检查——同机跑着别的项目也算:保存前会试着监听一次,被 nginx、其它面板之类占着就直接拒绝(端口没改的编辑不检查,那时占着它的正是本机运行中的数据面)。冲突是双向拦截的:线路不能用面板端口,面板 / 订阅 / 代理面板也不能改成线路正在用的端口或别的程序占着的端口——保存时就报错,不会等到重启才发现起不来。

上游

  • 可视化编辑或直接粘贴 vless://、hy2://、ss:// 等分享链接导入。
  • 一键测试延迟(经上游访问测试地址),定时巡检;连续失败发 Telegram 告警,恢复时再通知。
  • 巡检在真正用这条上游的机器上做:每台服务器只测自己线路用到的上游,结果汇总回主机,上游页一行里并排显示各服务器的延迟。没有线路使用的上游标「未使用」,不测也不告警;告警会点名是哪台机器上不通。有故障或数据过期时行尾才出现展开箭头,点开是每台的状态、检查时间与完整报错。
  • 运维页启用 WARP 后会自动出现名为 warp 的上游,线路选它即可走 WARP 出口。

外部节点

把别处的节点并进你的订阅:一条分享链接,或一整个外部订阅(机场、中转)。主机定时抓取(间隔可设),分配给用户后出现在该用户订阅里本站节点之后,可加统一前缀便于区分。支持 base64 / 明文链接列表、Clash YAML、sing-box JSON;抓取时先以 Clash Meta 身份要全量列表(有的面板按客户端发不同子集,通用身份只能拿到一两种协议),拿不到再退回通用身份。

列表里点开一条外部订阅,能看到它解析出的全部节点:每个节点的完整参数(服务器、端口、SNI、UUID、密码、传输方式等)和分享链接;可以在每台启用的服务器上逐个真连测速(结果按服务器分列),也可以勾选几个、各起一个名字直接添加为上游——走和手动新增上游同一套校验,重名报错不改名。

用户与套餐

  • 每个用户:配额、到期、周期重置(如每 30 天清零)、设备数、限速、备注、可用线路(可细到某台服务器上的入口)、可用外部节点。
  • 订阅地址泄露了就「重置订阅链接」(列表「更多」与详情里都有,代理面板同样):地址换成新的随机令牌,全部协议凭据重新生成,临时共享一并收回;旧地址、旧凭据在所有服务器上立即失效,已连的设备被断开。
  • 超量或到期自动停用并踢下线,列表里标出原因(超量 / 到期 / 手动);重置流量或续期后自动恢复,手动停用的只有手动启用才恢复。
  • 套餐是模板:建号时选套餐一键填好;续费 = 再套一次(用量清零、到期顺延);延期 = 保留用量只顺延到期。
  • 批量:按前缀批量生成、勾选后批量启停 / 延期 / 重置 / 删除,导出 CSV。
  • 用户抽屉:实时在线设备(每个 IP 标出正在使用的线路与服务器)、24h / 7d / 30d 流量图、订阅链接与二维码、踢线(所有服务器一起断;hysteria2 / TUIC / AnyTLS 连整条会话一起关,不能靠已鉴权的会话继续开新流)。

规则

  • 给用户加临时限速,两种:时段限速(每周指定几天的某个时间段,按面板时区,支持跨午夜)和突发限速(最近 N 分钟内上下行合计达到 X GB 就限速 M 分钟,到期自动解除,再次达到再次限速)。
  • 目标可叠加:全部用户(含以后新建)、某个代理名下全部用户(含以后新建)、逐个勾选;勾选器按主面板 / 各代理分组,切换分组不丢勾选。
  • 规则由主机每 10 秒判定,命中写一条生效状态,随快照下发副机;主副机都把状态叠加到用户自己的限速上。多条同时命中取最严;开着「只升不降」(默认)的规则不会高于用户自己的限速,关掉后规则值直接覆盖,可用于在特定时段放宽。同一用户可挂多条突发规则,各自独立触发与计时。
  • 改限速对在线连接立刻生效(令牌桶原地改速率),不用等重连——这也修掉了以前"改用户限速要等重连"的旧行为。
  • 用户列表标出「限速中」与规则名,详情里列出每条生效中的限速与剩余时间;触发与解除记入操作审计,可选 Telegram 通知。规则限速只在面板与外部 API 可见,不显示在用户的落地页。

代理

把一部分销售交给别人做:主面板「代理」页新建代理,勾选他能用的线路,给一份流量额度、一个设备池和带宽池,还可以限制他最多建多少个用户。

  • 代理面板:默认 https://<域名或IP>:2054/dl/,端口、路径、证书在 设置 → 代理面板 里改(证书留空与主面板共用)。
  • 登录名就是代理名称,新建的代理没有密码:第一次用名称登录后必须先设置密码,之后可自行改密码、开两步验证。忘记密码由主面板「重置密码」清空,他下次登录时重设。
  • 代理在自己的面板里只看得到自己的用户:建号只填名称(订阅地址是一串随机令牌,link / Clash / sing-box 三种格式与二维码都照常),可分配线路(只限主面板授权给他的,授权可以收窄到某台服务器)、流量、到期、设备、限速,也能续费、重置、踢线、批量操作、看用量曲线与在线设备。套餐也是各管各的:代理只看得到、也只能用自己建的套餐。
  • 额度:流量按全时用量计(代理自己重置、续费、删号都洗不掉,只有主面板"重置流量"能清零);设备数与上下行带宽是运行时的池——代理给单个用户填多少都行,但名下所有用户同时在线的设备总数不超过设备池(跨服务器算并集,池满只拒新设备),合计速率不超过带宽池(每台服务器各一份);到期日、停用、超额任意一条踩线,他名下用户立刻从数据面撤下、订阅返回 404。
  • 代理可以配自己的订阅标题(客户端里显示的名字)与落地页文案(标题、公告、客服),留空都跟随主面板;也能单独关掉自己的订阅页与临时共享。
  • 新建的代理有 24 小时"认领窗口":这段时间内在代理面板填代理名、密码留空直接点登录,进去后必须先设好密码;过期未认领就得由主面板再点一次"重置密码"重开窗口,避免账号一直敞着。
  • 代理的用户不出现在主面板的用户页与 CSV 导出里;要看就进代理详情:一行一个用户,列出用量、到期、在线设备,展开能看到每个 IP 走的是哪台服务器的哪条线路,也能直接复制他的订阅地址。

订阅

  • 通用链接:https://<域名或IP>:2056/sub/<用户名>,适用于 nextin、sing-box、Shadowrocket、Surge、Quantumult X、Loon、Karing 等。
  • 设置 → 订阅的"用用户名作订阅地址"默认开着;关掉后新建用户的订阅地址改成一串随机令牌(用户名只作名称,地址猜不到),已有用户不受影响,代理建的用户一律是随机令牌。
  • Clash:在链接后加 ?format=clash,适用于 mihomo / Clash Verge / Stash 等;每条线路生成一个按延迟自动选择的入口组。
  • sing-box:在链接后加 ?format=json,输出完整的 sing-box 客户端配置(TUN + 分组 + DNS + 分流),SFA(Android)/ SFI(iOS)/ 桌面版直接作为远程配置导入,落地页有一键导入。
  • 客户端里显示的订阅名来自 设置 → 订阅 的"订阅标题"(留空依次回落到订阅页标题、备注、用户名),中文按标准方式编码后随 Content-Disposition 与 Profile-Title 两个响应头发出。nextin、sing-box 这类每次更新都读标题的客户端刷新即可;Shadowrocket 与 Clash Verge 是在添加订阅那一刻定名的,之后不改名——用落地页的一键导入按钮添加(会把标题一起带上),或让用户删掉重加。
  • 浏览器打开链接显示落地页:用量、到期、各客户端一键导入、二维码、自定义公告与客服信息。
  • 客户端下载:落地页"一键导入"右上角有个下载箭头,新开一页按系统给下载入口 —— iOS / iPadOS / Apple TV(Nextin,附切区说明)、Android / Android TV(Clash Meta for Android)、Windows / macOS / Linux(Clash Verge Rev,Linux 另给 FlClash 的 AppImage)。每个系统都有官方直链、国内镜像与"全部版本"入口;这一页不含任何用户信息,可以直接转发给用户。代理的用户看到的是同一页。
  • 用完、到期、被停用(或所属代理额度用尽 / 到期)的用户,浏览器里仍能打开自己的落地页:顶部标出原因、下次重置日与该看的联系方式(代理的用户看代理自己配的那套);只是客户端从这一刻起拉取拿到 404,线路入站也不再放行。地址对不上任何人时浏览器看到「订阅地址无效」页,客户端同样纯 404。
  • 订阅端口按来源 IP 限流(默认每分钟 120 次,设置 subRateLimit,超出返回 429),对不上任何人的地址按 IP 聚合记一条访问日志,扫描器刷不爆日志表。
  • 临时共享:用户可在落地页生成一条随机地址临时给别人用,每人一条。共享地址给的是同一批节点但另一套凭据,在数据面挂在本人名下——流量、设备数、限速、到期都算本人;取消(或重新生成)会立即撤下这套凭据并断开连接,借用者已经拉走的节点也随之失效。共享地址只发订阅,不给落地页。设置 → 订阅页 可整体关闭。
  • 节点地址默认用服务器 IP、域名只作 SNI,避免客户端本地 DNS 被污染导致连不上;可在设置里改为域名。
  • 多服务器时每条线路按服务器各出一个节点,名称后缀为服务器名;流量倍率不为 1 的服务器会带 x2 之类的标记。

多服务器

任意数量的副服务器,接入步骤:

  1. 副机同样安装 m-ui,登录后 设置 → 角色 勾选"作为副服务器运行"。
  2. 副机 设置 → 配对信息 复制 API 地址与令牌。
  3. 主机 服务器 页新增:名称(如"香港",作为订阅节点后缀)、域名、API 地址、令牌,可选跳过证书校验(副机自签时;第一次连上会记住它的证书指纹,之后指纹变了就拒绝,重签证书后点「重置指纹」)。
  4. 几秒内主机完成推送并显示"已同步"。

工作方式:主机每几秒比对快照,有变化就推给副机;同时从副机拉回流量账本、在线 IP、运行状态与证书剩余天数。配额只在主机判定,禁用用户 5 秒内传到所有副机。副机失联超过 1 分钟告警一次,恢复再通知;离线期间副机继续用最后一份配置服务用户。

每台服务器可设流量倍率(含主机自己):经该服务器的流量按倍率计入用户用量,比如给贵的线路设 2 倍。

删除一台服务器时,线路里"部署到服务器"的那台会被自动摘掉;如果某条线路只部署在这台机器上,摘完就没有落点了,会被一并停用(不会悄悄放到所有机器上),接口与提示里会列出被停用的线路。

证书

三种来源任选:

  • 有域名:Let's Encrypt 自动签发,HTTP-01(需 80 端口可达)或 Cloudflare DNS-01(填 API Token,不用开 80);签发前预检 DNS 与端口,到期前自动续期,续期后热更新不重启。
  • 无域名:用服务器 IP 生成自签证书。默认只给线路入站用,订阅里每个节点都会自动写好"允许不安全"(链接 insecure=1、Clash skip-cert-verify、sing-box insecure),客户端不用手动开;面板与订阅保持 HTTP(自签走 HTTPS 会被浏览器和客户端拦下)。
  • 已有证书:直接填服务器上证书与私钥的路径(certbot、宝塔、nginx、商业证书都行),只记录路径不复制文件,保存前校验两者匹配且未过期;续期时覆盖原文件即可。

线路入站始终使用当前证书;面板 HTTPS 与订阅 HTTPS 各自可勾选(订阅改动立即生效,面板需重启 m-ui)。副服务器各自维护自己的证书。

备份与换机

  • 备份页一键下载 zip(数据库一致快照 + 证书);定时备份并轮转,可推送到 Telegram。
  • 还原:在新机备份页上传 zip,m-ui 自动重启并整机还原;或安装时 bash install.sh --restore 备份.zip。
  • 换机向导:新机装好并还原 → 检查面板 → 把域名解析切到新机(页内可查各大解析器是否生效)→ 旧机下线。端口、凭据、订阅地址全部不变,用户无感。

运维

  • WARP:一键安装 / 启用(本地 SOCKS5,MASQUE)/ 停用 / 卸载,并验证出口 IP。
  • 系统优化:swap、内核参数 + BBR、文件句柄上限、NTP,参数可按机器调整。任务串行执行,输出实时显示。
  • 限制系统日志占用:给 journald 写占用上限与保留时长并立刻回收。它装的是全机所有服务的日志(m-ui 通常只占其中很小一部分),限制它不影响面板运行 —— 面板的运行日志在进程内存里,与它无关。

日志

订阅访问日志、核心日志(数据面 + 面板)、操作审计。核心日志页可关闭记录(省资源),每类日志都可一键清空。

数据面日志级别默认 warn:只记警告与错误。逐条记连接的 info 一小时几万行,进 journald 再被 rsyslog 抄一份,副机磁盘一顿所有连接跟着顿几秒(真机排查过的"隧道偶尔卡一下"就是它),所以只在排障时临时调到 info / debug,改了即时生效不重启。数据面写日志也改成了有界异步队列,磁盘再慢也不会拖住连接;来不及写的会丢掉并计数。

自动清理:订阅访问日志与操作审计存在数据库里会一直累积,各自可以设「保留 1 天 / 1 周 / 1 个月 / 不自动清理」,就在各自页面的清空按钮旁边,每小时执行一次。默认保持原来的行为:订阅访问日志跟随「流量记录保留」的天数,操作审计不自动清理。核心日志只在内存里(最多 1 万条,重启即清),所以没有按天清理这一项。

面板的日志和系统的 journald 是两回事:后者装的是整台机器所有服务的日志,要限制它请用运维页的「限制系统日志占用」。

管理员

  • 修改用户名 / 密码。
  • 两步验证:生成密钥扫码,或手动输入一串密钥,填一次验证码即开启;兼容 Google / Microsoft Authenticator、Authy、1Password 等。手机丢失时 SSH 执行 m-ui 选"关闭两步验证",重置密码也会一并清除。
  • 外部 API:打开开关拿到令牌,商城 / 计费 / 机器人即可建号、套餐续费、启停、踢线、取订阅地址。代理在自己面板的「我的账号」里也能开一套,作用域只限他名下。页内有接口表与 curl 示例,完整说明见 docs/API.md。

设置

时区(默认 Asia/Shanghai,面板里所有时间按它显示)、面板 / 订阅 / 代理面板各自的监听地址 · 端口 · 路径 · 证书、订阅展示选项、落地页文案、Telegram 通知项、上游巡检参数、数据面统计粒度、私网访问(默认关闭:数据面拒绝用户访问 127.0.0.0/8、10/8、172.16/12、192.168/16 这些内网地址,自己的内网服务要给用户用再打开)等。改面板路径 / 订阅路径保存即生效;改端口、监听地址、证书路径需重启,页头有重启按钮。代理面板可以整体关掉(resellerEnabled),副机上不启动。

命令行

m-ui                                         数字菜单(见下)
m-ui run -db /etc/m-ui/m-ui.db               启动(systemd 用这个)
m-ui info -db <db>                           打印面板地址、路径、账号状态
m-ui set -db <db> key=value ...              改设置,如 webPort=3053 nodeMode=true
m-ui passwd -db <db> [-password 新密码]      重置现有管理员的密码,改过用户名也认得(同时关闭两步验证)
m-ui backup -db <db> -out 备份.zip           生成备份
m-ui restore -db <db> -from 备份.zip         还原备份(服务停止时)
m-ui import -from 旧面板.db -to <db>          从旧面板数据库导入
m-ui selfsign -hosts 域名,IP                 生成自签证书
m-ui render -db <db>                         打印并校验 sing-box 配置
m-ui version

-db 省略时取 systemd 单元里的库路径(默认 /etc/m-ui/m-ui.db);库不存在时直接报错,不会悄悄建一个空库再对着它备份或改密码。

不带参数运行 m-ui 进入菜单:

 1. 查看面板地址 / 账号          8. 查看最近日志
 2. 重启 m-ui                    9. 立即备份
 3. 停止 m-ui                   10. 更新到最新版
 4. 启动 m-ui                   11. 生成自签证书(无域名时)
 5. 重置管理员密码              12. 切换 主机 / 副机 角色
 6. 修改面板端口 / 路径 / 订阅端口   13. 关闭两步验证
 7. 重置全部设置                14. 卸载 m-ui

从旧面板迁移

整机迁移(线路、上游、用户、设置全部带过来):

bash install.sh --import /path/to/旧面板.db

导入保留全部入站端口、用户凭据、面板路径与订阅地址,客户端不用改任何东西;导入报告会列出核对结果。

只搬用户(线路已在 m-ui 上重新建好,只想把老用户的名称、用量、配额、到期带过来):用户页 从旧面板导入 上传 .db,或

m-ui import -from /path/to/旧面板.db -to /etc/m-ui/m-ui.db -users-only && systemctl restart m-ui

同名用户只更新用量 / 配额 / 到期 / 启停,新用户创建并沿用旧凭据,默认分配全部现有线路。

常见问题

客户端连不上,但订阅能刷新? 多半是客户端本地 DNS 把域名解析错了。m-ui 默认在订阅里直接写服务器 IP(域名只作 SNI),让用户刷新一次订阅;概览页"最近入站连接"能看到有没有该用户的 IP 到达服务器。

没有域名能用吗? 可以。证书页生成自签证书填 IP,订阅自动带允许不安全;有域名后再签正式证书,用户刷新订阅即可。

改了端口 / 监听地址不生效? 这些改动需要重启 m-ui(设置页页头有重启按钮,或菜单 2)。证书续期不需要重启,数据面也不需要:sing-box 监听证书文件变化自动换,连接不断。端口在保存时就会校验(1-65535、三个服务不能撞),填错当场报错,不会等到重启才发现面板起不来。

路径怎么填? app、/app、app/、//app//、前后带空格都会被规整成 /app/;想把面板挂在根上就填 /。地址少一个结尾斜杠(:2053/app)会自动跳到带斜杠的地址,订阅路径同理。

WARP 启用失败? 运维页任务输出会给出原因;常见是 40000 端口被占。WARP 只作为本地 SOCKS5 上游,线路选上游 warp 即走 WARP 出口。

副机显示离线? 服务器页会显示原因(令牌错误 / 连接超时 / 证书校验失败 / 证书指纹变了)。副机用自签证书时在主机勾选"跳过证书校验"——勾了也不是完全不看证书:首次连接记住指纹,之后指纹变了会拒绝,重签过证书就在服务器页点「重置指纹」。

打开 :2053/ 为什么是 404? 根路径故意不跳转到面板:跳转等于把面板路径告诉每一个扫到这个端口的人。直接打开完整的面板地址即可。

忘记密码 / 手机丢了进不去? SSH 执行 m-ui,菜单 5 重置密码(会同时关闭两步验证),或菜单 13 只关闭两步验证。

客户端里订阅名不对? 设置 → 订阅 填"订阅标题",代理在自己面板的订阅页里填。注意 Shadowrocket 与 Clash Verge 是在添加订阅那一刻定下名字的,之后刷新不会改名——让用户删掉重加一次,或者直接用落地页上的一键导入按钮(它会把标题一起带过去)。nextin、sing-box 这类每次更新都读标题的客户端刷新即可。

代理登录不上? 新代理有 24 小时认领窗口:窗口内填代理名、密码留空直接点登录,进去后按提示设密码。过期或忘记密码,主面板代理页"更多 → 重置密码"清空后重开窗口(两步验证会一并关掉)。

数据在哪? /etc/m-ui/m-ui.db(数据库)、/etc/m-ui/cert/(证书)、/etc/m-ui/backups/(定时备份)。备份 zip 含用户凭据与证书私钥,请妥善保管;还原时只会把证书写回数据目录内的路径。

开发

需要 Go 1.27+(见 go.mod)。Reality 依赖 with_utls 构建标签,所有构建与测试都要带上:

make build          # 本机
make linux          # CGO_ENABLED=0 GOOS=linux 静态二进制
make test           # go test -tags with_utls ./...

前端是零构建的 ES modules(web/assets/),直接编辑刷新即可;内嵌资源按内容哈希生成 ETag,升级后浏览器自动拿到新文件。架构说明见 docs/ARCHITECTURE.md。

core/      内嵌 sing-box 生命周期、流量统计、连接跟踪、限速与设备数
render/    线路 → sing-box 配置渲染与校验
sub/       订阅生成(链接 / clash / sing-box)、落地页、临时共享、订阅服务
web/       面板与代理面板的 HTTP API、外部 API、前端(零构建 ES modules)
hub/       多服务器同步(快照推送、流量回收、在线聚合)
runner/    进程编排:数据面、订阅服务、证书、备份、外部节点
jobs/      统计、配额判定(含代理额度)、清理定时任务
monitor/   上游巡检、看门狗、用户预警、日报
database/  模型与 SQLite 打开 / 迁移
acme/      Let's Encrypt 签发(HTTP-01 / Cloudflare DNS-01)
certutil/  证书校验与热加载
backup/    备份与还原
ops/       WARP 与系统优化脚本
hop/       Hysteria2 端口跳跃(nftables / iptables)
ext/       外部节点 / 外部订阅抓取与解析
upstream/  分享链接解析(上游导入与 sing-box 订阅共用)
creds/     各协议凭据生成
totp/      两步验证(RFC 6238)
notify/    Telegram 通知
logger/    日志缓冲(面板日志页读它)
tz/        面板时区
importer/  旧面板数据库导入(兼容)
brand/     logo 与联系方式

发布:打 v* 标签后 GitHub Actions 自动测试、构建 amd64 / arm64 并发布 Release。

反馈与支持

面板右上角"建议 / 支持"里有作者的 Telegram 与打赏地址;也欢迎提 Issue / PR。

许可证

GPL-3.0。内嵌 sing-box,第三方来源声明见 NOTICE。

About

m-ui — embedded sing-box control plane for self-hosted, multi-server deployments. One Go binary + SQLite; lines instead of inbound/outbound/route; dry-run and rollback on every apply; hot user/upstream reload; one master for many servers; subscriptions, resellers, WARP, certificates, backups. By Maoyangui.

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages