# How to Debug Connectivity Issues in S-UI: A Step-by-Step Guide to Sing-Box Diagnostics

> Debug S-UI connectivity issues with this step-by-step guide. Learn to check core status, inspect connections, close stale connections, and test outbound reachability effectively.

- Repository: [Alireza Ahmadi/s-ui](https://github.com/alireza0/s-ui)
- Tags: how-to-guide
- Published: 2026-05-22

---

**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`](https://github.com/alireza0/s-ui/blob/main/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`](https://github.com/alireza0/s-ui/blob/main/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:

1. **Core State** – Confirm the global `corePtr` initialized in [`main.go`](https://github.com/alireza0/s-ui/blob/main/main.go) is running via `IsRunning()`.
2. **Inbound Configuration** – Verify the inbound has been reloaded; stale connections persist after config changes unless explicitly closed.
3. **Connection Tracking** – Check `ConnTracker.connections` for entries matching your inbound tag. Use `CloseConnByInbound(tag)` to force cleanup.
4. **Outbound Reachability** – Execute a URL test through the suspect outbound to isolate routing failures.
5. **System Diagnostics** – Query CPU, memory, and network stats to rule out resource starvation.
6. **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`](https://github.com/alireza0/s-ui/blob/main/service/inbounds.go) during `RestartInbounds`.

### Test Outbound Reachability

The `core.CheckOutbound(ctx, tag, testURL)` function (defined in [`core/outbound_check.go`](https://github.com/alireza0/s-ui/blob/main/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`](https://github.com/alireza0/s-ui/blob/main/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

```go
// 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

```go
// 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

```go
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

```go
svc := service.ServerService{}
status := svc.GetStatus("cpu,mem,net,sys")
b, _ := json.MarshalIndent(status, "", "  ")
fmt.Println(string(b))

```

### Retrieve Recent Error Logs

```go
logs := service.ServerService{}.GetLogs("20", "error")
for _, line := range logs {
    fmt.Println(line)
}

```

## Key Source Files for Connectivity Debugging

| File | Purpose |
|------|---------|
| [`core/box.go`](https://github.com/alireza0/s-ui/blob/main/core/box.go) | Initializes the sing-box core, registers managers, and exposes the `corePtr` global state. |
| [`core/tracker_conn.go`](https://github.com/alireza0/s-ui/blob/main/core/tracker_conn.go) | Implements `ConnTracker` with `CloseConnByInbound()` and connection untracking logic. |
| [`service/inbounds.go`](https://github.com/alireza0/s-ui/blob/main/service/inbounds.go) | Contains `RestartInbounds()`, which forces clean reloads and closes lingering connections. |
| [`core/outbound_check.go`](https://github.com/alireza0/s-ui/blob/main/core/outbound_check.go) | Provides `CheckOutbound()` for latency testing and error detection. |
| [`service/server.go`](https://github.com/alireza0/s-ui/blob/main/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.go`](https://github.com/alireza0/s-ui/blob/main/core/tracker_conn.go) using `ConnTracker` to 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`](https://github.com/alireza0/s-ui/blob/main/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`](https://github.com/alireza0/s-ui/blob/main/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`](https://github.com/alireza0/s-ui/blob/main/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.