sshuttle: the VPN I already had

The problem with paying for a VPN

My VPN usage as an admin is small. I reach for it occasionally, not as a daily habit.

Commercial VPNs are built and priced for the daily habit. You pay a recurring subscription whether you connect once or every day. And the privacy promise comes down to trust: every packet leaves through the provider’s servers, and a “no logs” policy is a claim you cannot verify from the outside.

I already rent a VPS. I already SSH into it. The question was simple: why pay a third party to do what my own box can do?

The answer was sshuttle.

What sshuttle is

sshuttle creates a VPN-style connection to any server you can reach over SSH. The server needs Python 3.9 or newer and nothing else. You do not install sshuttle on the remote side. On connect, the client uploads its own Python code over the SSH session and runs it there, so there are no version conflicts between client and server.

You need root or sudo on your local machine. On the server, a normal user account is enough. Only the local firewall manager runs as root. The rest of sshuttle runs as your user.

It works on Linux, macOS, FreeBSD, OpenBSD and pfSense. Windows support exists but the project labels it experimental.

Why it does not suffer from TCP-over-TCP

Tunnelling packets over SSH normally performs badly. TCP relies on packet loss to know when to slow down. Wrap TCP inside another TCP stream like SSH, and the inner connection never sees loss because the outer one retransmits everything. Congestion control breaks and throughput collapses.

sshuttle avoids this by forwarding sessions, not packets. On Linux it uses kernel transparent proxying to capture outgoing TCP connections locally. It sends the data through the SSH session, then the server opens a fresh TCP connection to the real destination. Your kernel manages one TCP stream, the server’s kernel manages the other. What crosses the tunnel is plain data over TCP, which is safe.

Setup

Install sshuttle on the client only (see the installation docs), then:

sshuttle -r user@your-vps -x your-vps-ip:22 0/0 --dns

What each part does:

  • -r user@your-vps is the SSH target. It reads ~/.ssh/config, so host aliases and keys work as usual.
  • 0/0 routes all IPv4 TCP traffic through the VPS.
  • -x your-vps-ip:22 excludes the SSH connection itself from the tunnel. The docs note this may be required on some machines for sshuttle to work at all.
  • --dns intercepts plain DNS queries on port 53 and resolves them on the VPS side.

Only need one network, not everything? Route just that subnet:

sshuttle -r user@your-vps 10.0.0.0/24

That is the admin use case in one line. No split-tunnel config, no client app.

For something permanent, sshuttle supports systemd Type=notify, so a unit file can wait until the tunnel and firewall rules are actually up.

The disadvantages

sshuttle is not a drop-in replacement for a real VPN. These are the limits that matter.

UDP only works with TPROXY. The default nat method on Linux, the nft method, and pf on macOS and BSD handle TCP and DNS only. UDP needs the tproxy method, which takes extra setup. That means anything UDP-based outside of DNS, including QUIC, voice and many games, goes out your local connection untunnelled.

No ICMP. Ping and traceroute are not in the supported list for any method. You cannot use them to debug the remote side through the tunnel.

IPv6 can leak. The nat method does not support IPv6 at all. If your local network has IPv6 and you use the default method, IPv6 traffic skips the tunnel. Use --method nft or tproxy and add ::/0, or disable IPv6 locally.

DNS leaks without --dns. Leave the flag off and your queries go to your local resolver in the clear. Even with it on, only plain DNS on port 53 is captured.

Local root is mandatory. sshuttle rewrites your firewall rules, so it needs sudo on the client. On a locked-down corporate laptop, that may be a dead end.

It is not anonymous. This is the one people miss. Your VPS is rented in your name and paid with your card. Every site you visit sees the VPS IP, and that IP traces straight back to your account with the hosting provider. sshuttle removes the VPN company from the picture. It does not remove you from it. If your goal is anonymity, this is the wrong tool.

One egress location. A commercial VPN gives you dozens of countries on a dropdown. sshuttle gives you wherever your VPS lives.

Performance depends on one box. Throughput is capped by the VPS bandwidth and CPU. The docs also note that SSH’s large buffers can let a big file transfer slow every other session in the tunnel. sshuttle’s latency control limits this by default, at the cost of some raw bandwidth on fast links.

Who should use it

Use sshuttle if you:

  • already run a VPS with SSH access
  • need occasional access to remote networks or a trusted exit point
  • want to stop paying a subscription for occasional use
  • would rather trust your own server than a VPN provider’s logging policy

Skip it if you need anonymity, UDP-heavy traffic without extra setup, multiple exit countries, or you cannot get root on your client.

For my use, it replaced the subscription outright. The privacy gain is real but specific: I moved trust from a VPN company to infrastructure I control. That is the trade I wanted.

References