Run a health check on a group of proxy nodes and relay chains, and produce a table that shows at a glance "which one is sick and where it hurts".Read-only, no writes— when problems are found, give judgments and suggested actions; do not switch automatically, do not restart automatically (switching is a human decision).
Usage
/ray-nodecheck # 全部节点 + 中转链
/ray-nodecheck us-node-1 # 单节点深查
/ray-nodecheck --sub # 只核订阅有效性
First locate the source of the node list:
- There is a node list repo/config file (such as a certain
config.jsoninnodes[]) → read out name/server/port/ssh_host - There are relay chain probe logs → on each relay host, read from the probe log directory
<probe_log_dir>/<chain>.log(the directory depends on your deployment) - If neither exists → ask the user for the node list (server:port + ssh alias)
Step 1: Per-node liveness probing
For each node, run in parallel:
1a. Port liveness
curl -sk -o /dev/null -w '%{http_code}' --connect-timeout 10 https://<server>:<port>
Judgment: returning 400 or 200 = alive(Reality returns 400 to non-VLESS clients, which is normal); timeout/connection refused/000 = dead.
1b. Connection latency
curl -sk -o /dev/null -w '%{time_connect}' --connect-timeout 10 https://<server>:<port>
Tiers: <0.5s good / 0.5–2s slow / >2s or timeout judged dead.
1c. Panel-side live status(optional if the node runs 3x-ui)
Pull the inbound's real-time traffic (↑/↓) and client status via the panel API, and by the way confirm that clients is not null (the aftereffect of the empty email pitfall).
Step 2: Egress IP verification
The public IP seen from the node's actual egress, to verify whether it is the expected landing:
ssh <ssh_host> 'curl -s --connect-timeout 10 https://api.ipify.org'
Uses: ① confirm it has not been identified/polluted ② on the relay chain, confirm the egress lands on the correct terminal machine ③ early warning for IP changes (CGNAT drift). Compare the result with the last record; if it changed, mark it yellow.
Step 3: Relay chain probe logs (if your environment has a relay chain probe)
The probe hops every 15s and writes logs as single-line logfmt. Read the latest status:
ssh <relay_host> 'tail -5 <probe_log_dir>/<chain>.log'
ssh <relay_host> 'grep -vE "result=ok" <probe_log_dir>/<chain>.log | tail -50' # 只看异常
Log fields and interpretation:
chain=<chain> tcp_connect_ms=42 wg_hs_age_s=18 probe_dur_ms=45 result=ok
| result | Meaning | Threshold | What to do |
|---|---|---|---|
ok |
Healthy | tcp<500ms and wg handshake<180s | — |
slow |
High latency | tcp 500–2000ms | Observe; if it occurs continuously, check the intermediate hops |
tcp_timeout |
Forwarding unreachable | tcp>2000ms or cannot connect | That relay hop is down; consider manually switching the chain |
wg_stale |
Tunnel effectively broken | wg handshake≥180s (about 7 missed keepalives) | WG tunnel down; check the peer/restart wg |
wg_unknown |
No handshake detected | wg command failed/no output | Probe environment or wg interface problem |
wg_hs_age_s=-1 indicates it has never handshaked or the probe failed. For the chain-switch event history, see the probe log directory's switches.log。
Step 4: Subscription validity
curl -s <订阅URL> | head -50
Verify:
- It can be fetched and is valid YAML (mihomo config)
proxies:node count == expected node count (fewer means some node dropped out of the subscription)proxy-groupsThe node names in it match the actual nodes- rule-providers'
.mrssource URL is reachable (if a self-hosted source is down, client rule loading will fail) - Spot-check: each node's server:port in the subscription matches the Step 1 liveness result
Step 5: Health table
Output a master table, sorted by severity (dead ones at the top):
节点 端口 延迟 出口IP 状态 备注
─────────────────────────────────────────────────────────
us-node-1 ❌死 — — 🔴 curl 超时,查中转链
us-node-2 ✅活 0.8s 1.2.3.4 🟡 延迟偏高
jp-node-1 ✅活 0.1s 5.6.7.8 🟢
中转链 chain-a — 42ms — 🟢 wg 握手 18s,正常
中转链 chain-b — timeout — 🔴 tcp_timeout,最近12跳异常
订阅 — — — 🟢 4/4 节点在册,源可达
Append below the table:
- 🔴 Suggested actions for items(one sentence each, e.g. "us-node-1 dead + relay chain chain-b tcp_timeout, same origin, check the machine at the end of chain-b first")
- Switches requiring manual decisionclearly marked, do not switch on the user's behalf
- Differences from the last inspection (newly added red/yellow, recovered green)
Discipline
- Read-only throughout: do not restart services, do not change configuration, do not switch chains
- A probe timeout does not equal a dead node—distinguish between "probe environment issues" and "node truly dead"; if unsure, probe again or switch probe points
- Sensitive information (subscription URLs containing tokens, egress IPs) is retained in the output, but remind the user not to post this table publicly