WireGuard Made Clear: Server IP, Peer IP, AllowedIPs, Split-Tunnel vs Full-Tunnel (with wg-easy & CLI)


What you usually want

  • Addressing plan (example):
    • VPN IPv4 subnet: 10.8.0.0/24 → server = 10.8.0.1, clients = 10.8.0.2+
    • VPN IPv6 subnet (optional): fdcc:ad94:bacf:61a4::/64
  • wg-easy “Server Allowed IPs” for each peer: the peer’s single address, e.g. 10.8.0.5/32 and fdcc:…:cafe:5/128.
  • Client “AllowedIPs”:
    • Split-tunnel (recommended): 10.8.0.0/24 (and fdcc:…/64 if using IPv6).
    • Full-tunnel: 0.0.0.0/0, ::/0 and enable IPv4/IPv6 forwarding + NAT on the server.
Windows can’t be pinged? Enable firewall ICMP echo-reply rules and mark the WireGuard network as Private.
Web dies after connecting? You routed IPv6 globally (::/0) without IPv6 NAT/forwarding or DNS/MTU is wrong. Use split-tunnel or fix NAT/MTU.

1) Concepts you must know (in plain English)

  • Server vs. Peer: In WireGuard every node is a peer. We call the machine that accepts inbound connections the server.
  • Interface Address (“Address”): The IP your peer owns inside the VPN.
    Example: server 10.8.0.1, Windows 10.8.0.5, Raspberry Pi 10.8.0.4.
  • AllowedIPs (client side): What destinations should be sent into the tunnel.
    • 10.8.0.0/24 → only reach other VPN members (split-tunnel).
    • 0.0.0.0/0, ::/0 → send everything via VPN (full-tunnel).
  • Server Allowed IPs (server side, wg-easy term): Which addresses belong to this specific peer.
    Use single-host routes /32 (IPv4) and /128 (IPv6), e.g. 10.8.0.5/32.
  • Endpoint: Where a peer dials to (public IP:port of server).
  • PersistentKeepalive: Send a small packet every N seconds (usually 25) to keep NAT bindings alive.
  • Split-tunnel vs Full-tunnel: Split only routes VPN subnets; full routes all traffic via server.
  • DNS: If you push a DNS server (like 10.8.0.1), that server must actually answer DNS queries.

IPv4 VPN: 10.8.0.0/24
  Server: 10.8.0.1
  Raspberry Pi: 10.8.0.4
  Windows: 10.8.0.5

IPv6 VPN (optional): fdcc:ad94:bacf:61a4::/64
  Server: fdcc:...::cafe:1
  Raspberry Pi: fdcc:...::cafe:4
  Windows: fdcc:...::cafe:5
Use your own fd** prefix if you like; it’s just Unique Local Addressing.

3) Server setup (two ways)

3.1 Using wg-easy (Docker)

wg-easy UI fields you’ll see when creating a peer (client):

  • Address (IPv4/IPv6): the peer’s interface IPs (e.g., 10.8.0.5 and fdcc:…::cafe:5).
  • Allowed IPs: what this peer will route into the tunnel (client-side routes distributed via the config).
    • Split-tunnel: 10.8.0.0/24 (+ fdcc:…/64 if using v6)
    • Full-tunnel: 0.0.0.0/0, ::/0
  • Server Allowed IPs: single-address routes that identify this peer on the server:
    10.8.0.5/32, fdcc:…::cafe:5/128
  • Advanced → PersistentKeepalive: 25
  • Global setting:Allow clients to communicate with each other” → ON if you want peer-to-peer traffic.
For each peer, “Server Allowed IPs” should always be that peer’s /32 and /128—not the whole subnet.

3.2 Using raw wg/wg-quick (CLI)

/etc/wireguard/wg0.conf (server)

[Interface]
Address = 10.8.0.1/24, fdcc:ad94:bacf:61a4::cafe:1/64
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
# Optional DNS for local clients if you run a resolver at 10.8.0.1
# PostUp/PostDown: NAT + firewall rules are added in Section 6

