Skip to content

Latest commit

 

History

History
302 lines (217 loc) · 7.53 KB

File metadata and controls

302 lines (217 loc) · 7.53 KB

Client Setup Guide

This guide covers connecting one or more home servers (Raspberry Pi, NUC, old laptop, etc.) to your exit node so their services are publicly accessible.

How It Works

  • Each device gets its own WireGuard VPN client with a unique VPN IP (e.g. 10.13.13.2, 10.13.13.3).
  • Multiple services on one device share the VPN connection by using network_mode: "service:wireguard-client" — each listens on a different port.
  • Caddy on the exit node routes domain → VPN-IP:port.
Pi 1 (10.13.13.2)          Pi 2 (10.13.13.3)
├── webapp    :8080         ├── blog       :4000
├── api       :3000         └── dashboard  :9090
│
Caddy routes:
  app.example.com       → 10.13.13.2:8080
  api.example.com       → 10.13.13.2:3000
  blog.example.com      → 10.13.13.3:4000
  dashboard.example.com → 10.13.13.3:9090

Prerequisites

On your client machine you need:

  • Docker and Docker Compose installed
  • Internet access (no public IP required — that's the whole point!)

Step 1 — Create a VPN Client on the Exit Node

  1. Open the WireGuard web UI on your exit node at port 51821.

    Security note: The web UI should not be exposed to the public internet over plain HTTP. Use an SSH tunnel to access it securely:

    ssh -L 51821:localhost:51821 user@your-vps-ip

    Then open http://localhost:51821 in your browser.

  2. Log in with the password you set in WG_PASSWORD.

  3. Click "New Client" and give it a name (e.g. pi-1).

  4. Download the generated .conf file.

  5. Note the VPN IP assigned (e.g. 10.13.13.2) — you'll need it for Caddy.

Step 2 — Set Up WireGuard on the Client

Option A: Docker (recommended)

Copy the client/ folder from this repo to your client machine.

scp -r client/ pi@raspberrypi:~/exit-client/

On the client machine:

cd ~/exit-client
mkdir -p wg-config

# Copy the .conf file from step 1
cp /path/to/pi-1.conf wg-config/wg0.conf

# Start the VPN connection
docker compose up -d

Verify the tunnel is up:

docker exec wireguard-client wg show

You should see a handshake timestamp and data transfer stats.

Option B: Native WireGuard (without Docker)

If you prefer to run WireGuard natively:

# Install WireGuard
sudo apt install wireguard

# Copy the config
sudo cp /path/to/raspberry-pi.conf /etc/wireguard/wg0.conf

# Start the tunnel
sudo wg-quick up wg0

# Enable on boot
sudo systemctl enable wg-quick@wg0

Step 3 — Run Your App

With Docker (using the client compose file)

The easiest approach is to add your app to client/docker-compose.yml using network_mode: "service:wireguard-client". This makes your app share the VPN network stack, so it's directly reachable at the VPN IP.

services:
  wireguard-client:
    # ... (already configured)

  myapp:
    image: nginx:alpine
    container_name: myapp
    restart: unless-stopped
    network_mode: "service:wireguard-client"
    # No ports needed — traffic arrives via the VPN tunnel

Your app is now reachable at 10.13.13.2:80 from the exit node.

With a separate Docker Compose project

If your app runs in a different Compose project, you can either:

A) Use host networking on the WireGuard client and access the app via localhost:

# In client/docker-compose.yml
services:
  wireguard-client:
    # ... existing config ...
    network_mode: host

Then your app running on any port on the host is reachable via the VPN IP.

B) Use Docker networks to connect your app to the WireGuard client's network.

Step 4 — Configure Caddy on the Exit Node

Back on your VPS, edit caddy/Caddyfile to route traffic to the client:

myapp.example.com {
    reverse_proxy 10.13.13.2:80
}

Make sure you have a DNS A record pointing myapp.example.com to your VPS IP.

Reload Caddy:

docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile

Caddy will automatically obtain a TLS certificate from Let's Encrypt. Your app is now live at https://myapp.example.com.

Adding More Services on the Same Device

Every service that uses network_mode: "service:wireguard-client" shares the VPN tunnel and is reachable at the device's VPN IP. Just make sure each service listens on a unique port.

# client/docker-compose.yml
services:
  wireguard-client:
    # ... (VPN tunnel — already configured)

  webapp:
    image: my-web-app:latest
    restart: unless-stopped
    network_mode: "service:wireguard-client"
    environment:
      - PORT=8080
    # Reachable at 10.13.13.2:8080

  api:
    image: my-api:latest
    restart: unless-stopped
    network_mode: "service:wireguard-client"
    environment:
      - PORT=3000
    # Reachable at 10.13.13.2:3000

  dashboard:
    image: my-dashboard:latest
    restart: unless-stopped
    network_mode: "service:wireguard-client"
    environment:
      - PORT=9090
    # Reachable at 10.13.13.2:9090

Then in caddy/Caddyfile on the exit node:

app.example.com {
    reverse_proxy 10.13.13.2:8080
}

api.example.com {
    reverse_proxy 10.13.13.2:3000
}

dashboard.example.com {
    reverse_proxy 10.13.13.2:9090
}

Important: Your app must listen on 0.0.0.0 (all interfaces), not 127.0.0.1. Most Docker images do this by default.

Multiple Devices (Multiple Raspberry Pis)

Need multiple exit nodes instead? If you want the same services reachable from more than one VPS (for redundancy or geographic distribution), see Multiple Exit Nodes.

Each device gets its own WireGuard client. Repeat the setup for each device:

On the exit node (once per device)

  1. Open the wg-easy UI and create a new client (e.g. pi-1, pi-2).
  2. Download each .conf file.
  3. Note each device's VPN IP.

On each device

Copy the client/ folder and set up its own WireGuard config:

# Device 1 (Raspberry Pi 1)
cd ~/exit-client
mkdir -p wg-config
cp /path/to/pi-1.conf wg-config/wg0.conf
# Edit docker-compose.yml to add your services
docker compose up -d
# Device 2 (Raspberry Pi 2)
cd ~/exit-client
mkdir -p wg-config
cp /path/to/pi-2.conf wg-config/wg0.conf
# Edit docker-compose.yml to add your services
docker compose up -d

Caddy routing for multiple devices

# Pi 1 services (VPN IP: 10.13.13.2)
app.example.com {
    reverse_proxy 10.13.13.2:8080
}

api.example.com {
    reverse_proxy 10.13.13.2:3000
}

# Pi 2 services (VPN IP: 10.13.13.3)
blog.example.com {
    reverse_proxy 10.13.13.3:4000
}

monitoring.example.com {
    reverse_proxy 10.13.13.3:9090
}

Reload Caddy after any changes:

docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile

Troubleshooting

VPN tunnel not connecting

# Check WireGuard status
docker exec wireguard-client wg show

# Check logs
docker logs wireguard-client
  • Make sure UDP port 51820 is open on the VPS firewall.
  • Make sure the .conf file is correctly placed in wg-config/wg0.conf.

App not reachable from exit node

# From the VPS, test connectivity to the client VPN IP
docker exec caddy ping 10.13.13.2

# Check if the app is listening
docker exec wireguard-client curl -s http://localhost:8080
  • Make sure your app is using network_mode: "service:wireguard-client".
  • Make sure the app is listening on 0.0.0.0, not 127.0.0.1.

Caddy not issuing certificates

  • Check that DNS for your domain points to the VPS public IP.
  • Check Caddy logs: docker logs caddy
  • Make sure ports 80 and 443 are open and not used by another service.