How to expose service behind nat with wireguard and vps

Scenario: you have one (or more) services running at your home which you would like to expose on the internet. Sadly, you are also behind a dynamic dns and your ip can change in any moment.

Sure, you can use one of public dynamic dns providers such as no-ip or zoneedit, but what if you don’t want to depend on dns caching from your client and would like to reduce any downtime as much as possible?

Luckily, there is a quick and easy fix to this, we can leverage vps with static address and route the traffic through it.

Premise and assumptions

  • In this tutorial I am going to use Scaleways stardust vps (1.825 euro a month) as the endpoint.
  • Os used is Ubuntu 20.04 LTS
  • We are using root account (if you are not, use sudo)
  • Network interface on vps is called ens2
  • We are going to be forwarding tcp port 10250

Install wireguard

Installation is pretty straightforward

apt install wireguard
apt install wireguard-tools

Initial settings

export NET_PREFIX=192.168.50

as you can see, we will be using for our wireguard network, and the SERVER_ENDPOINT is the public ip address of our vps.


In order to accept wireguard connections you need to open udp port 51820 on your cloud provider firewall


Generate keys

In order to establish a connection between server and client we will need 2 sets of private and public keys.

export PRIVATE_CLIENT_KEY=$(wg genkey)
export PUBLIC_CLIENT_KEY=$(echo $PRIVATE_CLIENT_KEY | wg pubkey)
export PRIVATE_KEY=$(wg genkey)
export PUBLIC_KEY=$(echo $PRIVATE_KEY | wg pubkey)

In this tutorial I am not saving them in separate file, you can do it if you want it (i.e. echo $PRIVATE_CLIENT_KEY > private.key)

Server configuration

cat > /etc/wireguard/wg0.conf <<EOF
Address = ${NET_PREFIX}.1/24
ListenPort = 51820
PrivateKey = $PRIVATE_KEY
# packet forwarding
PreUp = sysctl -w net.ipv4.ip_forward=1

# masquerading
PostUp =   iptables -t nat -A POSTROUTING ! -s ${NET_PREFIX}.0/24 -o wg0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING ! -s ${NET_PREFIX}.0/24 -o wg0 -j MASQUERADE

# port forwarding 10250
PreUp =    iptables -t nat -A PREROUTING -i ens2 -p tcp --dport 10250 -j DNAT --to-destination ${NET_PREFIX}.2
PostDown = iptables -t nat -D PREROUTING -i ens2 -p tcp --dport 10250 -j DNAT --to-destination ${NET_PREFIX}.2

# VPN client's public key
# VPN client's IP address in the VPN
AllowedIPs = ${NET_PREFIX}.2/32

Enable & start service

sudo systemctl start [email protected]
sudo systemctl enable [email protected]

Client configuration

On vps run this command:

cat << EOF

# The address your computer will use on the VPN
Address = ${NET_PREFIX}.2/32
# Also ping the vpn server to ensure the tunnel is initialized
PostUp = ping -c1 ${NET_PREFIX}.1

PublicKey = ${PUBLIC_KEY}
Endpoint = ${SERVER_ENDPOINT}:51820

# allow access anywhere in the world
AllowedIPs = ${NET_PREFIX}.0/24

Copy generated content and save it in /etc/wireguard/wg0.conf on client side.

Start the service

sudo systemctl start [email protected]
sudo systemctl enable [email protected]


you can see if wireguard is working by running wg show, it should show you something like this

interface: wg0
  public key: 4/xxxxxxxxxxxxxxxxxxxxxxxxxx=
  private key: (hidden)
  listening port: 51820

peer: xxxxxxxxxxxxxxxxxxxxxxxxxx=
  endpoint: 90.xx.55.251:60906
  allowed ips:
  latest handshake: 14 seconds ago
  transfer: 10.94 KiB received, 1.26 MiB sent
  persistent keepalive: every 5 seconds

if not, double check (or recreate keys)

Enable logs

Normally wireguard doesn’t print any logs, in order to enable them run

modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control, they will appear in /var/log/syslog