Host a game server on Starlink with a free VPS relay

Yes, for a game that uses TCP, such as Minecraft Java Edition: Starlink's help page says its default IPv4 configuration uses carrier-grade NAT (CGNAT) and "does not allow inbound traffic" (October 2026), so players connect to a cloud VM with a public IPv4 address instead, and a relay there passes each connection over a tailnet to the game server at home. In October 2026, a Minecraft Java server at home on a Starlink connection answered 30 of 30 server-list pings through such a relay on a free-tier VM, sent over the public internet from the same home connection; the relay leg, VM to server and back, was a median of about 20 ms (the three sittings' medians ran from 17.9 to 23.6 ms), estimated as the round trip through the relay less the round trip to the VM and the server's answer time. Games that use UDP, such as Minecraft Bedrock Edition and Palworld, need a relay that carries UDP: one UDP packet sent to the tested relay's game port got no answer, as at a closed port.

By Rowan Hale · Published

Promo: $100 off Starlink with my referral link

If you've never been a Starlink customer and you order an eligible Residential or Roam plan through my link, we both get Starlink's reward for your country. In the US, that is a $100 credit on your second monthly bill, and I get $100 too. Use my referral link

How players reach a server at home without port forwarding

If your router has no public IPv4 address, players' connections from the internet arrive at your provider's NAT first, and a port forward on your own router gets them only if that NAT forwards the port too. The guide to self-hosting without a public IPv4 address shows what that looks like from a home Starlink connection.

Starlink's help page What IP address does Starlink provide? says its default IPv4 configuration uses carrier-grade NAT (CGNAT), and that this "default" policy "does not allow inbound traffic" (checked October 2026). A public IPv4 address on the line itself is a Priority plan option, Starlink's help page on setting a public IP says.

A relay turns the connection around. The path has four steps:

  1. A player's game connects to the public IPv4 address of a cloud VM, the relay VM, on the game's port.
  2. A relay program on the VM accepts the connection and opens its own connection to the game server's address on a tailnet, the private network your machines join with Tailscale's client.
  3. The tailnet carries that connection home, straight between the two machines or through a DERP relay server. Tailscale's page on connection types says every connection starts out relayed through DERP and goes direct where it can, and that some networks, which it calls hard NAT, prevent the direct path (checked October 2026).
  4. The game server answers the same way back.

Players connect only to the relay VM's public address, and the relay, not the player, opens the connection to your home.

Minecraft Java answered 30 of 30 server-list pings through the relay

I host a Minecraft Java Edition server this way, in Docker at home on a Starlink connection. Players connect to a free-tier cloud VM with a public IPv4 address, where socat relays each TCP connection on port 25565 over my Headscale tailnet, which runs on WireGuard, to the game server. I set the tailnet's policy so the relay VM can reach that one port at home and nothing else.

A read on the game server in October 2026 found the itzg/minecraft-server image with online-mode, the whitelist and enforce-whitelist all on. Docker publishes TCP 25565 to it, not UDP.

The test ran in October 2026 from a wired computer on the home network, in three sittings of 10 rounds each, all on one day, every probe at least 2 s after the one before. Each round made a bare TCP connection to the relay VM's public address on port 25565, then a server-list ping through the relay: the status request a Minecraft client sends to show a server in its list, followed by a ping and pong on the same connection. It never logs in. Before each sitting, the route to the VM's address was checked: it left by the home connection's default route, not the tailnet. In every sitting, the status answer through the relay matched the one read straight from the game server over the tailnet, so the relay led to this server.

Table 1. Probes from a wired computer on the home network, in three sittings, October 2026: through the relay VM's public address over the public internet, with two controls. Each probe got 5 s to answer.
Probe Result
TCP connection to the relay VM, port 25565 30 of 30 connected
Server-list ping through the relay 30 of 30 answered
Control: TCP connection to port 25566 on the VM, which nothing serves 0 of 3 connected; each timed out, none was refused
Control: server-list ping straight to the game server over the tailnet 29 of 30 answered

The one direct ping that failed, and two that connected only after their first packet was sent again, were on the direct tailnet path inside the home network. The relay answered normally in the same rounds. Why the direct path stalled wasn't examined, since it was only a control.

Limitation

