RPoVPN
TIP
Reverse Proxy-over-VPN (RPoVPN)
When self‑hosting services such as game servers from your home IP address, it can be risky to open ports on your network and publicly advertise this address. One preferred solution is to configure a reverse proxy over VPN (RPoVPN). This configures a second server likely in the cloud as the gateway, acting as the front‑end IP address for all traffic entering the network.
In a basic text topology, the service setup should look like the following:
Home server <--> VPN Tunnel exchanging TCP traffic <--> Gateway (VPS)
There are a few ways this can be achieved; within this guide we will use Nginx Proxy Manager and Traefik.
Prerequisites
- A local server such as a game server. In this example we will be using port
25565. - VPS server, e.g. an Ubuntu VPS in same or different country with static IPv4 address and ability to open ports on their firewall.
- A VPN tunnel link - WireGuard is recommended as the most popular protocol with the lowest latency. This can be achieved with a tailnet including both devices: https://tailscale.com/
Configuring RPoVPN
!!! info Both clients are also able to reverse proxy web traffic (HTTP/HTTPS). However, for this example we will leave this out and focus only on forwarding TCP through the Nginx Stream module. They can also be configured to work alongside web traffic.
Below are the guides on how to forward TCP traffic over a pre-configured VPN tunnel.
Nginx Proxy Manager
Docker Compose
version: "3.8"
services:
app:
image: jc21/nginx-proxy-manager:latest
restart: unless-stopped
ports:
# These ports are in format <host-port>:<container-port>
# - 80:80 # Public HTTP Port only - enable for HTTP(s) traffic
# - 443:443 # Public HTTPS Port - enable for HTTP(s) traffic
- 81:81 # Admin Web Port
# All ports below that need to be opened
- 25565:25565 # Minecraft TCP stream
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
proxy:
external: trueOnce configured, run docker compose up -d to start the container.
Once NPM is set up and active, the Streams section can be found under the Proxy header. Here you can create a new Stream, setting the incoming port and forwarding host.
Set the Host to the IPv4 address of the home device at the other end of the VPN, alongside the port of the service that the home device is exposing. In this instance 25565 is active on my home network.
Once enabled, you should now be able to access the public address of the gateway (VPS) server and port. All traffic will be routed through this public IP address keeping your home network address safe while also preventing the need to open the ports on your router's firewall.
Traefik
Docker Compose
version: "3"
services:
traefik:
container_name: traefik
image: traefik:latest
ports:
# - 80:80 - Disabled as we are not forwarding web traffic from this host
# - 443:443
- 25565:25565 # open Minecraft port
volumes:
- ./files:/etc/traefik/
restart: unless-stopped
networks:
proxy:
external: trueOnce Traefik is configured, we will need to manually create two files.
First create a traefik.yml file located in the same folder as the Compose YAML.
touch traefik.ymlSecondly, create a rules.yml (this can be anything you like, as long as it is referenced in the traefik.yml file).
touch rules.ymlEdit the following files to be configured as shown below:
traefik.yml
# files/traefik.yml
entryPoints:
minecraft:
address: ":25565"
# If you wanted another forward, below would be the format
# minecraft2:
# address: ":25566"
serversTransport:
insecureSkipVerify: true
providers:
file:
filename: /etc/traefik/config.yml # rules.yml file reference
watch: trueconfig.yml
# files/config.yml
tcp:
routers:
minecraft_router:
entryPoints:
- minecraft
rule: "HostSNI(`*`)"
service: minecraft_service
# If you wanted another forward, below would be the format
# minecraft2_router:
# entryPoints:
# - minecraft2
# rule: "HostSNI(`*`)"
# service: minecraft2_service
services:
minecraft_service:
loadBalancer:
servers:
- address: "100.64.0.1:25565"
# If you wanted another forward, below would be the format
# minecraft2_service:
# loadBalancer:
# servers:
# - address: "100.64.0.1:25566"