Skip to content

Node stops xray on keepalive timeout and does not recover automatically #50

Closed as duplicate

Description

@Nafanya1337

Bug: Node stops xray on keepalive timeout and does not recover automatically

Summary

When the panel becomes unreachable (server shutdown, network issue, etc.), the node waits for the keepalive timeout and then calls Disconnect(), which stops xray. After the panel comes back online, xray does not restart automatically. Users lose access to the node until a manual docker restart is performed.

Root cause

In controller/controller.go, keepAliveTracker() calls c.Disconnect() on timeout:

if time.Since(lastRequest) >= keepAlive {
    log.Println("disconnect automatically due to keep alive timeout")
    c.Disconnect() // stops xray, sets backend = nil, no auto-restart
}

Disconnect() shuts down the backend and sets c.backend = nil. There is no logic to restart xray when the panel reconnects — Connect() only starts monitoring goroutines, not the backend.

This means any temporary panel unavailability (reboot, network blip, deploy) longer than the keepAlive interval will kill xray on all nodes permanently until manual intervention.

Suggested fix

On keepalive timeout, stop monitoring goroutines and enter a degraded state, but keep xray running. On reconnect from the panel, clear the degraded flag, perform a full user and config sync, then resume normal operation.

Xray should never be stopped due to a lost panel connection — only due to an explicit operator action.

Steps to reproduce

  1. Set keepAlive to 30s on the node in the panel
  2. Confirm xray is running and panel is polling the node (GetStats / GetBackendStats in logs)
  3. Block all traffic from/to the panel IP:
ufw deny from <PANEL_IP>
ufw deny to <PANEL_IP>
ss -K dst <PANEL_IP>
ss -K src <PANEL_IP>
  1. Wait for keepalive timeout
  2. Restore connectivity:
ufw delete deny from <PANEL_IP>
ufw delete deny to <PANEL_IP>
  1. Wait 2-3 minutes, check xray:
docker exec node sh -lc 'pidof xray || echo stopped'

Expected behavior

Node keeps xray running with the last known config. When panel reconnects — full sync and resume.

Actual behavior

xray stops on keepalive timeout and does not restart when panel reconnects. Manual docker restart required.

Logs

[...] Method: GetStats, Status: Success
[...] Method: GetBackendStats, Status: Success
[...] disconnect automatically due to keep alive timeout
[...] xray core stopped

2026/05/21 13:35:01 IP: <PANEL_IP>, Method: GetBackendStats, Status: Success
2026/05/21 13:35:09 IP: <PANEL_IP>, Method: GetStats, Status: Success
2026/05/21 13:35:39 disconnect automatically due to keep alive timeout
2026/05/21 13:35:39 xray core stopped
root@Abob:# date
Thu May 21 01:48:53 PM UTC 2026
root@Abob:
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b7c2cf308a03 pasarguard/node:latest "./main" 50 minutes ago Up 50 minutes node
root@Abob:~#

After panel restored — no xray restart, pidof xray returns empty.

Environment

  • Node version: 0.4.3
  • Image: pasarguard/node:latest
  • xray version: 26.3.27
  • keepAlive setting: 30s
  • timeouts 10/15

Activity

  1. Nafanya1337 commented on May 21, 2026

    @Nafanya1337
    Author

    some script for testing on your node

    #!/bin/bash
    NODE="node"
    PANEL_IP="<PANEL-IP>"
    OUT="bugreport_$(date +%Y%m%d_%H%M%S).txt"
    
    log() { echo "[$(date '+%H:%M:%S')] $1" | tee -a $OUT; }
    
    log "=== STEP 1: baseline ==="
    docker exec $NODE sh -lc 'pidof xray && echo "xray: running" || echo "xray: stopped"' | tee -a $OUT
    
    log "=== STEP 2: block panel ==="
    ufw deny from $PANEL_IP
    ufw deny to $PANEL_IP
    ufw reload
    ss -K dst $PANEL_IP 2>/dev/null
    ss -K src $PANEL_IP 2>/dev/null
    log "panel blocked, connections killed"
    
    log "=== waiting for keepalive timeout (watch logs) ==="
    log "run in another terminal: docker logs -f $NODE"
    read -p "Press Enter when you see 'xray core stopped' in logs..."
    
    log "=== STEP 3: after timeout ==="
    docker exec $NODE sh -lc 'pidof xray && echo "xray: running" || echo "xray: stopped"' | tee -a $OUT
    
    log "=== STEP 4: restore panel ==="
    ufw delete deny from $PANEL_IP
    ufw delete deny to $PANEL_IP
    ufw reload
    log "panel unblocked"
    
    log "=== waiting 3 min for panel to reconnect ==="
    sleep 180
    
    log "=== STEP 5: after restore ==="
    docker exec $NODE sh -lc 'pidof xray && echo "xray: running" || echo "xray: stopped"' | tee -a $OUT
    docker logs --tail 30 $NODE >> $OUT 2>&1
    
    log "=== DONE, report: $OUT ==="
    cat $OUT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions