Basalt
Guides

Tunnels

Give your servers a public address without port forwarding.

The classic self-hosting wall: the server runs great, but your friends can't join because you're behind NAT, CGNAT, or a router you're not allowed to touch. Basalt tunnels knock that wall down by having each instance dial outbound to a tunnel server you control, which then accepts players on a public port and forwards them back.

Basalt's tunnel provider is frp. Unlike a hosted tunnel service, nothing about it is operated by Basalt: you run the tunnel server yourself, on a machine with a public IP, and point Basalt at it. That machine is the only piece that needs to be publicly reachable; your game nodes stay behind whatever NAT they're behind.

You need a tunnel server first

This guide covers using tunnels. Standing up the frp server they connect to is a one-time setup covered in Run a tunnel server.

Configure the panel once

In Settings → Tunneling, fill in the values from your tunnel server:

FieldWhat it is
frps server addressPublic IP or hostname of the machine running the tunnel server.
frps server portIts control port (7000 by default). Nodes dial this, players never do.
frp tokenThe shared secret from the server's .env. Must match byte for byte.
Wildcard domainOptional, for future HTTP routing. Not used by game-server tunnels.
Remote port min / maxThe public port range. Must match the server's range exactly.

Saving runs a DNS check on the wildcard domain if you set one, so a typo surfaces immediately instead of at the first tunnel.

Enable a tunnel on an instance

  1. Open the instance's Tunnel tab.
  2. Optionally set the port to tunnel. Left empty, it uses the instance's first port binding.
  3. Enable the tunnel.

That's the whole flow. There is no claim link, no third-party dashboard, and no manual step on another site: Basalt writes the proxy configuration itself, allocates a free public port from your range, and starts a small agent container alongside the game server. Within a tick or two the tab shows the public address.

Start the instance first

The agent shares the game container's network namespace, so the container has to exist. Enabling a tunnel on a stopped instance leaves it retrying until you start it, then it comes up on its own.

Reading the tab

The tab shows two independent things, and the difference matters when something is wrong.

Status is the tunnel's lifecycle: Provisioning while it is being set up, Active once the agent has registered its proxy with your tunnel server, Offline when the node is unreachable, and Error only for a problem that will not fix itself.

Reachable is separate: it means Basalt opened a connection to the public address from the outside and something answered. An Active tunnel that is not reachable is registered but not actually serving, which almost always means the port range is not open on the tunnel server's firewall.

UDP tunnels stay unconfirmed

A UDP connect() succeeds even against a closed port, so it can't be used as a reachability signal. UDP tunnels therefore show as unconfirmed by design; the proxy is registered and forwarding normally.

When something is wrong

The tab names the specific cause rather than making you read logs:

It saysWhat to do
frp isn't fully configuredA required field in Settings → Tunneling is missing. Fill it in; the tunnel picks itself up, no need to re-enable.
frps rejected the tokenThe token in the panel and on the tunnel server disagree. Fix it and re-enable the tunnel.
Waiting on frpsThe agent can't reach your tunnel server, or hasn't finished registering. Check the firewall on the control port. Also normal for a few seconds after enabling.
No free port leftEvery port in your range is taken. Widen the range on both sides.
Port taken by another tunnelTwo tunnels raced for the same port. Nothing to do; the loser takes a different one on its next attempt.
The node isn't respondingBasalt can't reach that node's daemon. The tunnel resumes when the node is back.

After you fix a configuration problem, the tunnel recovers on its own. It can take up to five minutes, because repeated failures back off progressively; nothing is stuck.

Giving it a domain

Players connect to <your tunnel server>:<allocated port>. To hand out something friendlier, point DNS at the tunnel server:

  • An A record (play.example.com) so players type a name instead of an IP.
  • An SRV record for games that support it. For Minecraft, _minecraft._tcp.play.example.com pointing at the tunnel server and the allocated port lets players connect with just play.example.com, no port at all. This pairs well with automatic port allocation, since the port lives in DNS rather than in your players' memory.

Proxied DNS breaks game traffic

If your DNS provider offers an HTTP proxy (Cloudflare's orange cloud, for instance), leave it off for these records. Those proxies carry HTTP and HTTPS, not the raw TCP or UDP a game server speaks.

When to use what

SituationRecommendation
Friends on your LANDirect connection to the node's local address.
You control the routerPort forwarding gives the most direct route.
CGNAT, dorms, offices, rented boxes without extra IPsA tunnel.

On this page