/homelab-wireguard-vpn
WireGuard VPN server setup, peer configuration, key generation, split tunneling vs full tunnel routing, and remote access to a home network from mobile and laptop clients.
$ npx -y skills add affaan-m/everything-claude-code --skill homelab-wireguard-vpn --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/homelab-wireguard-vpn
Context preview
The summary Claude sees to decide when to auto-load this skill.
WireGuard VPN server setup, peer configuration, key generation, split tunneling vs full tunnel routing, and remote access to a home network from mobile and laptop clients.
SKILL.md
homelab-wireguard-vpn.SKILL.mdname: homelab-wireguard-vpn
description: WireGuard VPN server setup, peer configuration, key generation, split tunneling vs full tunnel routing, and remote access to a home network from mobile and laptop clients.
metadata:
origin: community
Homelab WireGuard VPN
WireGuard is a fast, modern VPN protocol. It is the right choice for remote access to a home network — simpler to configure than OpenVPN and faster than most alternatives.
All configuration examples show common setups. Review each command — especially the iptables forwarding rules and key file permissions — before applying them to your system, and make changes in a maintenance window.
When to Use
- Setting up WireGuard server on a Raspberry Pi, Linux host, pfSense, or router
- Generating WireGuard keypairs and writing peer config files
- Configuring remote access from a phone or laptop to a home network
- Explaining split tunneling (route only home traffic) vs full tunnel (route all traffic)
- Troubleshooting WireGuard connections that will not come up
- Automating peer configuration generation for multiple clients
How WireGuard Works
Your phone (WireGuard client)
│
│ Encrypted UDP tunnel (port 51820)
│
Your home router (WireGuard server — needs a public IP or DDNS)
│
Your home network (192.168.1.0/24, NAS, Pi, etc.)
Every device has a keypair (public + private key).
The server knows each client's public key.
The client knows the server's public key + endpoint (IP:port).
Traffic is encrypted end-to-end with no central server or certificate authority.Server Setup (Linux)
# Install WireGuard
sudo apt update && sudo apt install wireguard -y
# Generate server keypair — create files with private permissions from the start
sudo mkdir -p /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server_private.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key'
# Write server config — substitute the actual private key value
# Do not store private keys in version control or share them
sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.8.0.1/24 # VPN subnet — server gets .1
ListenPort = 51820
PrivateKey = <paste_server_private_key_here>
# Scoped forwarding rules: allow VPN traffic in/out, not a blanket FORWARD ACCEPT
PostUp = iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Phone — replace with the actual phone public key
PublicKey = <phone_public_key>
AllowedIPs = 10.8.0.2/32
[Peer]
# Laptop — replace with the actual laptop public key
PublicKey = <laptop_public_key>
AllowedIPs = 10.8.0.3/32
EOF
sudo chmod 600 /etc/wireguard/wg0.conf
# Replace eth0 with your actual outbound interface name
# Check with: ip route show default
# Enable IP forwarding (required for routing traffic through the server)
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
# Start WireGuard and enable on boot
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
Client Configuration
# Generate a unique keypair for each client device
# Run on the client, or on the server and transfer the private key securely — never in plaintext
umask 077
wg genkey | tee phone_private.key | wg pubkey > phone_public.key
# Client config file (phone_wg0.conf):
[Interface]
PrivateKey = <phone_private_key>
Address = 10.8.0.2/32
DNS = 192.168.1.2 # Optional: use Pi-hole for DNS over the tunnel
[Peer]
PublicKey = <server_public_key>
Endpoint = your-home-ip.ddns.net:51820 # Your public IP or DDNS hostname
AllowedIPs = 192.168.1.0/24 # Split tunnel: only home network traffic
# AllowedIPs = 0.0.0.0/0, ::/0 # Full tunnel: all traffic through VPN
PersistentKeepalive = 25 # Keep NAT hole open (required for mobile clients)
Split Tunnel vs Full Tunnel
# Split tunnel: AllowedIPs = 192.168.1.0/24
Only traffic destined for your home network goes through the VPN.
Internet traffic (YouTube, Spotify) goes directly — better performance on mobile.
Best for: "I just want to reach my NAS and Pi from anywhere."
# Full tunnel: AllowedIPs = 0.0.0.0/0, ::/0
ALL traffic goes through your home internet connection.
Useful for: piggybacking home DNS/Pi-hole ad blocking.
Downside: home upload speed becomes your bottleneck everywhere.
# Multi-subnet split tunnel (most common homelab use case):
AllowedIPs = 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24, 10.8.0.0/24
Routes all your VLANs through the tunnel; internet stays direct.
Key Generation and Peer Management
import subprocess
def generate_keypair() -> tuple[str, str]:
"""Generate a WireGuard keypair. Returns (private_key, public_key)."""
private = subprocess.check_output(["wg", "genkey"]).decode().strip()
public = subprocess.run(
["wg", "pubkey"], input=private.encode(), capture_output=True
).stdout.decode().strip()
return private, public
def generate_preshared_key() -> str:
return subprocess.check_output(["wg", "genpsk"]).decode().strip()
def build_client_config(
client_private_key: str,
client_vpn_ip: str, # e.g. "10.8.0.3"
server_public_key: str,
server_endpoint: str, # e.g. "home.example.com:51820"
allowed_ips: str = "192.168.1.0/24",
dns: str = "",
) -> str:
dns_line = f"DNS = {dns}\n" if dns else ""
return f"""[Interface]
PrivateKey = {client_private_key}
Address = {client_vpn_ip}/32
{dns_line}
[Peer]
PublicKey = {server_public_key}
Endpoint = {server_endpoint}
AllowedIPs = {alloweRead more
name: homelab-wireguard-vpn description: WireGuard VPN server setup, peer configuration, key generation, split tunneling vs full tunnel routing, and remote access to a home network from mobile and laptop clients. metadata: origin: community
Homelab WireGuard VPN
WireGuard is a fast, modern VPN protocol. It is the right choice for remote access to a home network — simpler to configure than OpenVPN and faster than most alternatives.
All configuration examples show common setups. Review each command — especially the iptables forwarding rules and key file permissions — before applying them to your system, and make changes in a maintenance window.
When to Use
- Setting up WireGuard server on a Raspberry Pi, Linux host, pfSense, or router
- Generating WireGuard keypairs and writing peer config files
- Configuring remote access from a phone or laptop to a home network
- Explaining split tunneling (route only home traffic) vs full tunnel (route all traffic)
- Troubleshooting WireGuard connections that will not come up
- Automating peer configuration generation for multiple clients
How WireGuard Works
Your phone (WireGuard client)
│
│ Encrypted UDP tunnel (port 51820)
│
Your home router (WireGuard server — needs a public IP or DDNS)
│
Your home network (192.168.1.0/24, NAS, Pi, etc.)
Every device has a keypair (public + private key).
The server knows each client's public key.
The client knows the server's public key + endpoint (IP:port).
Traffic is encrypted end-to-end with no central server or certificate authority.Server Setup (Linux)
# Install WireGuard sudo apt update && sudo apt install wireguard -y # Generate server keypair — create files with private permissions from the start sudo mkdir -p /etc/wireguard sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server_private.key' sudo sh -c 'wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key' # Write server config — substitute the actual private key value # Do not store private keys in version control or share them sudo tee /etc/wireguard/wg0.conf << 'EOF' [Interface] Address = 10.8.0.1/24 # VPN subnet — server gets .1 ListenPort = 51820 PrivateKey = <paste_server_private_key_here> # Scoped forwarding rules: allow VPN traffic in/out, not a blanket FORWARD ACCEPT PostUp = iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT PostUp = iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT PostDown = iptables -D FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] # Phone — replace with the actual phone public key PublicKey = <phone_public_key> AllowedIPs = 10.8.0.2/32 [Peer] # Laptop — replace with the actual laptop public key PublicKey = <laptop_public_key> AllowedIPs = 10.8.0.3/32 EOF sudo chmod 600 /etc/wireguard/wg0.conf # Replace eth0 with your actual outbound interface name # Check with: ip route show default # Enable IP forwarding (required for routing traffic through the server) echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf sudo sysctl --system # Start WireGuard and enable on boot sudo wg-quick up wg0 sudo systemctl enable wg-quick@wg0
Client Configuration
# Generate a unique keypair for each client device # Run on the client, or on the server and transfer the private key securely — never in plaintext umask 077 wg genkey | tee phone_private.key | wg pubkey > phone_public.key # Client config file (phone_wg0.conf): [Interface] PrivateKey = <phone_private_key> Address = 10.8.0.2/32 DNS = 192.168.1.2 # Optional: use Pi-hole for DNS over the tunnel [Peer] PublicKey = <server_public_key> Endpoint = your-home-ip.ddns.net:51820 # Your public IP or DDNS hostname AllowedIPs = 192.168.1.0/24 # Split tunnel: only home network traffic # AllowedIPs = 0.0.0.0/0, ::/0 # Full tunnel: all traffic through VPN PersistentKeepalive = 25 # Keep NAT hole open (required for mobile clients)
Split Tunnel vs Full Tunnel
# Split tunnel: AllowedIPs = 192.168.1.0/24 Only traffic destined for your home network goes through the VPN. Internet traffic (YouTube, Spotify) goes directly — better performance on mobile. Best for: "I just want to reach my NAS and Pi from anywhere." # Full tunnel: AllowedIPs = 0.0.0.0/0, ::/0 ALL traffic goes through your home internet connection. Useful for: piggybacking home DNS/Pi-hole ad blocking. Downside: home upload speed becomes your bottleneck everywhere. # Multi-subnet split tunnel (most common homelab use case): AllowedIPs = 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24, 10.8.0.0/24 Routes all your VLANs through the tunnel; internet stays direct.
Key Generation and Peer Management
import subprocess
def generate_keypair() -> tuple[str, str]:
"""Generate a WireGuard keypair. Returns (private_key, public_key)."""
private = subprocess.check_output(["wg", "genkey"]).decode().strip()
public = subprocess.run(
["wg", "pubkey"], input=private.encode(), capture_output=True
).stdout.decode().strip()
return private, public
def generate_preshared_key() -> str:
return subprocess.check_output(["wg", "genpsk"]).decode().strip()
def build_client_config(
client_private_key: str,
client_vpn_ip: str, # e.g. "10.8.0.3"
server_public_key: str,
server_endpoint: str, # e.g. "home.example.com:51820"
allowed_ips: str = "192.168.1.0/24",
dns: str = "",
) -> str:
dns_line = f"DNS = {dns}\n" if dns else ""
return f"""[Interface]
PrivateKey = {client_private_key}
Address = {client_vpn_ip}/32
{dns_line}
[Peer]
PublicKey = {server_public_key}
Endpoint = {server_endpoint}
AllowedIPs = {alloweYour agent can write code, but ECC gives it a coordinated engineering system and toolbox: it plans before it builds, verifies changes with tests, reviews its own work from a fresh context, remembers what matters, and turns repeated wins into reusable skills
Repo: affaan-m/everything-claude-code
Other skills on ecc.
- /everything-claude-code
Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.
Open skill - /accessibility
Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA
Open skill - /agent-architecture-audit
Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for
Open skill - /agent-eval
Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics
Open skill - /agent-harness-construction
Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates.
Open skill - /agent-introspection-debugging
Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports.
Open skill