# Raspberry Pi
[Peer]
PublicKey = <PI_PUBLIC_KEY>
AllowedIPs = 10.8.0.4/32, fdcc:ad94:bacf:61a4::cafe:4/128
PersistentKeepalive = 25

# Windows laptop
[Peer]
PublicKey = <WIN_PUBLIC_KEY>
AllowedIPs = 10.8.0.5/32, fdcc:ad94:bacf:61a4::cafe:5/128
PersistentKeepalive = 25

4) Client configs

4.1 Windows (WireGuard GUI)

Split-tunnel (recommended):

[Interface]
PrivateKey = <WIN_PRIVATE_KEY>
Address = 10.8.0.5/24, fdcc:ad94:bacf:61a4::cafe:5/112
DNS = 1.1.1.1, 2606:4700:4700::1111         # or omit; see Section 5
MTU = 1420                                  # try 1280 if you have page loading issues

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = <SERVER_PUBLIC_IP>:51820
AllowedIPs = 10.8.0.0/24, fdcc:ad94:bacf:61a4::/64
PersistentKeepalive = 25

Full-tunnel:

AllowedIPs = 0.0.0.0/0, ::/0
If you choose full-tunnel, you must enable forwarding + NAT on the server (Section 6).

Make Windows ping-friendly (ICMP + Private profile)

Open Administrator PowerShell:

# Mark WireGuard network as Private
Get-NetConnectionProfile | ? {$_.InterfaceAlias -like "WireGuard*"} |
  Set-NetConnectionProfile -NetworkCategory Private

# Allow inbound ping (ICMP Echo) for IPv4 & IPv6
Get-NetFirewallRule -DisplayName "*Echo Request*" -Direction Inbound | Enable-NetFirewallRule
# (Or enable in "Advanced Security" under "File and Printer Sharing (Echo Request - ICMPv4-In)".)

4.2 Raspberry Pi / Linux client

/etc/wireguard/wg0.conf

[Interface]
PrivateKey = <PI_PRIVATE_KEY>
Address = 10.8.0.4/24, fdcc:ad94:bacf:61a4::cafe:4/112
DNS = 1.1.1.1                                # optional; see Section 5
MTU = 1420                                   # try 1280 if issues

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = <SERVER_PUBLIC_IP>:51820
AllowedIPs = 10.8.0.0/24, fdcc:ad94:bacf:61a4::/64
PersistentKeepalive = 25

Bring it up:

sudo apt update && sudo apt install -y wireguard resolvconf
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0

5) DNS: three safe patterns

  1. Use public DNS locally (simplest): set DNS = 1.1.1.1 (and/or 8.8.8.8).
  2. Push your own DNS from the VPN (e.g., 10.8.0.1) only if the server is actually running a resolver.
  3. For split-tunnel, you can omit DNS= entirely to keep your local resolver.

Symptom of bad DNS: websites don’t resolve (but pinging an IP works). Check nslookup example.com after connecting.


6) Full-tunnel requires forwarding + NAT on the server

If any client uses AllowedIPs = 0.0.0.0/0 (and/or ::/0), the server must behave like a router.

Enable kernel forwarding (Debian/Ubuntu):

sudo tee /etc/sysctl.d/99-wg.conf >/dev/null <<'EOF'
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
EOF
sudo sysctl --system

Add NAT (nftables). Replace eth0 with your server’s internet interface:

sudo nft add table inet nat
sudo nft 'add chain inet nat postrouting { type nat hook postrouting priority 100 ; }'
sudo nft add rule inet nat postrouting oif "eth0" ip saddr 10.8.0.0/24 masquerade
# If you really route IPv6 globally, also add:
sudo nft add rule inet nat postrouting oif "eth0" ip6 saddr fdcc:ad94:bacf:61a4::/64 masquerade
Many people break the web by enabling ::/0 on the client without IPv6 NAT/forwarding on the server. If you don’t need full-tunnel IPv6, prefer split-tunnel (fdcc:…/64) and skip IPv6 NAT.