Every probe ran from the same home connection the game server uses: out over the public internet to the relay VM, then back home through the relay. A player on another network wasn't part of the test. The pings asked for the server's status only, with no login and no play.

The test's pings left no line in the game server's log. The log's existing player lines, from earlier logins, all carry one private address from the network Docker set up on that machine: never the relay VM's tailnet address and never a player's own address. The log doesn't show which way those players came in.

The relay leg, from the VM to the game server and back, came to about 20 ms (an estimate)

A round trip through the relay covers the leg from the player to the relay VM and the relay leg: from the VM over the tailnet to the game server and back, with the relay's own forwarding. The relay leg came to about 20 ms, as an estimate. That is the median of 27 rounds, and the three sittings' medians ranged from 17.9 to 23.6 ms. The satellite internet comparison sets this estimate beside the minimum round trip through a geostationary satellite.

For each round, the estimate takes the ping-to-pong time through the relay, less that same connection's round trip to the VM, measured a moment before, and less the game server's own answer time, measured straight over the tailnet. The parts weren't measured at the same instant, and the home connection's round trip changes from second to second, as the latency guide shows. Three rounds were left out because their direct connection, which the estimate relies on, failed or needed a packet sent again.

The test computer and the game server share the home connection, so every probe through the relay left home over Starlink to the VM and came back in over Starlink inside the tailnet. For a player elsewhere, their own leg to the VM takes the place of the test computer's, and the relay leg stays.

Limitation

Not measured: a remote player's own leg to the VM, which depends on their network and how far they are from the VM; real play, as opposed to status pings; the round trip the game shows; whether the tailnet path from the VM to home ran direct or through a DERP relay server; the VM's own load; and other days and times.

A TCP relay carries TCP games; UDP games need a UDP relay

A TCP relay carries TCP only. Each game's own documentation or wiki says which protocol its server uses (checked October 2026):

Game Default ports, from its docs Relay it needs
Minecraft Java Edition TCP 25565 TCP, the relay tested here
Minecraft Bedrock Edition UDP 19132 (19133 for IPv6 with the RakNet transport) UDP
Terraria TCP 7777 TCP; see below
Factorio UDP 34197 UDP
Palworld UDP 8211 UDP
ARK UDP 7777 (game), 7778 (peer) and 27015 (query) UDP
Satisfactory TCP and UDP 7777, TCP 8888 TCP and UDP

The rows come from the Minecraft Wiki's pages on server.properties and Bedrock Dedicated Server, a community wiki; the Official Terraria Wiki's Server page; the Factorio Wiki's Multiplayer page, which says "Factorio uses UDP only"; the Requirements page of Pocketpair's Palworld Server Guide; the ARK Official Community Wiki's Dedicated server setup; and the Official Satisfactory Wiki's Dedicated servers page. Each lets you change the default port. Terraria's page says its server "runs on TCP port 7777 by default", and its note for cloud providers says to open 7777 on TCP and UDP. Satisfactory's says "Port redirection is not supported at the moment for the standard port", so a relay there keeps that port's number, 7777 by default, the same on both sides. Its reliable messaging port, TCP 8888 by default, "can be remapped" with the -ExternalReliablePort= command-line parameter, the same page says.

At the tested relay's address, one UDP packet, the discovery ping a Bedrock client sends to find servers, got no answer within 5 s on port 25565. The same packet to port 25566, which nothing serves, got the same silence, while a server-list ping over TCP on 25565 answered in the same minute. On the game server, Docker publishes 25565 for TCP only.

Free cloud VMs come with terms to check first

The relay needs a VM with a public IPv4 address that stays up while people play. Several cloud providers publish free offers: the AWS Free Tier, Azure's free services, Google Cloud's free features and the Oracle Cloud Free Tier (pages checked October 2026). Free offers change, so read the provider's current page before you build on one. Look for:

  • whether the VM stays free with no end date, or only for a trial period;
  • whether the free VM includes a public IPv4 address, and whether that address is billed separately;
  • how much outbound data is free each month;
  • whether the provider can reclaim a VM it considers idle;
  • what signing up needs, such as a payment card.

A relay sends every player's traffic through the VM, so its outbound data grows with players and play time.

Build it with one open port, one tailnet rule and the game's own login

