Quick answer

Inside a privileged Debian 12 LXC on Proxmox VE 8, wireguard-tools and a wg0.conf at 10.10.0.1/24 listening on UDP 51820 are the entire server, since the kernel module comes from the Proxmox host rather than the container. Clients take 10.10.0.2 upward with PersistentKeepalive 25, a DuckDNS endpoint, and split-tunnel AllowedIPs of 192.168.1.0/24, 10.10.0.0/24.

By LK Wood IV · 2026-05-27 · ~11 min read · St. Louis County, MO

Network topology diagram: remote phone (10.10.0.2) and laptop (10.10.0.3) WireGuard clients connect over UDP 51820 through a DuckDNS endpoint and router port forward to a WireGuard LXC (192.168.1.7, VPN gateway 10.10.0.1/24) on a Proxmox VE 8 host, reaching home LAN 192.168.1.0/24 and AdGuard Home DNS (192.168.1.5), with split-tunnel vs full-tunnel AllowedIPs options

Tailscale is easier. If you want the 15-minute path to remote access, the Tailscale tutorial is the right read. WireGuard is for when you want zero coordinator: your server, your key pair, no third-party knowing which clients are connected. (Weighing WireGuard against OpenVPN instead? See the protocol comparison.)

This guide sets up a WireGuard server in a Proxmox LXC container, generates client configs for phones and laptops, and covers the split vs full tunnel decision.

What you’ll have at the end

  • WireGuard server running in a Proxmox LXC, listening on UDP port 51820
  • Clients (phone, laptop) that can reach your homelab from anywhere
  • Your choice of split tunnel (access homelab only) or full tunnel (all traffic through home)
  • Dynamic DNS hostname so it works when your home IP changes

Prerequisites

  • Proxmox VE 8 host on a home network with internet access
  • A port forward on your router: UDP 51820 → your WireGuard LXC IP
  • Optionally: a DuckDNS or Cloudflare DNS hostname for your home IP

Verify WireGuard kernel module on the host:

# On the Proxmox host (not inside an LXC)
lsmod | grep wireguard
# Should show: wireguard
# If not, load it:
modprobe wireguard
echo "wireguard" >> /etc/modules

Step 1: Create the LXC container

In Proxmox web UI → Create CT:

Hostname: wireguard
Template: Debian 12 (bookworm)
Storage: 4GB disk
RAM: 256MB
vCPU: 1
Network: Static IP (e.g., 192.168.1.7/24)
Privileged: yes (simplest for net_admin capability)

Start the container, then inside it:

apt update && apt install -y wireguard qrencode

Step 2: Generate server key pair

# Inside the WireGuard LXC
cd /etc/wireguard
umask 077

# Generate server private and public keys
wg genkey | tee server_private.key | wg pubkey > server_public.key

cat server_private.key
cat server_public.key

Keep server_private.key secure — anyone with the private key can decrypt traffic.

Step 3: Create the server config

nano /etc/wireguard/wg0.conf
[Interface]
# The WireGuard server's IP on the VPN subnet
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <paste server_private.key here>

# Enable IP forwarding and NAT for client traffic
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

# Clients are added below as [Peer] blocks

The Address = 10.10.0.1/24 line defines the VPN subnet, and every client’s AllowedIPs (your LAN such as 192.168.1.0/24, or 0.0.0.0/0 for full-tunnel) is also a CIDR block. The subnet & CIDR calculator makes it easy to get those masks and host ranges right — and to pick a VPN subnet that won’t overlap the LAN you’re routing to.

The 10.10.0.0/24 subnet is the WireGuard VPN subnet — separate from your home LAN (192.168.1.0/24). The server gets 10.10.0.1; clients get 10.10.0.2, 10.10.0.3, etc.

Enable IP forwarding:

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p

Start and enable WireGuard:

systemctl enable --now wg-quick@wg0

# Verify
wg show

Step 4: Generate client configs

For each client (phone, laptop, etc.) generate a key pair and config. Do this on the server for convenience:

# Generate client keys (do this for each client)
wg genkey | tee client1_private.key | wg pubkey > client1_public.key

cat client1_private.key   # goes in the client config
cat client1_public.key    # goes in the server config as a [Peer]

Client config (save to a file or display as QR code):

[Interface]
Address = 10.10.0.2/32
PrivateKey = <client1_private.key>
DNS = 192.168.1.5   # your AdGuard Home IP, or 1.1.1.1 if not running AdGuard

