Many users face a common problem: their ISP assigns a dynamic IP or uses CGNAT, making it impossible to accept incoming connections. Renting a static IP costs money, and sometimes it's not even available. The solution is to set up a WireGuard VPN server on a VPS with port forwarding. In this article, we'll cover how to configure such a server and connect clients to it.

Why You Need WireGuard with Port Forwarding

There are many scenarios where you need to accept incoming connections to a device behind your ISP's NAT:

  • Game servers — hosting Minecraft, CS2, Valheim, and other games on your home PC
  • Remote access — connecting to your home computer via RDP, SSH, or VNC
  • Web development — demonstrating local projects to clients
  • IoT and smart home — accessing cameras, controllers, and sensors from outside
  • File servers — FTP, SFTP, Nextcloud on your home NAS
  • Mobile proxies — setting up incoming connections to proxy servers on modems

WireGuard is ideal for this task thanks to its high performance, minimal latency, and ease of configuration. Unlike OpenVPN, WireGuard operates at the Linux kernel level and provides throughput close to native.

How It Works

The solution architecture looks as follows:

WireGuard port forwarding diagram

When an external client connects to the VPS IP address on a port in the 64000–64199 range, the server forwards the traffic through the WireGuard tunnel to your device (10.8.0.2). Your application receives the connection as if the client connected directly.

Requirements

For the Server (VPS)

  • VPS with a public IP address (a minimal plan from $3-5/month will work)
  • Ubuntu 20.04/22.04/24.04 or Debian 11/12
  • Root access
  • Open UDP port 51820 (for WireGuard)
  • Open TCP/UDP ports for forwarding (in our example: 64000–64399)

For the Client

  • Any OS with WireGuard support: Windows, Linux, macOS, Android, iOS
  • WireGuard client installed

Installing WireGuard on the Server

Connect to the server via SSH and run the following commands:

1. Update the System

apt update && apt upgrade -y

2. Install WireGuard

apt install wireguard wireguard-tools -y

3. Enable IP Forwarding

This is a critical step — without it, port forwarding won't work:

# Enable immediately
echo 1 > /proc/sys/net/ipv4/ip_forward
# Make it persistent after reboot
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p

4. Generate Server Keys

# Create the config directory (if it doesn't exist)
mkdir -p /etc/wireguard
cd /etc/wireguard
# Generate server private and public keys
wg genkey | tee server_private.key | wg pubkey > server_public.key
# Set secure permissions
chmod 600 server_private.key
# View the keys
echo "Server private key:"
cat server_private.key
echo "Server public key:"
cat server_public.key

Important: save the server's public key — you'll need it for client configuration.

5. Generate Client Keys

# Keys for client 1
wg genkey | tee client1_private.key | wg pubkey > client1_public.key
# Keys for client 2
wg genkey | tee client2_private.key | wg pubkey > client2_public.key
# View
echo "=== Client 1 ==="
echo "Private: $(cat client1_private.key)"
echo "Public: $(cat client1_public.key)"
echo "=== Client 2 ==="
echo "Private: $(cat client2_private.key)"
echo "Public: $(cat client2_public.key)"

Server Configuration

Create the WireGuard configuration file:

nano /etc/wireguard/wg0.conf

Paste the following config, replacing the keys with your own:

[Interface]
# Server IP address in the VPN network
Address = 10.8.0.1/24
# Port for client connections
ListenPort = 51820
# Server private key (from server_private.key file)
PrivateKey = YOUR_SERVER_PRIVATE_KEY
# Don't overwrite config on shutdown
SaveConfig = false
# ============================================
# NAT for client internet access
# ============================================
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -A FORWARD -o wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -o wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# ============================================
# Port forwarding for client 1 (10.8.0.2)
# Range: 64000–64199 (200 ports)
# ============================================
# TCP
PostUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 64000:64199 -j DNAT --to-destination 10.8.0.2
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 64000:64199 -j DNAT --to-destination 10.8.0.2
# UDP
PostUp = iptables -t nat -A PREROUTING -i eth0 -p udp --dport 64000:64199 -j DNAT --to-destination 10.8.0.2
PostDown = iptables -t nat -D PREROUTING -i eth0 -p udp --dport 64000:64199 -j DNAT --to-destination 10.8.0.2
# ============================================
# Port forwarding for client 2 (10.8.0.3)
# Range: 64200–64399 (200 ports)
# ============================================
# TCP
PostUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 64200:64399 -j DNAT --to-destination 10.8.0.3
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 64200:64399 -j DNAT --to-destination 10.8.0.3
# UDP
PostUp = iptables -t nat -A PREROUTING -i eth0 -p udp --dport 64200:64399 -j DNAT --to-destination 10.8.0.3
PostDown = iptables -t nat -D PREROUTING -i eth0 -p udp --dport 64200:64399 -j DNAT --to-destination 10.8.0.3
# ============================================
# Clients
# ============================================
# Client 1
[Peer]
PublicKey = CLIENT_1_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32
# Client 2
[Peer]
PublicKey = CLIENT_2_PUBLIC_KEY
AllowedIPs = 10.8.0.3/32