7) MTU / MSS problems (why some pages hang)

If pages partially load or some sites fail, try lower MTU = 1280 on both client and server WG interfaces.
(You can also clamp MSS in your firewall, but MTU 1280 is the quick fix.)


8) End-to-end test checklist (copy-paste)

On each client after connecting:

# Linux/macOS:
wg show
ip a show wg0
ping -c 3 10.8.0.1            # ping server inside VPN
# Split-tunnel should keep your default route local:
ip route get 1.1.1.1
# (Windows) PowerShell:
#  tracert -4 1.1.1.1
#  tracert -6 2606:4700:4700::1111
#  nslookup aicargo.pro

If you expect full-tunnel:

curl -4 https://ifconfig.co
curl -6 https://ifconfig.co
# Should show the server’s public IPs.

Windows ↔ Raspberry Pi ping:

  • Ensure Windows ICMP inbound rules are enabled (Section 4.1).

From Raspberry Pi:

ping -c 3 10.8.0.5
traceroute 10.8.0.5

9) Common pitfalls (and quick fixes)

  • Can’t ping Windows: Windows firewall blocks ICMP or WG network is “Public”. → Set to Private + enable Echo Request rules.
  • VPN connects, web dies: You routed IPv6 globally (::/0) but server lacks IPv6 NAT/forwarding. → Use split-tunnel or add IPv6 NAT.
  • Handshake OK, no traffic: Server Allowed IPs not set to /32 (and /128) per peer, or “clients can talk to each other” is off.
  • Permission denied (SSH): Wrong user/key, or ~/.ssh permissions wrong (700 dir, 600 authorized_keys), or server sshd not listening on wg0.
  • Multiple peers use the same IP: Each peer must have a unique Address.
  • DNS broken: You pushed a VPN DNS that doesn’t answer. Use 1.1.1.1/8.8.8.8 or run a real resolver on the server.
  • MTU blackhole: Drop MTU to 1280.

10) Minimal reference configs

Server (wg-easy conceptually):

  • Peer “Windows”
    • Address: 10.8.0.5 (and fdcc:…::cafe:5)
    • Allowed IPs: 10.8.0.0/24 (+ fdcc:…/64)
    • Server Allowed IPs: 10.8.0.5/32 (+ fdcc:…::cafe:5/128)
    • PersistentKeepalive: 25
  • Peer “RaspberryPi”
    • Address: 10.8.0.4 (and fdcc:…::cafe:4)
    • Allowed IPs: 10.8.0.0/24 (+ fdcc:…/64)
    • Server Allowed IPs: 10.8.0.4/32 (+ fdcc:…::cafe:4/128)
    • PersistentKeepalive: 25
  • Global: “Clients can communicate” → ON

Windows (split-tunnel):

[Interface]
PrivateKey = <WIN_KEY>
Address = 10.8.0.5/24, fdcc:ad94:bacf:61a4::cafe:5/112
DNS = 1.1.1.1
MTU = 1420

[Peer]
PublicKey = <SERVER_KEY>
Endpoint = <SERVER_IP>:51820
AllowedIPs = 10.8.0.0/24, fdcc:ad94:bacf:61a4::/64
PersistentKeepalive = 25

Raspberry Pi (split-tunnel):

[Interface]
PrivateKey = <PI_KEY>
Address = 10.8.0.4/24, fdcc:ad94:bacf:61a4::cafe:4/112
DNS = 1.1.1.1
MTU = 1420

[Peer]
PublicKey = <SERVER_KEY>
Endpoint = <SERVER_IP>:51820
AllowedIPs = 10.8.0.0/24, fdcc:ad94:bacf:61a4::/64
PersistentKeepalive = 25

11) FAQ

Q: Which side sets what?

  • Client “AllowedIPs”: what the client sends into the tunnel.
  • Server “Server Allowed IPs”: which addresses identify that client (routing back).