[Peer]
# The WireGuard server
PublicKey = <server_public.key>
Endpoint = your-home.duckdns.org:51820
# Split tunnel: access homelab only
AllowedIPs = 192.168.1.0/24, 10.10.0.0/24
# Full tunnel: all traffic through home (uncomment instead)
# AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

PersistentKeepalive = 25 keeps the connection alive through NAT — without it, an idle connection behind NAT may drop after a few minutes.

Add the client to the server config:

# Append to /etc/wireguard/wg0.conf on the server
cat >> /etc/wireguard/wg0.conf << EOF

[Peer]
# Client 1 (phone)
PublicKey = <client1_public.key>
AllowedIPs = 10.10.0.2/32
EOF

# Reload WireGuard (no downtime for other clients)
wg syncconf wg0 <(wg-quick strip wg0)

Display the client config as a QR code (scan with the WireGuard app on iOS/Android):

# Create the client config as a file first
cat > /tmp/client1.conf << 'EOF'
[Interface]
Address = 10.10.0.2/32
PrivateKey = <client1_private.key>
DNS = 192.168.1.5

[Peer]
PublicKey = <server_public.key>
Endpoint = your-home.duckdns.org:51820
AllowedIPs = 192.168.1.0/24, 10.10.0.0/24
PersistentKeepalive = 25
EOF

qrencode -t ansiutf8 < /tmp/client1.conf
# Or save as PNG:
qrencode -t PNG -o /tmp/client1.png < /tmp/client1.conf

Delete the client private key file from the server after you’ve scanned it — it should only exist on the client.

Step 5: Set up dynamic DNS

If your home IP changes (most residential ISPs assign dynamic IPs), use a free DDNS service:

DuckDNS (free, simple):

  1. Go to duckdns.org, create an account, create a domain: yourname.duckdns.org
  2. Create an update script:
mkdir -p /opt/duckdns
cat > /opt/duckdns/duck.sh << 'EOF'
#!/bin/bash
echo url="https://www.duckdns.org/update?domains=yourname&token=YOUR-TOKEN&ip=" | curl -k -o /opt/duckdns/duck.log -K -
EOF
chmod +x /opt/duckdns/duck.sh

# Add to cron — update every 5 minutes
echo "*/5 * * * * root /opt/duckdns/duck.sh >/dev/null 2>&1" >> /etc/cron.d/duckdns
  1. Use yourname.duckdns.org:51820 as the Endpoint in all your client configs.

Step 6: Set up the port forward

On your router (OPNsense, pfSense, or consumer firmware):

  • Forward UDP port 51820 to your WireGuard LXC IP (192.168.1.7)

In OPNsense: Firewall → NAT → Port Forward → Add:

  • Interface: WAN
  • Protocol: UDP
  • Destination port: 51820
  • Redirect target: 192.168.1.7, port 51820

Verify the connection

On a client, after importing the config:

  1. Enable the WireGuard tunnel
  2. Check that you can reach the server: ping 10.10.0.1
  3. For split tunnel: verify you can reach homelab devices: ping 192.168.1.2 (Proxmox host)
  4. For full tunnel: verify your exit IP: visit a “what is my IP” site — it should show your home IP

On the server, wg show displays connected peers, their last handshake time, and transfer stats. A handshake within the last 3 minutes means the client is actively connected.

Adding AdGuard Home as VPN DNS

If you’re running AdGuard Home for DNS filtering and local hostname resolution, set it as the DNS in client configs:

[Interface]
DNS = 192.168.1.5    # AdGuard Home IP

This lets VPN clients:

  • Resolve pve.lan, nas.lan, and other local hostnames
  • Get DNS-level ad blocking while connected to the VPN
  • Use AdGuard’s split DNS without a separate Tailscale configuration

Split tunnel vs full tunnel

Split tunnel (AllowedIPs = 192.168.1.0/24, 10.10.0.0/24):

  • Only homelab traffic goes through the VPN
  • Internet traffic uses the local connection at the client’s location
  • Faster for general browsing; no impact on video streaming
  • Can’t browse the web through your home IP

Full tunnel (AllowedIPs = 0.0.0.0/0, ::/0):

  • All traffic routes through the WireGuard server and your home network
  • Use your home IP as the exit node
  • Useful on untrusted networks (airports, hotels) or for geo-unlocking
  • Your home upload bandwidth becomes the internet speed limit for clients

For a homelab VPN where the goal is remote access to services, split tunnel is usually the right choice — it’s faster and doesn’t load your home upload connection with all internet traffic.

