Tailscale instead of port forwarding
Reach a home NAS, Pi or Minecraft server without opening a router port. How Tailscale gets through NAT, when it falls back to a relay, and what it cannot do.
Does Tailscale need port forwarding?
Tailscale does not need port forwarding, and for reaching your own machines it replaces port forwarding completely. Every device in your tailnet (the private network Tailscale builds between your devices) opens its own outbound connections. Nothing on your home router changes: no forwarded port, no DMZ (demilitarized zone) host, no dynamic DNS record, no public IP requirement. A NAS (network-attached storage box), a Raspberry Pi or a game server behind an ipTIME or a Fritz!Box router becomes reachable from your laptop anywhere, on its own private address, while the router's inbound side stays closed.
What it does not do is also short. Tailscale connects devices that are logged in to your tailnet, or shared into it. A stranger on the internet, or a friend who has not joined, cannot reach anything through it. Exposing a service to the whole public internet is a different job, covered near the end.
How Tailscale gets through NAT without a forwarded port
Port forwarding exists because of NAT (network address translation). Your router owns the one public IPv4 address, and every device behind it has a private one. An inbound packet arriving at the router carries no hint about which private device wants it, so the router drops it. A port forward is a manual rule that says "port 25565 belongs to 192.168.0.20". Tailscale gets the same result without the rule, in four steps.
First, every node dials out. On start, tailscaled opens an HTTPS connection to the coordination server on TCP 443. It sends its WireGuard public key and learns the public keys and addresses of every other node in the tailnet. The coordination server never carries your traffic, only the keys and the address book.
Second, each node finds out what it looks like from outside. It sends a STUN (session traversal utilities for NAT) request over UDP to port 3478 on Tailscale's relay servers. The reply contains the public IP and port that the router assigned to that outbound packet. This is the mapping a port forward would have created by hand, except the router created it on its own for an outbound flow. If the router supports UPnP or NAT-PMP, Tailscale also asks it for a mapping automatically, which helps, but nothing depends on it.
Third, both sides punch. Node A and node B learn each other's public endpoint through the coordination server. Both send UDP packets from their own listener, port 41641 by default, to the other side's public endpoint at the same time. Each router already holds an outbound mapping for its own node, so the packet arriving from the other side matches that mapping and is let in. From that moment the two nodes talk directly over WireGuard, and the router still has no rule.
Fourth, when punching fails, traffic relays. CGNAT (carrier-grade NAT, where the ISP puts you behind a second NAT it controls) and symmetric NAT (where the router picks a fresh port for every destination, so the STUN answer is useless) both defeat step three, and so does a corporate firewall that blocks UDP. Tailscale then sends the encrypted WireGuard packets through a DERP (designated encrypted relay for packets) server over TCP 443. The relay sees ciphertext only. It also costs latency and throughput, and why a relayed Tailscale connection is slow and how to get it direct covers how to tell which path you have and what to change.
None of those four steps is an inbound connection to your router. That is the whole reason no port forward is needed.
Install the client and check the path
The install is one script and one login on each device.
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale ip -4tailscale up prints a login URL. Open it, sign in, and the device joins the tailnet. tailscale ip -4 prints the device's tailnet address in the 100.64.0.0/10 range. That address stays the same across reboots and across networks, so it is the one you write into SSH configs and game clients.
From a second device in the tailnet, check the path to the first:
tailscale ping nasUse the hostname or the tailnet address. The first reply often reports via DERP, because the direct path is still being negotiated. Within a few seconds the replies should switch to a public IP and port, which means the hole punch worked. Replies that still say via DERP after a minute mean one side sits behind a NAT the punch cannot cross, and you have a relayed connection.
The one firewall rule worth adding
Tailscale works with no firewall changes on most networks, because outbound UDP is normally allowed. One optional rule improves the odds of a direct path: let UDP 41641 through.
On a home router or a workplace firewall, that means allowing outbound UDP to port 41641, plus outbound UDP to port 3478 for STUN, if outbound UDP is restricted at all. Most home routers allow it already, so there is nothing to do.
On a VPS the useful direction is inbound. A VPS has a public IP and no NAT in front of it, so an inbound rule for UDP 41641 makes the VPS an easy target for every peer: the peer's punch always lands, even when the peer's own NAT is difficult. With ufw:
sudo ufw allow 41641/udp
sudo ufw statusufw status should list 41641/udp as ALLOW from Anywhere. Providers that run a separate network firewall in their control panel need the same rule there, because a packet blocked at the provider edge never reaches ufw. This rule is a convenience, not a requirement. With the port closed the VPS still joins the tailnet and peers still reach it, some of them through a relay instead of directly.
Reach a home NAS or Raspberry Pi from outside
Case one is the simplest, and it is what most people mean by "port forwarding with Tailscale": get to a box at home from a laptop or phone that is not at home.
Install the client on the home box and on the laptop. That is the entire setup. The NAS or Pi now answers on its tailnet address for every service it runs, with nothing forwarded:
ssh pi@100.101.102.103
curl -I http://100.101.102.103:8080/Replace the address with the output of tailscale ip -4 on the home box. The SSH login should behave exactly as it does on the home Wi-Fi, and curl -I should print the HTTP status line of whatever web app the box runs. Because the connection is authenticated by WireGuard keys tied to a logged-in device, a service that would be dangerous on a forwarded port, such as an SMB (server message block) share or a web admin panel, is reasonable here: only your own devices can send it a packet at all. The ipTIME or Fritz!Box in between never sees an inbound connection and needs no change. If your ISP has put you behind CGNAT, this still works, because the home box only ever dials out, and in the worst case the path goes through a relay.
MagicDNS, on by default in new tailnets, also gives every node a name, so ssh pi@nas works once the laptop is on the tailnet.
When the device cannot run Tailscale: a subnet router
Some devices cannot run the client: a printer, an IP camera, an older NAS with a locked-down operating system, or a smart TV. A subnet router fixes that. One machine on the home network that can run Tailscale (a Pi is enough) advertises the whole home subnet, and other tailnet devices route to that subnet through it.
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-tailscale.conf
sudo sysctl --system
sudo tailscale up --advertise-routes=192.168.0.0/24Use your real home subnet. The route then needs approval in the admin console under the machine's route settings, and Linux clients must opt in with sudo tailscale set --accept-routes. After that, ping 192.168.0.50 from the laptop reaches the camera at home. The forwarding sysctl matters because without it the Pi drops every packet not addressed to itself, and the route shows as approved while nothing behind it answers. The approval step, the client-side opt-in and the other ways a route ends up approved but unreachable are in running a Tailscale subnet router, which uses a VPS as the router but applies unchanged to a Pi in the living room.
Let friends join a Minecraft server without opening the router
Case two: a Minecraft server on a PC at home, and friends who want to join. The old answer is to forward TCP 25565 on the router and hand out your public IP, which also hands it to every scanner on the internet. Tailscale replaces that with sharing.
Two options exist. The first is to invite the friends into your tailnet as users. That works, but user seats on the free plan are limited, and it gives them a login to your whole network, which is more than a game needs. The second, and better, option is node sharing. In the admin console, open the Machines page, find the game PC, open its menu and choose Share. Tailscale creates an invitation link. Each friend needs a free Tailscale account of their own, which makes them the owner of their own small tailnet, and accepts the link from that account. The shared machine then appears in their machine list, and only that machine. Sharing gives the recipient access to the shared machine and nothing else in your tailnet, and a shared machine is quarantined by default: it can answer connections from the friend's tailnet but cannot start any.
The friends add the server in Minecraft by its tailnet address, 100.101.102.103:25565, or just the address when the port is the default. The game PC runs with zero forwarded ports, and the router is untouched. Shared machines do not advertise subnet routes, so sharing works for a service that runs on the shared machine itself, which is exactly the game-server case.
The limit is that every player must run Tailscale and accept the share. For a group of friends that is fine. For a public server where anyone can join, it is not, and the next two sections are the answer.
Run the game server on a VPS and keep RCON on the tailnet
The other way to skip port forwarding is to stop hosting the game at home. A VPS has a public IP and no NAT, so the game port is open to the public without any router involved. Running a Minecraft server on a VPS covers sizing, the Java flags, backups and the systemd unit. The part that matters here is which ports face the internet.
A Minecraft server opens two ports. TCP 25565 is the game, and players need it. TCP 25575 is RCON (remote console), which accepts a password and then runs any server command, including op and stop. An RCON port open to the internet is a password-guessing target. Put the VPS in your tailnet and give the two ports different rules:
sudo ufw allow OpenSSH
sudo ufw allow 25565/tcp
sudo ufw allow in on tailscale0 to any port 25575 proto tcp
sudo ufw enable
sudo ufw statusThe second rule opens the game to everyone. The third opens RCON only to packets arriving on the tailscale0 interface, which means only from devices in your tailnet. The public IP still answers on 25565, and a connection to 25575 from the public side is dropped by ufw's default deny policy. Check both from your laptop, which is in the tailnet:
nc -zv 100.101.102.103 25575
nc -zv <vps-public-ip> 25575The first should print succeeded. The second should hang and then time out, because the packet arrived on the public interface and matched no allow rule. The same pattern covers SSH, a web map, a database or a metrics dashboard: public port on the public IP, admin ports on tailscale0 only.
Expose something to the whole internet: Funnel or a VPS reverse tunnel
Case three is the one Tailscale, by itself, does not solve. A blog, a public API, a public game server, a status page: the visitors are not in your tailnet and never will be. Two tools handle it, and each is its own post.
Tailscale Funnel publishes an HTTPS service from a node to the public internet through Tailscale's relays. It works only over TLS (transport layer security) and only on ports 443, 8443 and 10000, so it fits a web app and does not fit a raw game protocol on 25565. Tailscale Serve versus Funnel explains what each one exposes and to whom.
A VPS reverse tunnel handles everything else. The home box opens an outbound tunnel to a VPS with a public IP, and the VPS forwards a public port back down the tunnel, TCP or UDP, on any port number. That is the tool for a public Minecraft server hosted at home behind CGNAT, and a VPS reverse tunnel for a home server behind CGNAT walks through it. Pick one of these two and stop looking for a Tailscale setting that opens a port to the world. There is none.
What Tailscale does not do
- It does not forward a port for arbitrary public clients. A packet from someone outside the tailnet never reaches your node, because there is no WireGuard key for it, and a WireGuard packet with no valid key is dropped without a reply.
- It does not give the public a way in. People must be members of your tailnet or have accepted a shared node.
- It does not guarantee a direct path. On a difficult NAT the connection is relayed, which is slower.
- It does not open the router. The router stays closed, and that is the point.
FAQ
Does Tailscale need port forwarding?
No. Every Tailscale node makes outbound connections only. It reaches the coordination server over HTTPS on TCP 443 and learns its public address with STUN on UDP 3478. Direct peer connections come from UDP hole punching on port 41641, and when the punch fails traffic goes through a DERP relay over TCP 443, still outbound. A home router never needs a forwarded port or a DMZ host, and a device behind CGNAT joins the same way.
Can Tailscale replace port forwarding for a Minecraft server?
Yes, for a server that your friends join. Install Tailscale on the game PC, share that machine with each friend from the Machines page of the admin console, and they connect to its tailnet address on port 25565 with no port forwarded. It does not replace port forwarding for a public server, because players outside your tailnet cannot reach the node. For that, host the game on a VPS with a public IP and keep RCON on the tailnet, or use a VPS reverse tunnel from the home box.
Which ports does Tailscale use?
Three. TCP 443 outbound carries the coordination server connection and DERP relay traffic. UDP 3478 outbound is STUN, which discovers the public address. UDP 41641 is the default WireGuard listener for direct connections, and it can be changed if 41641 is taken. None of them must be opened inbound. Allowing inbound UDP 41641 on a VPS is optional and gives peers a direct path more often.
Why is my Tailscale connection relayed instead of direct?
tailscale ping keeps answering via DERP when the UDP hole punch cannot complete. The usual causes are a NAT the punch cannot cross (CGNAT, or a symmetric NAT that assigns a new port for every destination) and a firewall that blocks outbound UDP. Fix what you control: allow outbound UDP 41641 and 3478, or put one peer on a VPS with inbound UDP 41641 open, so the other side's punch always has a fixed target.