Q: Do I need IPv6?
No. If you don’t need it, skip it entirely. If you use it, prefer split-tunnel (/64) unless you also configure IPv6 NAT/forwarding for full-tunnel.

Q: Why use /32 on the server for a peer?
So the server knows “packets for 10.8.0.5 must go to that peer.” Using a wide subnet there breaks routing between peers.

Q: DNS via VPN or not?
If your server doesn’t run a resolver, don’t push DNS=10.8.0.1. Use public resolvers or run Unbound/AdGuard on the server.


12) Troubleshooting: single-command pack

# On server
sudo wg show
sudo nft list ruleset | sed -n '1,160p'
ip route
ip -6 route

# On Linux client
wg show
ip a show wg0
ip route get 1.1.1.1
ping -c 3 10.8.0.1
curl -4 https://ifconfig.co

# On Windows (PowerShell)
tracert -4 1.1.1.1
tracert -6 2606:4700:4700::1111
nslookup example.com
Get-NetConnectionProfile | ? {$_.InterfaceAlias -like "WireGuard*"}
Get-NetFirewallRule -DisplayName "*Echo Request*" -Direction Inbound

Final note

If your goal is peer-to-peer inside the VPN (Windows ↔ Raspberry Pi), the safest recipe is:

  • Split-tunnel on both clients (AllowedIPs = 10.8.0.0/24).
  • On the server, Server Allowed IPs = each peer’s /32 (and /128).
  • Enable “clients can communicate with each other.”
  • On Windows, set WireGuard network to Private and enable Echo Request rules.

That combination “just works” and won’t break your normal web browsing.

这两个是 wg-easy 里给“某个客户端(peer)” 填的两处,作用不一样:

1) Allowed IPs(客户端要走隧道的“目的地”)

  • 这是客户端的路由表:哪些目的地址要丢进 WireGuard 隧道。
  • 常见填法:
    • 只想访问 VPN 内网:10.8.0.0/24(只走到 WG 虚拟网段,不走公网)
    • 全量代理(所有流量都走 VPN):0.0.0.0/0, ::/0
    • 只带公司网段:192.168.1.0/24(只这段走隧道,其它直连)
你图里 10.8.0.0/24 的意思:客户端只把去 10.8.0.x 的包交给 VPN,其它网站不会走 VPN。

2) Server Allowed IPs(服务器认为“这个客户端拥有/负责”的地址)

  • 这是服务器侧的“路由 + 白名单”:哪些源地址/网段是由这个客户端发来的、或这个客户端背后可达。
  • 必须至少包含这个客户端的隧道地址/32,比如:10.8.0.5/32(表示这台客户端“拥有”10.8.0.5)
  • 做站点互联时,还可加客户端后面的网段,如:192.168.50.0/24(服务器和其它 peer 才会把这网段的流量转给它)
你图里 10.8.0.5/32 的意思:这台客户端的隧道 IP 是 10.8.0.5,服务器把发往 10.8.0.5 的包交给它;也不允许别的客户端冒用这个地址。

一句话对比

  • Allowed IPs(客户端):我(客户端)要把“去哪些目的地”的流量交给 VPN。
  • Server Allowed IPs(服务器):这台客户端“拥有/负责”哪些 IP/网段,服务器把“去这些地址”的流量转给它,并且只接受它用这些源地址。

快速填法模板

  • 单纯远程进内网:
    • Allowed IPs:10.8.0.0/24
    • Server Allowed IPs:10.8.0.X/32(X=分配给这台客户端的地址)
  • 全局代理:
    • Allowed IPs:0.0.0.0/0, ::/0
    • Server Allowed IPs:10.8.0.X/32
  • 站点到站点(客户端后面有局域网):
    • Allowed IPs(客户端):0.0.0.0/0 或只需要的网段
    • Server Allowed IPs(服务器):10.8.0.X/32, 192.168.50.0/24
小坑:两台客户端的 Server Allowed IPs 不能重叠(比如都写 10.8.0.5/32 或同一私网段),否则路由冲突。