4 min read

How to route your local traffic to a remote server

If you'd like to skip the backstory and get straight to the setup, scroll down to the Initial Considerations sections

So far my blog has mostly been my thoughts and opinions but I've decided I'm going to write some tutorials for things I've had trouble figuring out, mostly because I know if I don't write this down, I'll never be able to remember it later.

Around a year ago, I really got into self-hosting applications. Apart from the benefit of being in complete control of your data, I've learnt a lot about processes and networking setting them up and found a great community of self-hosting enthusiasts online with so many great open source projects for almost any need you might have. However, I soon stumbled on to a problem with hosting resources. Cloud resources aren't too cheap for hosting a lot of applications. Moreover, applications like Plex which are CPU heavy are not allowed on many shared hosting services like Hetzner or DigitalOcean unless you buy a dedicated server which is expensive. The alternate is to host locally on your computer, which is what I chose to do. However, my ISP puts my connection behind a Carrier Grade NAT and does not allow for port forwarding unless you pay for an expensive corporate plan. This left me with only one option, bypass the ISP restrictions and route all my traffic through a different server. However, this took me some time to figure out how to do, so here's a guide that should hopefully help you too.

Initial Considerations

There are a couple of ways you could route your traffic. A reverse SSH tunnel is one of the best ways to do this and it gives you complete control over the connection. However, it connects you to a particular computer, and does not make you part of that network. Moreover, there are more security concerns handling your own SSH tunnel and making sure it's encrypted properly and keeping it connected. For these reasons, I chose to use a VPN instead to connect the local computer and cloud server on a common network which then creates a tunnel through which devices on the network can communicate with each other. The best options for such a set are OpenVPN and Wireguard. Wireguard is more recent and claims faster speeds and better encryption. For this tutorial, I'll be using Tailscale which is VPN service built on Wireguard protocol that lets you connect your devices together, and abstracts a lot of the manual Wireguard setup. However, if you're comfortable writing your own Wireguard rules, that might provide slightly faster speeds. Tailscale has a very generous free tier that should work for most home hosting needs unless you need team members or a complicated mesh setup.

Local Server Setup

This would be the setup on your local machine which is behind NAT or if you want to route your traffic elsewhere for security and privacy reasons. You could setup your applications directly on the computer, or through docker containers for an additional layer of isolation. After that, create an account on Tailscale and download it for your platform. This will create a VPN that your device will be connected to, and we add the remote server to this network too to allow communication between them despite any ISP blocking. Once Tailscale is setup, you can check your network configuration details or your dashboard on tailscale.com to find your local server IP address in the VPN. This address is only valid within the virtual Tailscale network and won't be accessible from anywhere outside it. Let's call this LOCAL-VPN-IP.

Remote Server Setup

For your remote server, try to find a hosting service that offers a VPS with root access and a public IP and is located close to your local server location. This will help reduce latency which will help vastly if you plan on setting up streaming services. However, if you're hosting webpages only, a distant server should be fine too.

The main considerations are internet speeds and bandwidth. I chose DigitalOcean since they provided a server in the same city as mine and their starting plan of $5/mo is more than sufficient for this task with speeds over 1 Gbps and a bandwidth of 1000Gb per month. However, Hetzner, Linode, and Vultr are all great, cheap alternatives too for this. If you want to try out DigitalOcean, you can get started with my Referral Link here.

Once you have your server up and running, install Tailscale using their Linux Instructions. However, before you bring the network up using sudo tailscale up, enable IP forwarding using these instructions.

Then, run

sudo tailscale up --advertise-exit-node

This runs the Tailscale service on the remote server as an exit node, which means we can route the traffic from any other computer on the VPN through this node.

Back on the Tailscale Dashboard select the remote server, and under Review Route Settings enable the the "Use as Exit Node" option

Final IP-Tables Routing

Back on the local server, set the Exit Node to the Tailscale Service on the remote server. Now if we access the internet on the local server, it's all routed through the remote server, and we can verify this by checking the IP Address on sites like https://www.whatismyip.com/ which should show the remote server public IP.

However, this is only for outbound access. If we need someone to be able to access services on the local server from outside the network, we need to setup IP Table rules to forward incoming data on the Public IP which is generally connected on eth0 to local server through the VPN.

I tried a couple of rules but the one that finally worked for me was this -

iptables -t nat -A PREROUTING -d <REMOTE-PUBLIC-IP>/32 -p tcp -m tcp --dport <PUBLIC PORT> -j DNAT --to-destination <LOCAL-VPN-IP>:<APPLICATION-LOCAL-PORT>

For example, to run Plex the local application local port would be 32400 and the Public Port can be anything you want. If you have a firewall using UFW or the Cloud Provider's Firewall, make sure to edit inbound rules to allow access on the Public Port.

Now head over to http://REMOTE-PUBLIC-IP:PUBLIC-PORT and you should be able to access your application running locally.

If you have any questions or know of a better way to do this, feel free to reach out to me on my Twitter