Skip to content

policy: restore ConnectionIdle to 120s (matches upstream v2node; fixes idle SSH/long-lived connections dropping at ~60s)#31

Merged
Ember-Moth merged 1 commit into
perfect-panel:masterfrom
echoowall:fix-connidle-timeout
Jul 13, 2026
Merged

policy: restore ConnectionIdle to 120s (matches upstream v2node; fixes idle SSH/long-lived connections dropping at ~60s)#31
Ember-Moth merged 1 commit into
perfect-panel:masterfrom
echoowall:fix-connidle-timeout

Conversation

@echoowall

@echoowall echoowall commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Problem

core/xray.go sets the level-0 policy ConnectionIdle to 30s:

ConnectionIdle: proto.Uint32(30),

xray closes a proxied connection once there is no application-layer data for ConnectionIdle seconds, and it enforces this with a polling activity timer (common/signal/timer.go): the check runs every ConnectionIdle seconds and only fires after a full interval with no activity following the one that consumed the previous activity token — so the effective timeout is ~2× the value, i.e. ~60s for 30.

The result: idle-but-healthy long-lived connections — interactive SSH, database/Redis, MQTT, WebSocket — are dropped after ~60s of silence even though the socket is perfectly fine. TCP keepalive does not help (it refreshes the socket, not this application-level timer).

On an affected node, a packet capture of an idle proxied SSH session showed the node itself sending FIN at exactly the 60s mark, with TCP keepalives still flowing on both legs — confirming an application-level idle timeout rather than a NAT/socket issue.

Context

  • This project is modified from wyx2685/v2node, which uses ConnectionIdle: 120. ppnode lowered it to 30 (4× more aggressive) at some point.
  • xray-core's own default is 300s (SessionDefault) / 600s (default manager) — so 30s is ~10–20× more aggressive than xray itself.

Fix

Restore ConnectionIdle to 120, matching upstream v2node. Verified end-to-end: with 30 an idle SSH tunnel dropped at ~60s every time; with the larger value the same tunnel stays up (no drop observed to 200s+).

A cleaner long-term fix would be to expose this as a config field (as v2node/V2bX do) instead of a hardcoded constant, so operators can tune it without patching source.

@echoowall

Copy link
Copy Markdown
Contributor Author

Extra findings (why 30s behaves like ~60s, and how it compares to xray / V2bX defaults)

The effective timeout is ~2× the configured value.
xray enforces ConnectionIdle with a polling activity timer (common/signal/timer.go): SetTimeout(d) starts a task.Periodic{Interval: d, Execute: check}, and each check() consumes at most one activity token. A connection that goes idle right after setup survives the first check (which consumes the setup's token) and is only closed on the second check — i.e. after ~2·d. So ConnectionIdle: 30 actually tears connections down at ~60s, which matches the packet capture (node sends FIN at 60.0s).

30s is far below xray's own default. xray-core defaults ConnectionIdle to 300s (features/policy/policy.go SessionDefault) / 600s for the default manager (features/policy/default.go). This project overrides that with 30s — ~10–20× more aggressive — which silently breaks idle-but-alive long-lived connections (interactive SSH, DB/Redis, MQTT, WebSocket).

vs V2bX: V2bX ships the same 30 default, but exposes it as a configurable field (connIdle in the xray config, core/xray/xray.go uses &c.ConnIdle), so operators can raise it. Here it's a hardcoded constant, so there is no way to change it without patching the source.

This PR just raises the constant. A cleaner long-term fix would be to expose it as a config option like V2bX does, defaulting to something no more aggressive than xray's own default.

This project lowered ConnectionIdle to 30s. xray enforces it with a
polling activity timer, so the effective idle timeout is ~2x the value
(~60s), and idle-but-healthy long-lived connections (interactive SSH,
database sessions, MQTT/WebSocket) get torn down after ~60s even though
the socket is fine. TCP keepalive doesn't help — it refreshes the socket,
not this application-level timer.

Restore it to 120, the value used by wyx2685/v2node which this project is
modified from. (xray's own default is 300-600s.)
@echoowall
echoowall force-pushed the fix-connidle-timeout branch from ce26a9c to 176c34e Compare July 12, 2026 18:40
@echoowall echoowall changed the title policy: raise ConnectionIdle from 30s to 1h (fixes idle SSH/long-lived connections being dropped) policy: restore ConnectionIdle to 120s (matches upstream v2node; fixes idle SSH/long-lived connections dropping at ~60s) Jul 12, 2026
@Ember-Moth
Ember-Moth merged commit d494878 into perfect-panel:master Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants