Tanky's blog

你或许不需要 GUI 代理客户端

我用 Debian 系统,我之前一直用 v2rayN 做代理客户端,但它太“重”了:每次开机都要手动启动,就算设置开机自启,也会弹出窗口,就算设置不弹出窗口,也会在任务栏显示图标,就算把图标隐藏了,也会比单独的 Xray 内核占用更多内存。

我的需求就是开机自启+线路切换+流量监控。我用自己的 VPS 搭梯子,用不着订阅。

Xray 有 Linux 的一键安装脚本,给系统上的所有用户安装,但我不需要。直接从 Xray-core 的 releases 下载Xray-linux-64.zip,解压到~/program/Xray

线路切换

一开始我想用它的 API 做线路切换,但是 gRPC API 太重了,所以用一个主配置文件+多个 outbound 配置文件。

program/Xray/
├── bin
│   ├── geoip.dat
│   ├── geosite.dat
│   ├── LICENSE
│   ├── README.md
│   └── xray
├── conf
│   ├── base.json < 通用配置
│   ├── cdn.json < CDN线路
│   └── xhttp.json < 直连线路
├── route < 配置文件名
└── xray.sh

base.json:

{
  "inbounds": [
    {
      "tag": "socks",
      "port": 10808,
      "listen": "127.0.0.1",
      "protocol": "mixed",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http","tls"],
        "routeOnly": true
      },
      "settings": {
        "auth": "noauth",
        "udp": true,
        "allowTransparent": false
      }
    },
    {
      "tag": "api",
      "port": 10812,
      "listen": "127.0.0.1",
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "inboundTag": ["api"],
        "outboundTag": "api"
      },
      {
        "type": "field",
        "outboundTag": "direct",
        "ip": ["geoip:private", "geoip:cn"]
      },
      {
        "type": "field",
        "outboundTag": "direct",
        "domain": ["geosite:private", "geosite:cn"]
      }
    ]
  },
  "metrics": {
    "tag": "api"
  },
  "policy": {
    "system": {
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  },
  "stats": {}
}

除了 inbound ,还写了路由规则,直连局域网地址和国内地址。启用 API 和 stats ,之后的流量统计用得到。

xhttp.json:

{
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "address": "example.com",
        "port": 443,
        "id": "xxxx",
        "encryption": "none"
      },
      "streamSettings": {
        "network": "xhttp",
        "xhttpSettings": {
            "path": "/xray"
        },
        "security": "tls",
        "tlsSettings": {
        }
      }
    }
  ]
}

这里省略cdn.json,因为除了地址,别的内容都差不多。

~/program/Xray/xray.sh:

route="$(cat route)"
./bin/xray run -c conf/base.json -c "conf/$route"

启动时从route文件读取配置文件名称。

switch-proxy.sh:

cat << EOF
Choose one of the routes:
[1]: Direct
[2]: CDN
EOF

# 交互式设置 route
route=xhttp.json
read -p "Choice: " id
if [ "$id" == "1" ]; then
    route=xhttp.json
elif [ "$id" == "2" ]; then
    route=cdn.json
else
    echo "Use default route"
fi

echo -n "$route" > ~/program/Xray/route
echo "Route: $route"
systemctl --user restart xray
echo "Xray core restarted"

开机自启

用 user 级别的 systemd service 。创建~/.config/systemd/user/xray.service

[Unit]
Description=Xray core
After=network-online.target

[Service]
Type=simple
WorkingDirectory=%h/program/Xray/
ExecStart=bash %h/program/Xray/xray.sh

[Install]
WantedBy=default.target

启用服务:

systemctl --user enable xray.service

让它在开机时启动:

loginctl enable-linger

流量统计

Xray 提供了一个 HTTP 的 API ,用于统计,用不着 gRPC 。这种功能,用 Node.js 是最简单的。

function formatBytes(bytes, decimals = 2) { // 从 Stackoverflow 上抄的
    if (!+bytes) return '0 Bytes'
    const k = 1024
    const dm = decimals < 0 ? 0 : decimals
    const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB']
    const i = Math.floor(Math.log(bytes) / Math.log(k))
    return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`
}

const response = await fetch("http://localhost:10812/debug/vars")
const json = await response.json()
const outbound = json.stats.outbound
console.log({ // Node 打印 object 时有缩进和颜色,好看一些
    proxy: {
        uplink: formatBytes(outbound.proxy.uplink),
        downlink: formatBytes(outbound.proxy.downlink)
    },
    direct: { 
        uplink: formatBytes(outbound.direct.uplink),
        downlink: formatBytes(outbound.direct.downlink)
    }
})

在第一行加上#!/usr/bin/env node,再chmod +x stats.js,就可以直接在命令行中./stats.js。(这里不直接把它放到代码块里,因为会影响代码高亮)

日志

Xray 的 stdout 会被记录到 journal 里面,可以用这个命令查看连接日志:journalctl --user -u xray -f -o cat-f是实时更新,-o cat设置输出格式为原始内容。

打印出来是这样的:

2026/09/03 23:40:24.926246 from DNS accepted https://dns.google/dns-query [dns-module -> proxy]
2026/09/03 23:40:26.464586 from tcp:127.0.0.1:47430 accepted tcp:github.com:443 [socks >> proxy]
2026/09/03 23:43:20.517017 from 127.0.0.1:34974 accepted tcp:127.0.0.1:10812 [api -> api]

可以看到每个连接的来源和去向。

总结

虽然内存占用没有减少很多,但开机的时候完全没有感受,体验好了很多。