These steps use Minecraft Java Edition. For another TCP game, use its port from the table above.

  1. At home, run the game server in Docker with the game's own login checks on. The itzg/minecraft-server image takes its settings as environment variables, per its documentation (checked October 2026):

    scrolls sideways
    docker run -d -it --name mc -p 25565:25565 \
      -e EULA=TRUE -e ONLINE_MODE=TRUE \
      -e WHITELIST=your-friend-1,your-friend-2 \
      -e ENFORCE_WHITELIST=true \
      -v "$PWD/data:/data" itzg/minecraft-server
    • -p 25565:25565 publishes the port for TCP. Docker's page on port publishing maps a port to TCP unless it ends in /udp (checked October 2026).
    • EULA=TRUE accepts Minecraft's end-user license agreement, which the image's documentation says to "always include".
    • ONLINE_MODE=TRUE keeps online-mode on, which the Minecraft Wiki describes as "Whether to only allow players verified with the Minecraft account database to join". It is on by default.
    • WHITELIST turns the whitelist on and lists who may join. Put your players' Java Edition profile names in place of your-friend-1,your-friend-2; the image's documentation says to use those, "not Xbox gamertags".
    • ENFORCE_WHITELIST=true sets enforce-whitelist. With it on, the Minecraft Wiki says, "players not present on the whitelist get kicked from the server after the server reloads the whitelist file".
    • -v "$PWD/data:/data" keeps the world in a data folder where you run the command, since the image stores the server's data in the container's /data.

    Other games have their own join checks, such as the server password in ARK's start command, on the same wiki page as its ports.

  2. Get a cloud VM with a public IPv4 address (what to check in a free offer). The relay needs only Tailscale's client and socat on it.

  3. Join the game server's machine and the relay VM to one tailnet. With Headscale, which this page doesn't set up, each machine runs tailscale up --login-server with your Headscale server's address, and you approve it on the Headscale server (Headscale's registration methods, checked October 2026). The guide to leaving a wired ISP covers what a tailnet reaches on its own.

  4. Allow the relay VM to reach the game port and nothing else. Headscale's policy page says that by default "no policy is loaded which means that Headscale allows all traffic between nodes", that it reads the file at the policy.path setting of its configuration, and that it "needs to be reloaded to pick up changes" (checked October 2026). This policy file allows one thing: the relay VM opening TCP connections to port 25565 on the game server. On Headscale 0.29.7, in October 2026, this policy loaded, headscale nodes tag applied both tags and refused a tag the policy doesn't define, and the rules Headscale built from it allowed only TCP to port 25565 on the game server, from the relay; no client was connected for that check.

    scrolls sideways
    {
      "tagOwners": {
        "tag:game-relay": [],
        "tag:game-server": []
      },
      "acls": [
        {
          "action": "accept",
          "src": ["tag:game-relay"],
          "proto": "tcp",
          "dst": ["tag:game-server:25565"]
        }
      ]
    }

    Tailscale's policy syntax reference, whose file format Headscale uses, says access rules "deny access by default". So once this policy loads, anything it doesn't allow is blocked: if other devices on your tailnet need to reach each other, add rules for them. Without "proto": "tcp", the same reference says, a rule "applies to all TCP and UDP traffic". In Tailscale's syntax, an empty list under tagOwners is shorthand for the tailnet's admins; on Headscale, the administrator sets tags with the headscale nodes tag command in the next step. Headscale's policy page recommends grants, Tailscale's newer rule syntax, and supports rules like this one too.

  5. Tag the two machines on the Headscale server, with the IDs that headscale nodes list shows for them in place of 1 and 2, as Headscale's page on tags describes (checked October 2026):

    scrolls sideways
    headscale nodes list
    headscale nodes tag -i 1 -t tag:game-server
    headscale nodes tag -i 2 -t tag:game-relay
  6. On the relay VM, start the relay. game-server stands for the game server's name or address on the tailnet:

    scrolls sideways
    socat TCP4-LISTEN:25565,fork,reuseaddr \
      TCP4:game-server:25565

    socat's manual calls a TCP4-LISTEN address with a TCP4 address "a simple TCP port forwarder". With fork, socat "handles its channel in a child process" for each connection, so several players can connect at once. reuseaddr "Allows other sockets to bind to an address even if parts of it (e.g. the local port) are already in use by socat" (checked October 2026). Run the command under the VM's service manager so it starts again after a reboot.

  7. Open one port. In the provider's firewall for the VM, and in the VM's own firewall if it runs one, allow TCP 25565 from anywhere. Keep every other port closed, apart from the one you log in to the VM through if you log in over the internet.

  8. Give players the relay VM's public IPv4 address, such as 203.0.113.10 in this page's examples, or a DNS name you point at it, such as mc.example.com.