⚠️ Important Notes on the Config:

  • eth0 — the network interface name. On your server, it might be different (ens3, enp0s3, etc.). Check with the ip a command
  • SaveConfig = false — must be set to false, otherwise WireGuard will overwrite the config on shutdown and iptables rules will be lost
  • -i eth0 in PREROUTING rules — specifies the incoming interface, improves security

Determining the Network Interface Name

# Find the interface with the external IP
ip route get 8.8.8.8 | grep -oP 'dev \K\S+'

If the command returns something other than eth0, replace eth0 in the config with the returned value.

Starting WireGuard

# Start WireGuard
wg-quick up wg0
# Enable auto-start on system boot
systemctl enable wg-quick@wg0
# Check status
wg show

You should see information about the wg0 interface and registered peers.

Client Configuration

On the client device, create a configuration file. For Windows and macOS, this is done through the WireGuard GUI application ("Add Tunnel" → "Add empty tunnel").

Config for Client 1 (receives ports 64000–64199)

[Interface]
# Client private key (from client1_private.key file)
PrivateKey = CLIENT_1_PRIVATE_KEY
# Client IP address in the VPN network
Address = 10.8.0.2/32
# DNS server (optional, can be removed)
DNS = 1.1.1.1
[Peer]
# Server public key (from server_public.key file)
PublicKey = SERVER_PUBLIC_KEY
# Route all traffic through VPN
AllowedIPs = 0.0.0.0/0, ::/0
# IP address and port of your VPS
Endpoint = YOUR_SERVER_IP:51820
# Keep connection alive (important for NAT)
PersistentKeepalive = 25

Config for Client 2 (receives ports 64200–64399)

[Interface]
PrivateKey = CLIENT_2_PRIVATE_KEY
Address = 10.8.0.3/32
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = YOUR_SERVER_IP:51820
PersistentKeepalive = 25

???? AllowedIPs Options

The AllowedIPs parameter determines which traffic goes through the VPN:

  • 0.0.0.0/0, ::/0 — all traffic (IPv4 and IPv6) through VPN. Your external IP will change to the server's IP.
  • 0.0.0.0/1, 128.0.0.0/1, ::/1, 8000::/1 — same thing, but without hijacking the default route (solves issues with some systems)
  • 10.8.0.0/24 — only traffic to the VPN network. Internet goes directly, but port forwarding still works

PersistentKeepalive — Why It's Needed

The PersistentKeepalive = 25 parameter makes the client send keepalive packets to the server every 25 seconds. This is necessary for two reasons:

  1. NAT traversal — home routers "forget" UDP connections after 30-120 seconds of inactivity. Keepalive maintains the mapping active.
  2. Incoming connections — the server needs to know the client's current endpoint to send traffic. Without keepalive, the server will "lose" the client after idle time.

Firewall Configuration

Make sure the necessary ports are open on the server:

For UFW (Ubuntu)

# WireGuard port
ufw allow 51820/udp
# Ports for forwarding (client 1)
ufw allow 64000:64199/tcp
ufw allow 64000:64199/udp
# Ports for forwarding (client 2)
ufw allow 64200:64399/tcp
ufw allow 64200:64399/udp
# Apply changes
ufw reload
# Check status
ufw status

For iptables (without UFW)

# WireGuard port
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
# Ports for forwarding
iptables -A INPUT -p tcp --dport 64000:64399 -j ACCEPT
iptables -A INPUT -p udp --dport 64000:64399 -j ACCEPT
# Save rules
apt install iptables-persistent -y
netfilter-persistent save

Checking on the Hosting Provider

Some VPS providers have an additional firewall in their control panel. Check the settings in your hosting dashboard and open ports 51820/udp and 64000–64399 tcp/udp there.

Testing

1. Checking Client Connection

After connecting the client, run on the server:

wg show

You should see information about the connected peer with the latest handshake:

interface: wg0
 public key: xxxxx
 private key: (hidden)
 listening port: 51820
