0) Goals we achieved

  • Main server runs WireGuard server (wg0)
  • Pi1 + Pi2 run WireGuard clients
  • Split tunnel (VPN traffic only; internet stays normal/fast)
  • AdGuard Home runs on a Pi and keeps DNS control (no WG DNS override)
  • Optional: Monitor Docker hosts from Uptime Kuma over WireGuard
  • IPv6 permanently disabled (optional)
  • UFW firewall locked down with only the ports you want public

1) WireGuard on the Main Server (Server side)

1.1 Install

sudo apt update
sudo apt install -y wireguard

1.2 Generate keys

wg genkey | sudo tee /etc/wireguard/server.key | wg pubkey | sudo tee /etc/wireguard/server.pub >/dev/null
sudo chmod 600 /etc/wireguard/server.key

1.3 Create /etc/wireguard/wg0.conf

sudo nano /etc/wireguard/wg0.conf

Paste:

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>

Insert the private key:

sudo cat /etc/wireguard/server.key

1.4 Enable + start

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wg

2) WireGuard on Raspberry Pi 1 and Pi 2 (Client side)

Do this on each Pi, changing the IP.

2.1 Install

sudo apt update
sudo apt install -y wireguard

2.2 Generate keys

wg genkey | tee ~/client.key | wg pubkey > ~/client.pub
chmod 600 ~/client.key

2.3 Create /etc/wireguard/wg0.conf

Pi1 config (10.8.0.2)

sudo nano /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.2/24
PrivateKey = <PI1_PRIVATE_KEY>
# IMPORTANT: No DNS line (because you run AdGuard)

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = <SERVER_PUBLIC_IP>:51820
AllowedIPs = 10.8.0.0/24
PersistentKeepalive = 25

Pi2 config (10.8.0.3)

Same, but:

Address = 10.8.0.3/24
PrivateKey = <PI2_PRIVATE_KEY>

Critical split tunnel rule:

AllowedIPs = 10.8.0.0/24

❌ Do NOT use .../0 or 0.0.0.0/0.

2.4 Start on each Pi

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wg

3) Add Pi peers to the Main Server

On the main server, open:

sudo nano /etc/wireguard/wg0.conf

Add:

[Peer]
PublicKey = <PI1_PUBLIC_KEY>
AllowedIPs = 10.8.0.2/32

[Peer]
PublicKey = <PI2_PUBLIC_KEY>
AllowedIPs = 10.8.0.3/32

Restart:

sudo systemctl restart wg-quick@wg0
sudo wg

4) Verify split tunnel is correct

4.1 Tunnel ping tests

From Pi1/Pi2:

ping 10.8.0.1

From server:

ping 10.8.0.2
ping 10.8.0.3

4.2 Confirm internet stays normal (split tunnel)

On Pi2:

ip route

You should see:

  • default route via your LAN gateway (ex: 192.168.x.1)
  • 10.8.0.0/24 dev wg0

If you ever see default via wg0, you accidentally full-tunneled.


5) DNS note (AdGuard Home on Pi)

You fixed this correctly.

✅ If AdGuard runs on the Pi, do NOT set DNS = ... in the WG config.
Because WireGuard would override system DNS and break resolution.


6) Optional: Disable IPv6 permanently (Ubuntu / Pi)

6.1 Sysctl method (recommended)

sudo nano /etc/sysctl.d/99-disable-ipv6.conf

Paste:

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

Apply:

sudo sysctl --system

Verify:

ip a | grep inet6

7) Firewall (UFW) with your current public ports (22, 80, 443)

7.1 Reset + defaults

sudo ufw disable
sudo ufw reset
sudo ufw default deny incoming
sudo ufw default allow outgoing

7.2 Allow required ports

SSH (choose one)

Safer (WireGuard only):

sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp comment "SSH via WireGuard"

Or if public SSH is needed:

sudo ufw limit 22/tcp comment "SSH rate limit"

HTTP/HTTPS:

sudo ufw allow 80/tcp comment "HTTP"
sudo ufw allow 443/tcp comment "HTTPS"

WireGuard port:

sudo ufw allow 51820/udp comment "WireGuard"
sudo ufw allow in on wg0 comment "WireGuard tunnel"

7.3 Enable

sudo ufw enable
sudo ufw status numbered
sudo ufw status verbose

8) Optional: Docker monitoring over WireGuard (Uptime Kuma)

8.1 Install docker socket proxy on each Pi (recommended)

Create docker-compose.yml:

version: "3.8"
services:
  docker-socket-proxy:
    image: tecnativa/docker-socket-proxy
    container_name: docker-socket-proxy
    ports:
      - "2375:2375"
    environment:
      CONTAINERS: 1
      INFO: 1
      PING: 1
      VERSION: 1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: unless-stopped

Run:

docker compose up -d

8.2 Firewall: allow Docker proxy ONLY over WireGuard

On each Pi:

sudo ufw allow from 10.8.0.0/24 to any port 2375 comment "Docker API via WireGuard only"

8.3 Add in Uptime Kuma

  • Monitor Type: Docker Host
  • URL:
    • http://10.8.0.2:2375
    • http://10.8.0.3:2375

9) What NOT to do (the pitfalls we hit)

  • ❌ Don’t expose Docker 2375 publicly
  • ❌ Don’t use AllowedIPs = .../0 (that becomes full tunnel)
  • ❌ Don’t set DNS = 10.8.0.1 unless server truly runs DNS
  • ❌ Don’t rely on Certbot/Caddy to “secure Docker API” (wrong model)
  • ✅ Use WireGuard + firewall instead

Quick “health checklist” commands

On any node:

sudo wg
ip route
sudo ufw status verbose
ss -tulpen | head -n 30

If you want, I can also generate a one-page printable version of this runbook with:

  • blanks for keys/IPs
  • checkboxes
  • and “expected outputs” for each command.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Secret Link