How to Debug Connectivity Issues in S-UI: A Step-by-Step Guide to Sing-Box Diagnostics
To debug connectivity issues in S-UI, verify the core is running via corePtr.IsRunning(), inspect active connections in ConnTracker, force-close stale connections with CloseConnByInbound(), and test outbound reachability using core.CheckOutbound().
S-UI is a web-based user interface built on the sing-box core that simplifies proxy management, but when clients report "cannot reach the server," administrators need systematic debugging tools. The codebase in alireza0/s-ui exposes granular connection tracking and diagnostic hooks that allow you to pinpoint failures across the inbound, routing, and outbound layers. This guide walks through the exact source code paths and methods used to diagnose network problems.
Understanding S-UI’s Connection Architecture
S-UI’s networking stack centers on the core.Box instance, which creates a route.ConnectionManager and maintains a ConnTracker in core/tracker_conn.go. Every inbound-side TCP or UDP stream passing through the router is wrapped in wrappedConn or wrappedPacketConn and stored in the tracker map, keyed by a unique UUID.
The tracker serves as the source of truth for active sessions. When an inbound configuration changes, old connections may linger and block ports or consume file descriptors. The shouldUntrackIOErr helper (lines 14-27 in core/tracker_conn.go) automatically removes finished connections to prevent memory leaks, but manual intervention is often required during debugging.
Step-by-Step Debugging Workflow
When troubleshooting "cannot reach the server" errors, investigate these layers in sequence:
- Core State – Confirm the global
corePtrinitialized inmain.gois running viaIsRunning(). - Inbound Configuration – Verify the inbound has been reloaded; stale connections persist after config changes unless explicitly closed.
- Connection Tracking – Check
ConnTracker.connectionsfor entries matching your inbound tag. UseCloseConnByInbound(tag)to force cleanup. - Outbound Reachability – Execute a URL test through the suspect outbound to isolate routing failures.
- System Diagnostics – Query CPU, memory, and network stats to rule out resource starvation.
- Log Analysis – Pull recent entries with
GetLogs()to identify specific errors like "dial timeout" or "peer closed".
Verify Core State
Before inspecting connections, ensure the sing-box core is active. The global pointer corePtr exposes IsRunning(), which returns false if the core has crashed or failed to initialize.
Inspect and Manage Active Connections
Access the live connection map through corePtr.GetInstance().ConnTracker(). Iterate over the connections map to count active sessions for a specific inbound tag. If you observe stale entries from previous configurations, call CloseConnByInbound(tag) to close all associated net.Conn and network.PacketConn instances. This method is invoked automatically in service/inbounds.go during RestartInbounds.
Test Outbound Reachability
The core.CheckOutbound(ctx, tag, testURL) function (defined in core/outbound_check.go) attempts to fetch a URL through the specified outbound. It returns a CheckOutboundResult struct with three fields:
OK– Boolean indicating success.Delay– Rounded latency in milliseconds.Error– Human-readable failure message.
This test isolates whether connectivity issues stem from the client-to-inbound path or the outbound-to-destination path.
Check System Resources
Resource exhaustion can manifest as dropped packets or refused connections. ServerService.GetStatus("cpu,net,sys") (from service/server.go) returns JSON-encoded diagnostics. High CPU usage or saturated network interfaces indicate the host, not the proxy configuration, is the bottleneck.
Analyze Logs
Use logger.GetLogs(count, level) to retrieve recent entries. Filtering for "error" level often reveals the exact disconnect reason, such as DNS resolution failures or TLS handshake timeouts.
Practical Code Examples
Verify Core and Count Inbound Connections
// Check if core is running and count connections for tag "socks-in"
if !corePtr.IsRunning() {
fmt.Println("❌ Core is not running")
return
}
tag := "socks-in"
tracker := corePtr.GetInstance().ConnTracker()
cnt := 0
for _, c := range tracker.connections {
if c.Inbound == tag {
cnt++
}
}
fmt.Printf("🔌 %d active connections for inbound %s\n", cnt, tag)
Reset Connections for a Specific Inbound
// Reproduce RestartInbounds logic from service/inbounds.go
func resetInbound(tag string) error {
if !corePtr.IsRunning() {
return fmt.Errorf("core not running")
}
// Close stale connections
closed := corePtr.GetInstance().ConnTracker().CloseConnByInbound(tag)
fmt.Printf("🗑️ Closed %d stale connections for %s\n", closed, tag)
// Fetch and reload configuration (simplified)
inboundConfig, err := fetchInboundJSON(tag) // user-defined helper
if err != nil {
return err
}
// Add users if inbound type supports them
inboundConfig, err = inboundService.addUsers(db, inboundConfig, inboundId, inboundType)
if err != nil {
return err
}
return corePtr.AddInbound(inboundConfig)
}
Test Outbound Endpoint
ctx := context.Background()
outTag := "proxy-1"
testURL := "https://www.google.com/generate_204"
result := core.CheckOutbound(ctx, outTag, testURL)
if result.OK {
fmt.Printf("✅ Outbound %s reachable, latency %d ms\n", outTag, result.Delay)
} else {
fmt.Printf("❌ Outbound %s error: %s\n", outTag, result.Error)
}
Pull System Diagnostics
svc := service.ServerService{}
status := svc.GetStatus("cpu,mem,net,sys")
b, _ := json.MarshalIndent(status, "", " ")
fmt.Println(string(b))
Retrieve Recent Error Logs
logs := service.ServerService{}.GetLogs("20", "error")
for _, line := range logs {
fmt.Println(line)
}
Key Source Files for Connectivity Debugging
| File | Purpose |
|---|---|
core/box.go |
Initializes the sing-box core, registers managers, and exposes the corePtr global state. |
core/tracker_conn.go |
Implements ConnTracker with CloseConnByInbound() and connection untracking logic. |
service/inbounds.go |
Contains RestartInbounds(), which forces clean reloads and closes lingering connections. |
core/outbound_check.go |
Provides CheckOutbound() for latency testing and error detection. |
service/server.go |
Exports GetStatus() for CPU, memory, and network resource monitoring. |
Summary
- Verify core state with
corePtr.IsRunning()before investigating connection issues. - Inspect active connections in
core/tracker_conn.gousingConnTrackerto identify stale sessions. - Force-close connections via
CloseConnByInbound(tag)when reloading inbound configurations. - Test outbound paths with
core.CheckOutbound()to isolate routing failures. - Monitor system resources through
ServerService.GetStatus()to detect resource starvation. - Analyze logs using
GetLogs()to capture specific error messages from the sing-box core.
Frequently Asked Questions
How do I know if the S-UI core is actually running?
Check the global corePtr instance exposed in core/box.go. Calling corePtr.IsRunning() returns a boolean indicating whether the sing-box core has successfully initialized and is currently active. If this returns false, no connections can be established regardless of configuration correctness.
What causes stale connections to block new inbound traffic?
When you modify an inbound configuration via the UI or API, the old listener may close but existing wrappedConn or wrappedPacketConn instances remain in the ConnTracker map. These sockets hold file descriptors and can prevent port rebinding. Call CloseConnByInbound(tag) to forcibly terminate all connections associated with that inbound tag.
How can I test if an outbound server is reachable from S-UI?
Use the core.CheckOutbound() function in core/outbound_check.go. Pass a context, the outbound tag, and a test URL such as https://www.google.com/generate_204. The function returns a CheckOutboundResult indicating success, latency in milliseconds, or a specific error string detailing the failure reason.
Where does S-UI store active connection metadata?
Active connections are stored in the connections map inside the ConnTracker struct defined in core/tracker_conn.go. This map uses a UUID as the key and stores connection wrappers that include the inbound tag, network type (TCP/UDP), and underlying net.Conn or network.PacketConn interfaces.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →