steezeburger.com

Rathole Tunnel: Securely and Cheaply Expose Local Palworld Server

Why?

The biggest reason I needed to run a reverse tunnel was to get around NAT issues, but there are several benefits:

How?

  • local Palworld server
  • Rathole tunnel
    • Rathole is a reverse tunneling tool that allows you to easily expose local services to the internet. It’s written in Rust and very fast. It’s similar to localtunnel or ngrok, but it’s open source and you run your own server.
    • Rathole server running on a remote VPS
      • I went with the cheapest DigitalOcean droplet, ~$4/month
      • My goal was to get a publicly accesible box up quickly. You could probably run Rathole on a Raspberry Pi, and this could be a good solution if you already have an rpi running at home and exposed publicly to the internet.
    • Rathole client running on my local machine

Final Solution

Rathole Server

  • Create new droplet on Digital Ocean (DO)

    I am using Digital Ocean because I am familiar with it, but any cheap VPS should work. We’re only running a tunnel on this box, not the game server itself.

    • Create a new project if you do not already have a DO project
    • Create a new droplet
      • Choose a region that is a decent middle ground between you and your friends or expected server members.
      • Leave the default datacenter (unless you have other plans for this droplet and need specific features that are only available in certain datacenters)
      • I always choose the most recent LTS (long term support) version of Ubuntu, “22.04 (LTS) x64” at time of writing.
      • Droplet type should be “shared cpu”
      • CPU options > Regular, Disk Type: SSD
      • Then choose the cheapest, $4/month option
      • No need to add a volume
      • Add your ssh key
      • Can change hostname to whatever you like, I went with rathole
      • Can use any tags you want, e.g. rathole, reverse-tunnel, palworld, etc
  • Once the droplet has finished provisioning, ssh into it. The droplet IP will be shown on the droplets dashboard on the DO website
    • ssh root@<droplet-ip> - you’ll be authenticated via the ssh key you added to the droplet
    • generally a good idea to upgrade the system after first login
      • sudo apt update && sudo apt upgrade -y
      • probably want to reboot after sudo reboot (note: you’ll lose connection and need to ssh back in!)
  • Install Docker
    • Some linux distributions come with unofficial docker packages, but it’s recommended to install from the official docker repository
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      # add Docker's official GPG key:
      sudo apt-get update
      sudo apt-get install ca-certificates curl
      sudo install -m 0755 -d /etc/apt/keyrings
      sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
      sudo chmod a+r /etc/apt/keyrings/docker.asc

      # Add the repository to Apt sources:
      echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
      sudo apt-get update

      # Install docker and related packages
      sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  • Create a project directory and necessary files
    • Create directory and files
      1
      2
      3
      4
      5
      mkdir ~/rathole-palworld
      # docker-compose configuration
      touch ~/rathole-palworld/docker-compose.yaml
      # rathole server configuration
      touch ~/rathole-palworld/server.toml
    • Populate docker-compose.yaml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      # docker-compose.yaml
      services:
      palworld-rathole-server:
      restart: unless-stopped
      container_name: palworld-rathole-server
      image: rapiz1/rathole
      command: ["--server", "/app/server.toml"]
      ports:
      - 2333:2333 # for rathole communication
      - 8211:8211/udp # for palworld communication
      - 27015:27015/udp # for steam client communication
      volumes:
      - ./server.toml:/app/server.toml
    • Populate server.toml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      # server.toml
      [server]
      bind_addr = "0.0.0.0:2333" # `2333` specifies the port that rathole listens for clients
      default_token = "use_a_secret_that_only_you_know"

      [server.services.palworld]
      type = "udp"
      bind_addr = "0.0.0.0:8211"
      nodelay = true

      [server.services.palworld2]
      type = "udp"
      bind_addr = "0.0.0.0:27015"
      nodelay = true
  • Run Rathole server via docker compose
    1
    2
    # starts the rathole server in the foreground
    docker compose up
    • Helpful docker-compose commands
      • docker compose up -d - start the server in the background
      • docker compose down - stop the server
      • docker compose logs -f - view the server logs
      • docker compose logs -f palworld-rathole-server - view the server logs by container name

Rathole Client

NOTE: We are now going to be running a client on the same machine as the game server! So you’ll be running commands in a WSL/Ubuntu terminal session that’s running on your local machine now.

  • Create directory and files for Rathole client
    1
    2
    3
    4
    5
     mkdir ~/rathole-palworld
    # docker-compose configuration
    touch ~/rathole-palworld/docker-compose.yaml
    # rathole server configuration
    touch ~/rathole-palworld/client.toml
  • Populate docker-compose.yaml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    services:
    palworld-rathole-client:
    restart: unless-stopped
    container_name: palworld-rathole-client
    image: rapiz1/rathole
    command: ["--client", "/app/client.toml"]
    network_mode: host
    volumes:
    - ./client.toml:/app/client.toml
  • Populate client.toml - make sure to replace your.digital.ocean.ip with the IP of your droplet! And the default_token needs to match the default_token in your Rathole server’s server.toml

    NOTE: 2024/2/16 - Added client.transport and client.transport.tcp sections. This helped A LOT with some users’ connection issues. There is some issue with the udp packets not being received correctly and causing the tunnel to terminate, but this setting mostly fixed the issue.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # client.toml
    [client]
    remote_addr = "your.digital.ocean.ip:2333" # The address of the server. The port must be the same with the port in `server.bind_addr`
    default_token = "use_a_secret_that_only_you_know"
    retry_interval = 1

    [client.transport] # The whole block is optional. Specify which transport to use
    type = "tcp" # Optional. Possible values: ["tcp", "tls", "noise"]. Default: "tcp"

    [client.transport.tcp]
    keepalive_secs = 5 # Optional. Specify `tcp_keepalive_time` in `tcp(7)`, if applicable. Default: 20 seconds
    keepalive_interval = 2 # Optional. Specify `tcp_keepalive_intvl` in `tcp(7)`, if applicable. Default: 8 seconds

    [client.services.palworld]
    local_addr = "127.0.0.1:8211"
    type = "udp"
    nodelay = true

    [client.services.palworld2]
    local_addr = "127.0.0.1:27015"
    type = "udp"
    nodelay = true
  • Run Rathole client via docker compose
    1
    2
    # starts the rathole client in the foreground
    docker compose up
  • Test connection
    • You should now be able to connect to your Palworld server using the IP of your DigitalOcean droplet. In the Palworld mulitplayer server page, you will input something that looks like 123.123.123.90:8211, where the left side of the colon is the IP address of your DigitalOcean droplet.

Final Notes

This solution works well to get around NAT issues and expose a local game server to the internet without exposing anything else. It’s more secure than port forwarding or DMZ, and it’s still quite cheap. It’s been working well for my friends and I, and I hope it works well for you. I created a Github repository to help you get started with this solution. You can find it here.

You can leave an issue on the repo, or you can find out how to contact me on my Github profile if you need any help or have any questions! You can also leave a comment on this blog post if you are logged into Github.

Thanks for taking the time to read this post, and I hope it helps you out!


Palworld things I’m working on: