服务:nix收敛代理配置

一句话结论

依你 2026-10-11 的定案:proxy.nix 只解决 system 面(环境变量 / mihomo 国内直连清单 / 给非 nix 脚本的 env 文件),不再把各应用的代理往 proxy.nix 里塞;各应用在自己模块里接线,值统一从 config.networking.proxy.* 取。已于 2026-10-11 实施完成并验收,三处改动见下。

职责划分(你定案 2026-10-11)

层文件管什么
system 面(唯一值源)modules/system/service/proxy.nix顶部 let 里的 proxyDefault / cnDirectDomains / baseNoProxy ⇒ 写出 networking.proxy.default 与 networking.proxy.noProxy;生成 /etc/mihomo/cn-direct.yaml;生成 /etc/proxy/env.sh
应用面(各自接线,值引上面)modules/system/service/docker.nixdocker daemon 拉镜像的代理(本机生效的是 rootless.daemon.settings)
应用面(各自接线,值引上面)modules/system/service/git.nix/etc/gitconfig 的 http(s).proxy
故意重复(不改)modules/desktop/browser/google-chrome.nixChrome 自带策略 + 自带绕过清单(模块注释写明“故意不复用,避免模块间依赖”)
其它消费者(只读 env)curl / uv / nix / hermes / KDE GUI统统跟随 networking.proxy 写出的环境变量,无需单独接线

★ 一个必须记住的坑:rootless 的落点是子选项

本机跑的是 rootless docker,而 nixpkgs 里 rootless 的错误/正确落点差别如下:

✗ virtualisation.docker.daemon.settings              ← 只喂给【rootful】单元(本机不生效,实测 daemon.json 仍是 { })
✓ virtualisation.docker.rootless.daemon.settings ← 本机真正生效的那一个(docker-rootless.nix 里独立的推出项)

验证方法(酸试):看单元实际引用的那个文件里有没有 proxies。实施后实测输出:

单元引用: /nix/store/pv52fmdali9m8w8apxd38v90a3dymizk-daemon.json
{ "proxies": { "http-proxy": "http://127.0.0.1:7890",
"https-proxy": "http://127.0.0.1:7890",
"no-proxy": "<103 条国内直连清单>" } }

落地方案(已实施)

① docker.nix(modules/system/service/docker.nix)—— 两段都写上(rootful 那份留作备用,同一个值源):

virtualisation.docker.daemon.settings =                 # 只对 rootful 生效,留着备用
lib.mkIf (config.networking.proxy.default != null) {
proxies = {
http-proxy = config.networking.proxy.default;
https-proxy = config.networking.proxy.default;
no-proxy = config.networking.proxy.noProxy;
};
};

virtualisation.docker.rootless.daemon.settings = # ★ 本机生效的就是这一段
lib.mkIf (config.networking.proxy.default != null) {
proxies = {
http-proxy = config.networking.proxy.default;
https-proxy = config.networking.proxy.default;
no-proxy = config.networking.proxy.noProxy;
};
};

⚠ 为什么要 mkIf (… != null):nixpkgs 的 networking.proxy.default 默认就是 null(没引 proxy.nix 时),不加守卫会把 null 写进 daemon.json。这是“引用数据源 + 不造假值”的最小写法,不是防御式编程。

② proxy.nix 补一份给非 nix 脚本 source 的文件(声明式,不用 tmpfiles):

environment.etc."proxy/env.sh".text = ''
export http_proxy=${proxyDefault}
export https_proxy=${proxyDefault}
export no_proxy=${config.networking.proxy.noProxy}
export all_proxy=${proxyDefault}
'';

③ git.nix 两行改为引用数据源(去掉硬编码):

http.proxy  = config.networking.proxy.default;
https.proxy = config.networking.proxy.default;

不做的事:不加任何“重启服务”的 hook;不改 Chrome 那份自带清单;不新增自定义 options;不把 docker 的接线搬进 proxy.nix。

实施记录(2026-10-11)

步骤结果
改动文件的备份/etc/nixos/backup/proxy-converge-20261011-181803/(三个文件原样)
rebuild两次均成功:proxy-docker-git-converge / proxy-rootless-setting
docker daemon 代理docker info ⇒ http://127.0.0.1:7890 ✓(daemon.json 里也确有 proxies ✓)
git 系统级代理git config --system --get http.proxy ⇒ http://127.0.0.1:7890 ✓
/etc/proxy/env.sh生成✓,四行导出(含 no_proxy 103 条)实际值均正确 ✓
重启 docker 的代价clash 断约 10~30 秒后自恢复;mariadb/clash/clash-ui 均回 Up ✓;127.0.0.1:3306 握手仍正常 ✓

本机代理链路现状(实测 2026-10-11)

编号消费者实际落点数据来源是否单一数据源
C01主机侧环境变量/etc/set-environmentnetworking.proxy.{default,noProxy}(proxy.nix 顶部 let)✓ 是
C02GUI 应用(KDE 系)~/.config/kioslaverc(ProxyType=4 = 读环境变量)跟随 C01✓ 是
C03git/etc/gitconfigprograms.git.config 引用 config.networking.proxy.default✓ 是(已去硬编码)
C04docker daemon(镜像拉取)rootless.daemon.settings ⇒ daemon.json引用 config.networking.proxy.*✓ 是(已声明式化)
C05Chrome自带策略 + 自带绕过清单google-chrome.nix(刻意重复)✓ 故意重复,不改
C06mihomo 容器(真正出网的代理本体)内联 data/config.yaml 国内直连段 + 生成 /etc/mihomo/cn-direct.yamlproxy.nix✓ 是
C07acurl只读环境变量跟随 C01✓ 是(已实测)
C07buv只读环境变量跟随 C01✓ 是(已实测)
C07cnix / nix-daemon只读环境变量(daemon 环境里也有)跟随 C01✓ 是(已实测)
C07dhermes读环境变量(config_defaults.py 里写死 trust_env: True)跟随 C01✓ 是(已实测+源码核对);本地端点例外