peer: CLIENT_PUBLIC_KEY
 endpoint: CLIENT_IP:PORT
 allowed ips: 10.8.0.2/32
 latest handshake: 5 seconds ago
 transfer: 1.25 MiB received, 3.50 MiB sent

2. Testing Port Forwarding

On the client device, start a test server:

# Python 3 (simple HTTP server on port 64100)
python3 -m http.server 64100
# Or netcat for TCP
nc -l -p 64100

From another device (not connected to the VPN), try to connect:

# Via browser
http://YOUR_SERVER_IP:64100
# Or via curl
curl http://YOUR_SERVER_IP:64100
# Or netcat
nc YOUR_SERVER_IP 64100

3. Testing UDP

# On the client (listening for UDP)
nc -u -l -p 64100
# From an external device
echo "test" | nc -u YOUR_SERVER_IP 64100

4. Checking iptables Rules on the Server

# View NAT rules
iptables -t nat -L -n -v
# You should see DNAT rules for your port ranges

Troubleshooting

Client Won't Connect

  • Check if WireGuard is running on the server: systemctl status wg-quick@wg0
  • Check if port 51820/udp is open on the server firewall
  • Make sure the server's public key in the client config matches the key on the server
  • Verify the server IP address in Endpoint is correct

Connected but Port Forwarding Doesn't Work

  • Check IP forwarding: cat /proc/sys/net/ipv4/ip_forward (should be 1)
  • Check the network interface name in the config (eth0, ens3, etc.)
  • Make sure ports are open on the firewall
  • Check if the application on the client is listening on the required port

Connection Drops Periodically

  • Make sure PersistentKeepalive is set in the client config
  • Try reducing the value to 15-20 seconds

Slow Speed

  • Try changing MTU in the [Interface] section: MTU = 1420
  • Check CPU load on the server
  • Choose a VPS geographically closer to you

Viewing Logs

# WireGuard logs
dmesg | grep wireguard
# System logs
journalctl -u wg-quick@wg0 -f
# iptables debugging (DNAT logging)
iptables -t nat -I PREROUTING -p tcp --dport 64000:64199 -j LOG --log-prefix "DNAT: "
dmesg | grep DNAT

Scaling: Adding New Clients

To add a new client:

  1. Generate a new key pair
  2. Choose an available IP (10.8.0.4, 10.8.0.5, etc.)
  3. Choose an available port range (64400–64599, etc.)
  4. Add the [Peer] section and iptables rules to the server config
  5. Restart WireGuard: wg-quick down wg0 && wg-quick up wg0

Example of adding client 3:

# Add to /etc/wireguard/wg0.conf
# Port forwarding for client 3 (10.8.0.4)
PostUp = iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 64400:64599 -j DNAT --to-destination 10.8.0.4
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 64400:64599 -j DNAT --to-destination 10.8.0.4
PostUp = iptables -t nat -A PREROUTING -i eth0 -p udp --dport 64400:64599 -j DNAT --to-destination 10.8.0.4
PostDown = iptables -t nat -D PREROUTING -i eth0 -p udp --dport 64400:64599 -j DNAT --to-destination 10.8.0.4
# At the end of the file
[Peer]
PublicKey = CLIENT_3_PUBLIC_KEY
AllowedIPs = 10.8.0.4/32

Conclusion

WireGuard with port forwarding is an elegant solution for getting a "static IP" without having to pay your ISP or change your plan. A single inexpensive VPS can serve dozens of clients, each receiving their own set of ports for incoming connections.

Advantages of this approach:

  • Cost savings — a VPS for $3-5/month is cheaper than a static IP from most ISPs
  • Flexibility — you can quickly change port ranges and add clients
  • Security — all traffic is encrypted, real IP is hidden
  • Performance — WireGuard provides minimal overhead
  • Reliability — connection automatically recovers after disconnections

If you use mobile proxies from mobileproxy.space, this setup allows you to organize stable access to proxy servers running on modems behind the ISP's NAT.

Frequently Asked Questions

Can I forward port 80 or 443?

Yes, but many hosting providers block these ports by default. Also keep in mind that some ISPs filter incoming traffic on standard ports.

How many clients can connect to one server?

Theoretically — thousands. Practically, the limitation is determined by the server's bandwidth and the number of available ports (65535 minus system ports).

Does this work with IPv6?

Yes, but additional ip6tables configuration is required. In this article, we covered IPv4 only.

What if I have multiple network interfaces on the server?

Specify the specific interface in iptables rules using the -i parameter for PREROUTING and -o for POSTROUTING.