Mar 06
Port forward with Tailscale
by Ryan Welch · 4 Min Read
Lately, there’s been growing interest in moving away from cloud services and self-hosting instead. Many of us set up a small home server, only to realize we need a reliable way to access these self-hosted services when we’re away from home. Using a VPN such as Tailscale ↗ makes this fairly straightforward—but the drawback is that every device needing access must have the Tailscale client installed.
An alternative is port-forwarding on your home router, this means exposing certain ports on your router and forwarding them to your home server. Sounds simple enough, but there’s a catch: the internet is running out of IPv4 addresses, and residential internet providers often use CGNAT (Carrier-Grade Network Address Translation). This can make traditional port-forwarding impossible.
CGNAT is essentially NAT at the ISP level. Instead of getting a dedicated public IPv4 address, your home connection is assigned a private IP address within the ISP’s network, and then your traffic flows out through a shared public IPv4. Because you don’t truly “own” a public IP in this setup, you can’t directly forward ports from the internet to your home.
A way around CGNAT is to combine a VPN and port-forwarding strategy: get a small cloud server with its own public IPv4 address, install Tailscale on it, and forward traffic from that public address to your home server over Tailscale.
Choose a cloud hosting provider (for example, DigitalOcean ↗) and create a small Linux server. Make sure it has:
Installing Tailscale is straightforward. As mentioned in their docs ↗, you can run a one-liner:
curl -fsSL https://tailscale.com/install.sh | shIP forwarding is a feature in Linux that enables data packets to be forwarded from one network interface to another. It’s disabled by default, so enable it:
sudo sysctl -w net.ipv4.ip_forward=1Tip
If the setting doesn’t persist after a reboot, edit /etc/sysctl.conf and set net.ipv4.ip_forward=1.
Next, configure iptables to forward traffic. Here’s an example (you’ll need to adjust details for your environment):
iptables -A PREROUTING -t nat -p tcp -i eth0 --dport 80 -j DNAT --to-destination 100.100.100.100:80iptables -A POSTROUTING -t nat -p tcp -d 100.100.100.100 --dport 80 -j MASQUERADEKey points:
-p tcp means you’re forwarding TCP traffic (change to UDP if needed).-i eth0 should be replaced with the public interface name on your cloud server (check via ip link show).--to-destination 100.100.100.100:80 and (-d 100.100.100.100 / --dport 80) is your home server’s Tailscale IP/port (Tailscale IPs typically start with 100.).--dport 80 in the first line indicates which port to forward on the public interface side.Conflicting rules
Tools like ufw may conflict with raw iptables rules, so be cautious if you’re mixing them.
It’s a good idea to configure some sort of firewall in order to only expose the necessary ports. You may have noticed we have already used iptables which as mentioned is the built-in firewall for linux, however for convenience I chose to use the firewall built into my cloud hosting service.
Restrict inbound traffic so only the essential ports are open:
Exposing 41641 is important. Without it, Tailscale will relay all traffic through their servers, this may affect your latency and throughput. Since your home server is behind CGNAT, it will not be possible to direct connect unless this port is open on the cloud server.
Tailscale NAT
Tailscale has a great blog post ↗ on how their VPN attempts to establish a direct connection.
By default, Linux uses an older congestion control algorithm that can be quite conservative (and punitive in the face of minor packet loss). Enabling BBR can help sustain higher throughput, especially if you have spotty or mobile connections. And if it’s good enough for Google ↗ then it’s good enough for me.
sudo sysctl net.ipv4.tcp_congestion_control=bbrecho "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.d/10-custom-kernel-bbr.confecho "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.d/10-custom-kernel-bbr.confTailscale has a newer feature called Funnel (currently in beta) that provides a public HTTPS URL to your Tailscale node—potentially cutting out the need for a separate cloud server. Depending on your needs, it might simplify setup. Check their docs to see if it suits you.
This approach lets you self-host securely and bypass CGNAT issues by routing through a cloud instance. Happy hosting!
Tailscale has a newer feature called Funnel (currently in beta) that provides a public HTTPS URL to your Tailscale node—potentially cutting out the need for a separate cloud server. Depending on your needs, it might simplify setup. Check their docs ↗ to see if it suits you.
See also Cloudflare Tunnels ↗.
Usage restrictions
Note that since these are services provided by Tailscale and Cloudflare respectively, they may have restrictions on the use case and type of traffic you can use the service for. For example, streaming video from a self-hosted application is against Cloudflare Tunnels ToS.
If your home ISP supports IPv6, you could host your service publicly without NAT. However, IPv6 adoption is still spotty, and some clients may not connect via IPv6.
This approach lets you self-host securely and bypass CGNAT issues by routing through a cloud instance. We also do not need to worry about dynamic IPs anymore!
Last edited Mar 15