证据(毒代理 http://127.0.0.1:9 与真代理对照):curl 毒⇒000/真⇒204;uv 毒⇒Connection refused/真⇒正常;nix 用户侧 毒⇒Could not connect(7)/真⇒下载成功;nix-daemon 自身环境里就写着 127.0.0.1:7890;hermes 进程环境有代理 ✓ 且源码 hermes_cli/config_defaults.py:2212-2217 明确 “Let adapters read HTTP_PROXY/HTTPS_PROXY/NO_PROXY…”,web_routers/config_env.py:962 对本地端点 trust_env=False(刻意绕开代理,不是漏洞)。

代理本体 = 容器 clash,定义在 /s500data/docker/mihomo/docker-compose.yml(restart: always),对外服务 127.0.0.1:7890。

逐条裁决(针对“另一个 AI”的提案)

编号它的提案裁决理由
R01收敛到单一模块(proxy.nix)部分采信system 面已汇在 proxy.nix;应用面按你的定案留在各自模块内接线
R02用 options.services.unified-proxy 自定义选项 + apps 开关驳回与本机契约冲突:已全线消灭 options.local.*,改为一律“模块顶部 let 参数 + import 即生效”
R03HTTP_PROXY = mkIf cfg.apps.nixos proxyUrl 写在 sessionVariables 里修正类型非法;且本机不需要 —— networking.proxy 已覆盖这批环境变量
R04docker daemon 用 daemon.settings.proxies 声明采信但修正落点方向对;但本机 rootless,必须用 rootless.daemon.settings(实测 daemon.settings 对 rootless 不生效)
R05git 代理走 programs.git.config采信(已是),但修正本机 git.nix 已这么写,但值是硬编码 ⇒ 已改为引用数据源
R06Chrome 用 programs.chromium.extraOpts 强制代理驳回本机 Chrome 走 google-chrome 包 + policies.json;自带绕过清单是刻意设计
R07用 systemd.tmpfiles.rules 写 /system/service/proxy-data/env.sh驳回(意图采信)① 路径臆造 ② f+ 每次激活重写 = 可变状态。正确手段 = environment.etc
R08hook 里无条件 systemctl reload-or-restart docker驳回正是 2026-10-11 事故的做法:重启 daemon 会连带给 clash 断网
R09“三级生效机制”(含必须 reboot)修正不需要 reboot;真实生效点见下表

生效点表(真实版,替代“三级机制”)

改了什么要跑什么何时生效
proxy.nix / git.nix 的数据rebuild <描述>环境变量、/etc/gitconfig、/etc/proxy/env.sh 立即写入;已打开的终端要重开或 source /etc/set-environment
docker 的 daemon.jsonrebuild 后再做一次 systemctl --user restart docker.service⚠ 连带给 clash 断约 10~30 秒
GUI / Chrome 行为注销重登不必整机重启 ✗
代理本体(clash 容器)docker start clash clash-ui 或 docker compose -f /s500data/docker/mihomo/docker-compose.yml up -d立即

代理故障 30 秒定位手册 ★

认址规则:本机代理唯一合法值 = http://127.0.0.1:7890;出现任何别的 IP(尤其 192.168.*)就是错的。

第①问  代理本体在吗:
ss -lntp | grep 7890 # 没输出 ⇒ 起容器(命令见上一节)

第②问 报错里的地址是谁:
proxyconnect tcp: dial tcp 192.168.1.100:7890: connection refused
⇒ 说明【那个消费者自己的代理值】被写错了,不是网络问题(2026-10-11 就是这案)

第③问 各消费者当前值(一条命令全查):
printf 'env=%s\ngit=%s\ndocker=%s\nkde=%s\n' \
"${http_proxy:-未设}" \
"$(git config --system --get http.proxy)" \
"$(docker info 2>/dev/null | awk -F': ' '/HTTP Proxy/{print $2}')" \
"$(grep -h ProxyType ~/.config/kioslaverc)"
判读:env / git / docker 都应该是 127.0.0.1:7890;kde 的 ProxyType=4 = 用环境变量

第④问 修哪一处:
system 面(环境变量) → 只改 modules/system/service/proxy.nix 顶部一处 → rebuild
docker 那条不对 → 改 modules/system/service/docker.nix 里【rootless.daemon.settings】→ rebuild → 再 restart docker.service
git 那条不对 → 改 modules/system/service/git.nix → rebuild
用户级残留遮盖 → 删掉 ~/.config/systemd/user/*docker* 里的单元文件(它会盖掉 nix 单元)
环境变量没刷新 → 重开终端 / source /etc/set-environment

2026-10-11 真实案例:mariadb 容器起不来,报错正是第②条;根因 = 一个用户级 docker.service 单元(文件已不在,systemd 里残留加载定义)把 daemon 代理指向 192.168.1.100:7890(那是手机的地址),盖掉了 nix 侧正确单元;修法 = 清残留 + 重启 daemon;代价 = clash 断约 1 分钟。这次收敛之后,这类故障只需改对应模块一处 + rebuild。