# Sing Box Ubuntu Setup

> Configures sing-box on Ubuntu for split-routing of system traffic through a TUN interface. Use when the user wants selective proxying (some apps through VPN, others direct), automatic failover between multiple VPN servers, DNS leak prevention, and global mode switching via Clash API.

- Skill: `bibendi/sing-box-ubuntu-setup` (Agent Skill, multi-file: 19 files)
- Install (CLI): `npx skillmds@latest add bibendi/sing-box-ubuntu-setup`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bibendi/sing-box-ubuntu-setup/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: bibendi (https://skillmd.com/u/bibendi)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bibendi/sing-box-ubuntu-setup

---


# sing-box Ubuntu Setup

## Context & Purpose

Use this skill when an agent needs to install, configure, or troubleshoot a sing-box deployment on Ubuntu that:

- Captures all system traffic through a TUN interface (not per-app proxies).
- Routes some domains and CIDR ranges through one or more VPN outbounds and the rest directly.
- Uses curated geosite and geoip lists to decide the route for each connection.
- Has automatic failover between multiple VPN servers.
- Exposes a "panic button" to force all traffic direct when every VPN server is down.
- Coexists with other network-namespaced tools (Tailscale, WireGuard, Docker).

The skill assumes the user already has one or more VPN outbounds (VLESS, VMess, Trojan, Shadowsocks, Hysteria2, WireGuard, etc.) supplied by their own server. The skill does not provide VPN credentials.

## Target Directory Architecture

```text
./skills/sing-box-ubuntu-setup/
├── SKILL.md                                # Router (this file)
├── README.md                               # Public-facing overview
├── references/
│   ├── architecture.md                     # Runtime model, TUN lifecycle, IP rule coexistence
│   ├── initialization.md                   # Install, directories, first config, systemd unit
│   ├── migration.md                        # sing-box 1.11 → 1.12 → 1.13 breaking changes
│   ├── recipes/
│   │   ├── setup-split-routing.md          # rule_set + .srs files + route ordering
│   │   ├── configure-split-dns.md          # direct-dns + proxy-dns + dns.rules
│   │   ├── add-failover-and-panic-button.md # urltest outbound + clash_mode
│   │   ├── keep-tailscale-working.md       # IP rule priority + fwmark coexistence
│   │   ├── preset-global-proxy.md          # The reference deployment in one file
│   │   ├── schedule-rule-updates.md        # cron job for .srs refresh
│   │   └── verify-routing-decisions.md     # Clash API: /rules, /connections, /traffic
│   └── troubleshooting.md                  # Errors, symptoms, fixes
└── scripts/
    ├── install-sing-box.sh                 # apt or binary download + user creation
    ├── download-geo.sh                     # Pull .srs files from a CDN (parameterized)
    ├── preset-global-proxy.sh              # Fetch the canonical .srs bundle in one call
    ├── update-geo.sh                       # Refresh + reload sing-box (cron-friendly)
    ├── mode-switch.sh                      # Switch clash_mode via PATCH /configs
    └── backup-config.sh                    # Snapshot config.json with timestamp
```

## Routing Map

| Question | Read |
|---|---|
| How does sing-box intercept traffic? What does it own? | `references/architecture.md` |
| How do I install and start it for the first time? | `references/initialization.md` |
| I run an old version. What changed? | `references/migration.md` |
| I want to route specific sites through VPN, others direct. | `references/recipes/setup-split-routing.md` |
| DNS leaks or wrong resolver for proxy domains. | `references/recipes/configure-split-dns.md` |
| Multiple VPN servers, automatic failover, panic button. | `references/recipes/add-failover-and-panic-button.md` |
| Tailscale stops working after sing-box starts. | `references/recipes/keep-tailscale-working.md` |
| I want a working "global proxy + curated direct lists" preset. | `references/recipes/preset-global-proxy.md` |
| I want .srs files to refresh automatically. | `references/recipes/schedule-rule-updates.md` |
| How do I see what rule matched a request? | `references/recipes/verify-routing-decisions.md` |
| Some specific error or behaviour is wrong. | `references/troubleshooting.md` |

## Reading Order

1. `references/architecture.md` — understand TUN, IP rules, the DNS path, and outbound chains before touching the config.
2. `references/initialization.md` — install the binary, create directories, deploy a minimal config, enable systemd.
3. Recipe files — read only the one matching the user's current task. For the most common "global proxy + curated direct lists" deployment, start with `references/recipes/preset-global-proxy.md` and use `scripts/preset-global-proxy.sh` to fetch the .srs bundle.
4. `references/migration.md` — read only when upgrading between minor or major versions.
5. `references/troubleshooting.md` — read only when something that should work does not.

## Critical Constraints

- Do not invent VPN credentials, server names, or UUIDs. The user supplies these.
- Do not hard-code any user-specific paths in shared files. The config snippets use `/etc/sing-box` and `/var/lib/sing-box` as the standard layout.
- Do not bypass `sing-box check` before restarting the service. The daemon aborts on a bad config and the user loses connectivity.
- Do not put `clash_mode` rules below rule_set rules. Mode overrides must be above geo rules to be honoured.
- Do not use `strict_route: true` unless the user has no other virtual interfaces (Tailscale, WireGuard, Docker). Strict mode rewrites the main routing table and can break neighbours.
- Do not delete the .srs files referenced by `route.rule_set` without first removing the matching rule_set entry. sing-box fails to start if a referenced file is missing.
- For shared/public publication: replace any real domain, IP, UUID, or server name in the snippets with placeholders like `<your-vpn-host>`, `<your-uuid>`, `<your-public-key>`.

## Top-Level Skeleton

A minimal config that this skill expands upon:

```json
{
  "log": { "level": "info", "timestamp": true },
  "dns": {
    "servers": [
      { "tag": "direct-dns", "type": "udp", "server": "<local-dns>" },
      { "tag": "proxy-dns",  "type": "tls", "server": "<remote-dns>" }
    ],
    "rules": [
      { "rule_set": ["geosite-proxy"], "server": "proxy-dns" },
      { "server": "direct-dns" }
    ],
    "final": "direct-dns"
  },
  "inbounds": [
    {
      "type": "tun", "tag": "tun-in",
      "interface_name": "tun0",
      "address": ["172.19.0.1/30"],
      "auto_route": true,
      "strict_route": false,
      "endpoint_independent_nat": true,
      "stack": "system"
    }
  ],
  "outbounds": [
    { "type": "direct", "tag": "direct" },
    { "type": "<vpn-type>", "tag": "primary", "...vpn-options..." },
    { "type": "<vpn-type>", "tag": "backup",  "...vpn-options..." }
  ],
  "route": {
    "default_domain_resolver": "direct-dns",
    "rule_set": [/* see references/recipes/setup-split-routing.md */],
    "rules": [
      { "action": "sniff" },
      { "protocol": "dns", "action": "hijack-dns" },
      /* mode overrides, then block, then proxy, then direct, then fallback */
    ],
    "final": "direct"
  },
  "experimental": {
    "clash_api": {
      "external_controller": "127.0.0.1:9090",
      "default_mode": "rule"
    }
  }
}
```

The real value is in how the `route.rules` list is composed. The recipes show the exact order and the rationale for each line.