Three ways to relay a UDP game, from their documentation

Limitation

None of the methods in this section was tested here. Each comes from its project's own documentation, checked October 2026.

UDP has no connection for a relay to accept. socat's manual says that with UDP "no real connection is established; data has to arrive from the peer first", and that "no end-of-file condition is specified in UDP". So a UDP relay keeps track of each player and has to drop that player after a time with no data.

In each method below, the game server publishes its port for UDP (-p 34197:34197/udp in Docker), and the policy rule takes "proto": "udp" and the game's port. Factorio's port, 34197, is the example.

socat in UDP mode

On the relay VM:

scrolls sideways
socat -T 60 UDP4-LISTEN:34197,fork,reuseaddr \
  UDP4:game-server:34197

-T is socat's "Total inactivity timeout", which the manual calls "Useful with protocols like UDP that cannot transfer EOF". Here it ends a player's child process after 60 seconds with no data. Up to socat 1.8.0.0, -T 0 meant no timeout; since 1.8.0.1 it means 0, the manual says, so give it a number.

Destination NAT in the VM's kernel

The nftables wiki's NAT page shows the two rules this takes: a dnat to rule in a prerouting chain of type nat, which sends the game port's packets on to the game server's tailnet address, and masquerade in a postrouting chain, where "the source address is automagically set to the address of the output interface", here the VM's tailnet address, so the game server answers the VM. The kernel must also forward packets between its interfaces, which is the setting net.ipv4.ip_forward: "Forward Packets between interfaces", in the kernel's documentation.

nginx's stream module

In nginx's stream core module, listen 34197 udp reuseport; in a server block opens a socket "for working with datagrams"; the page says reuseport "should also be specified" so that packets from one address and port stay in one session. The stream proxy module passes them on with proxy_pass to the game server, and proxy_timeout, 10 minutes by default, closes a session when "no data is transmitted within this time". The core module's page says it "is not built by default" and needs the --with-stream build option, so check that your nginx has it.

Bedrock Edition can need more than one port. The Minecraft Wiki's copy of the server.properties file that ships with Bedrock Dedicated Server 1.26.60.6 sets transport=nethernet and server-udp-ports=19132, a setting that "Configures UDP client transport ports": that is a "Single UDP port shared by all clients". If it isn't set, the file says, "UDP ports are allocated from the operating system's ephemeral port range", and a relay would have to carry those ports too. With that transport, the file's IPv6 port setting is ignored and IPv6 uses 19132 as well. This page hasn't tried Bedrock through a relay.

Managed tunnels skip the VM, from their own pages

None of these was tested here. Each one runs the public end for you, so there's no VM to set up or patch; in return, their network carries your players' traffic. All pages were checked October 2026.

playit.gg calls itself "a global proxy that lets you host a server without port forwarding", with an agent on your machine: "Only you run the agent, your players just connect." Its home page names a free tier and a paid Premium plan; its game list puts Minecraft Java and Bedrock, Palworld, Terraria, Factorio and a plain "UDP" entry under "Free", and plain "TCP" and "TCP+UDP" entries, with some other games, under "Requires Playit Premium".

ngrok's pricing page says that on its Free plan "TCP endpoints also need a verified credit card, and ngrok doesn't charge it".

Tailscale Funnel "can only listen on ports 443, 8443, and 10000", and "only works over TLS-encrypted connections". Minecraft Java Edition's default port, TCP 25565, isn't one of those ports. On Headscale's features page, Funnel is one of the items without a tick.

Cloudflare Tunnel needs Cloudflare's client on each player's machine for anything other than a web service, as the guide to leaving a wired ISP quotes. Cloudflare Spectrum's page says "Custom TCP/UDP applications require an Enterprise plan with Spectrum as a paid add-on" (last updated April 2026). Its protocols per plan table (last updated August 2026) lists Minecraft, one app, on the Pro and Business plans too, where Spectrum is a paid add-on.