WireGuard vs Tailscale: the honest comparison

WireGuard (self-hosted)Tailscale
Setup time30–60 min15 min
Third-party dependencyNoneTailscale coordinator
Works behind double NATRequires port forward on one sideYes, fully automatic
Client countUnlimitedGenerous device allowance, 6 free users
Key managementManualAutomatic
ACLs / per-client rulesManual iptablesTailscale ACL policy
Mobile appOfficial WireGuard appTailscale app
Local DNS integrationManual (set DNS per client)MagicDNS, automatic

Tailscale wins on simplicity and NAT traversal. WireGuard wins on control, privacy (no coordinator logs), and flexibility (full tunnel, custom DNS, any subnet routing). For the full decision framework — including which one to pick behind CGNAT — see Tailscale vs WireGuard: which one should you actually run?.

If you have OPNsense already, it has a built-in WireGuard plugin that eliminates the need for a separate LXC container — it runs directly on the firewall and gets port forwarded automatically.


Running VLANs? See the VLAN homelab guide for segmenting your WireGuard clients from IoT and trusted LAN traffic. For OPNsense specifically, the OPNsense mini PC setup guide covers the WireGuard plugin under Firewall → WireGuard.

Sources

Frequently asked questions

WireGuard vs Tailscale: which should I use?
Tailscale if you want something that works in 15 minutes with no server to maintain, peer-to-peer NAT traversal by default, and handles roaming clients well. WireGuard if you want zero third-party coordinator (full control of the VPN server), a fixed entry point (static IP/DDNS on your home network), or you want to route all internet traffic through your home IP (full-tunnel). Tailscale uses WireGuard under the hood but adds the coordination layer. For a homelab that’s already running Proxmox, WireGuard is a natural choice — it’s one LXC container and no ongoing SaaS dependency.
Do I need a static IP for a WireGuard server?
No, but it helps. A dynamic DNS hostname (DuckDNS is free) updates every time your home IP changes — most residential IPs change rarely or not at all. Set the WireGuard server endpoint to your DDNS hostname instead of a static IP. The worst case: after an IP change, re-run ‘wg-quick down/up’ on the client to re-resolve the hostname.
Can I run WireGuard in a Proxmox LXC container?
Yes, with a kernel module consideration. WireGuard is a kernel module — it must be loaded on the Proxmox host, not inside the LXC. On Proxmox VE 8 with a recent Debian kernel, the wireguard module is already loaded by default. The LXC uses the host kernel’s WireGuard module, so you just need wg-quick and the WireGuard tools inside the container. Use a privileged LXC for simplest setup, or add the ’net_admin’ capability to an unprivileged container.
How do I route all client traffic through the WireGuard server (full tunnel)?
In the client config, change AllowedIPs from your LAN subnet (e.g., 192.168.1.0/24) to 0.0.0.0/0, ::/0. This routes all internet traffic through your WireGuard server. On the server, enable IP forwarding and NAT (iptables masquerade). This makes your home IP the exit node for all client traffic — useful for accessing geo-restricted content or using your home’s trusted network while traveling.
How many clients can a WireGuard server handle?
WireGuard is highly efficient. A single LXC container with 1 vCPU and 256MB RAM can handle 10–20 simultaneous VPN clients easily. WireGuard’s cryptographic overhead is low compared to OpenVPN or IPsec. A 1 Gbps home internet connection is more likely to be the bottleneck than the WireGuard server itself.

Evidence ledger

Last updated
Methodology
This tutorial was written and edited by Lowell K. Wood IV in St. Louis County, MO. Specs, prices, commands, and version numbers are drawn from the official vendor, reseller, and project documentation current on the date above, and were verified before publishing. First-person hardware claims appear only where the article shows a verifiable artifact — a photo, receipt, or measurement — or links to the TechFuelHQ Open Bench Datasets. Every fact is human-verified against its cited source before publishing; AI assists with first-draft structure and source-gathering, not with the verdict. Full editorial standard: methodology.
Update log
  • 2026-07-25 — Last reviewed and updated.
Corrections
Spotted an error or stale price? Email hello@techfuelhq.com. Confirmed corrections are added to the update log above.

About the author

Written by Lowell K. Wood IV. Lowell builds and runs TechFuelHQ from St. Louis, Missouri, pairing thirteen-plus years of hands-on homelab, PC, server, and networking experience with cited third-party testing and first-party benchmarks on the gear he still runs. He also works ground EMS as a Nationally Registered Paramedic (NREMT).