How I Set Up sing-box as a Transparent LAN Gateway

2025-10-26#NOTE

# The Goal and The Struggle

I spent the last two days battling with `nftables` and `iptables`, and it was a painful experience. My goal was simple: I wanted to route all the network traffic from my home router through my home server.

Why? I run a piece of software called `sing-box` on the server, and I wanted to use it to filter all the traffic on my network. This would give me a chance to block advertisements and any other unwanted requests for every device in my home.

I was convinced I needed complex firewall rules to get this to work. I was wrong.


# The Real Solution: sing-box Configuration

After two days of frustration, the solution turned out to be surprisingly simple: you don't need to modify `iptables` or `nftables` at all.

The entire routing mechanism can be handled directly by `sing-box`. The most critical part is correctly configuring your `inbounds`. The key was adding a `dns` inbound to my configuration, which allows `sing-box` to capture and handle DNS requests, directing traffic as needed.

One other hurdle: if you want to run `sing-box` as a `systemd` service (using `systemctl`), it needs elevated privileges to bind to low ports and manage network traffic. You must ensure the service runs as the `root` user and is granted the proper capabilities (like `CAP_NET_ADMIN` and `CAP_NET_BIND_SERVICE`).


# New Problem: Bypassing for Local Apps

## qbittorrent

I tried adding `process_name`, `process_path` or even `process_name_regex` to a route rule with outbound set to `direct`, but it didn't work.

The way I found working is creating a separate service user on my home server. Let's say the name is `qbittorent`, then start the service with this user.

Then, the most critical step, get the uid of this user, let's say it's 996.

There is an option called `exclude_uid` in tun configuration, can search the name in sing-box documentation. Set its value to 996. You are good to go.

This is telling sing-box to not handle traffic from this uid at all. By this, you don't even need to worry about routing rules. Because the traffic never goes into sing-box.

## WeChat

The same with WeChat. At first I tried using route rule to match traffic to direct outbound.

Again, the magic is never letting sing-box to handle the traffic.

This is a bit different from qbittorent. Instead `exclude_uid`, another field called `route_exclude_address_set` was used.

The value could be remote rule like `geoip-cn` or whatever you want to add.

## Cloudlfare Tunnel

I have a few service exposing to internet by Cloudflare Tunnel. I found there were lots of log like `lookup quic.cftunnel.com: empty result` showing up.

I found this GitHub issue and it's helpful: https://github.com/SagerNet/sing-box/issues/2437.

It's is in Chinese.

Basically, you shouldn't use `sniff_override_destination`. I don't exactly understand how disabling this field can fix this issue. But it just works!

## Tailscale Exit Node

Another setup I want to do is using Tailscale exit node as a proxy node. Which means if my devices are connecting to my home server, which is a Tailscale exit node. The traffic from my devices should be able to go through sing-box.

I achieve this by adding Tailscale endpoint setting to my sing-box configuration.

Not sure how it works, but again it just works...

I guess because endpoint in sing-box has both inbound and outbound. Perhaps sing-box has internalized the routing logic.


# My Network Setup (For Context)

I should mention that my network configuration is a bit specific, which is what makes this setup possible.

My Ubuntu server has multiple network interfaces. I used `Netplan` (the default network manager for Ubuntu) to configure the second network interface as a DHCP server.

My main home router then plugs directly into this server interface and acts as a DHCP client.

This physical setup forces all traffic from my home router (and all the devices connected to it) to pass through the Ubuntu server before it can reach the internet. This is what allows `sing-box` to intercept and manage everything. If your server only has a single network interface, your setup will likely be different.


# Lesson Learned: LLMs vs. The Manual

This experience taught me a valuable lesson. I can't count how many times I asked ChatGPT and Gemini for a solution. Every single time, they gave me complicated answers based on modifying `iptables` or `nftables`.

The real answer had nothing to do with them. It was simply about understanding the `sing-box` configuration file.

I think this is because `sing-box` is a relatively niche and specialized tool, so there isn't much material for LLMs to train on. The models defaulted to the "standard" Linux way of doing things, which, in this case, was the wrong path.

Next time I'm stuck, I'm going straight to the official documentation first.

# Helpful Resource

Finally, I found this GitHub repository full of configuration examples, which was incredibly helpful:

🩷
0
👍
0
😄
0
🙁
0