A mesh VPN that every player joins needs no relay at all, but each player installs the client and joins your tailnet (what a mesh VPN reaches).

Not tested here yet

  • A player on another network. Every probe ran from the same home connection the game server uses.
  • Real play. No probe logged in or played, so the round trip the game shows, several players at once and a whole session are untested.
  • UDP through any relay, Bedrock Edition and every game but Minecraft Java Edition.
  • The relay VM's side. Whether its path home ran direct or through a DERP relay server, its load, its firewall settings and whether the tailnet policy keeps it from reaching anything else at home weren't checked from the VM.
  • What the game server logs for a player who came through the relay. The log's player lines came from earlier logins whose path isn't known.
  • A VM in another region, other days and times, the managed tunnels, and running the game on the VM itself.

See how the home tests fit together for a homelab.

Promo: $100 off Starlink with my referral link

If you've never been a Starlink customer and you order an eligible Residential or Roam plan through my link, we both get Starlink's reward for your country. In the US, that is a $100 credit on your second monthly bill, and I get $100 too. Use my referral link

Sources

  1. What IP address does Starlink provide?, Starlink, accessed
  2. How do I set my IP address to a public IP?, Starlink, accessed
  3. Connection types, Tailscale, accessed
  4. Syntax reference for the tailnet policy file, Tailscale, accessed
  5. Policy, Headscale, accessed
  6. Tags, Headscale, accessed
  7. Registration methods, Headscale, accessed
  8. Features, Headscale, accessed
  9. socat manual, dest-unreach.org, accessed
  10. Port publishing and mapping, Docker, accessed
  11. Minecraft Server on Docker (Java Edition), Intro, itzg/docker-minecraft-server, accessed
  12. Minecraft Server on Docker (Java Edition), Server properties, itzg/docker-minecraft-server, accessed
  13. server.properties, Minecraft Wiki, accessed
  14. Bedrock Dedicated Server, Minecraft Wiki, accessed
  15. Server, Official Terraria Wiki, accessed
  16. Multiplayer, Factorio Wiki, accessed
  17. Requirements, Palworld Server Guide (Pocketpair), accessed
  18. Dedicated server setup, ARK Official Community Wiki, accessed
  19. Dedicated servers, Official Satisfactory Wiki, accessed
  20. Performing Network Address Translation (NAT), nftables wiki, accessed
  21. IP Sysctl, Linux kernel documentation, accessed
  22. Module ngx_stream_core_module, nginx, accessed
  23. Module ngx_stream_proxy_module, nginx, accessed
  24. AWS Free Tier, Amazon Web Services, accessed
  25. Explore Free Azure Services, Microsoft Azure, accessed
  26. Free Google Cloud features and trial offer, Google Cloud, accessed
  27. Oracle Cloud Free Tier, Oracle, accessed
  28. Make Your Game Server Public, playit.gg, accessed
  29. ngrok Pricing, ngrok, accessed
  30. Tailscale Funnel, Tailscale, accessed
  31. Cloudflare Spectrum, Cloudflare, accessed
  32. Protocols per plan, Cloudflare, accessed
  33. Does Starlink have a referral program?, Starlink, accessed
  34. Referral Program Terms (US), section 4.1, Starlink, accessed

Retest log

  1. Test Three sittings from a wired computer on the home network, over the public internet through the relay VM, with controls; one UDP packet to the relay's game port.
  • Can you host a server on Starlink without a public IPv4 address?

    Not directly over IPv4: without a public IPv4 address, connections from the internet arrive at your provider's NAT, and a port forward on your own router doesn't reach it.

  • Leaving a wired ISP: does Starlink give you a public IP address?

    For IPv4, only as an option on Priority plans: Starlink's help pages say its default IPv4 configuration uses carrier-grade NAT (CGNAT) and "does not allow inbound traffic", that public IPv4 is "an optional configuration available to Local and Global Priority customers", and that "truly static IPs are not available" (October 2026).

  • Is Starlink good for a homelab? A verdict from five tests

    On the one home Starlink connection tested, packets of 1500 bytes crossed over IPv4 and IPv6 in October 2026, and round trips over IPv4 spiked at four set seconds each minute in one of two one-hour tests, with 901 of 143,462 pings lost over both; the ways around carrier-grade NAT (CGNAT), such as a tunnel that starts inside your network, aren't tested here.