Skip to content

synthetiqgroup/rpi-wireguard-exit-node

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

Raspberry Pi as WireGuard VPN Exit Node

Turn a Raspberry Pi into a WireGuard VPN server so you can browse the internet from your home IP — even when you're on the other side of the world.

Tested on Raspberry Pi 3B with Raspberry Pi OS Lite 64-bit. Should work on any RPi 3B or newer running the same OS.

Use case: You're travelling abroad and some websites, streaming services, or apps don't work the same way — or at all. This setup lets you route all your traffic through your home IP. Your devices connect to the RPi via a WireGuard tunnel and everything works as if you were at home.

Why Not a Commercial VPN?

Because you're exiting through your own residential IP, not a shared datacenter IP:

  • No CAPTCHAs — commercial VPNs share IPs across thousands of users, triggering anti-bot checks. Your home IP is clean.
  • No service blocks — streaming platforms (Netflix, Disney+), banks, and other services actively block known VPN IP ranges. A residential IP isn't flagged.
  • Access your home network — reach your NAS, cameras, printer, or smart home devices as if you were on your couch.
  • No subscription — one-time hardware cost, no monthly fees.
  • No third party — no VPN provider sees your traffic. You own the server, you control the logs.
  • Stable, unique IP — always the same IP (your ISP box), which helps with services that check geographic consistency.

Architecture

flowchart LR
    subgraph abroad["🌏 Abroad"]
        direction TB

        subgraph option_a["Option A — Travel router"]
            direction TB
            dev_a1["📱 Phone"]
            dev_a2["💻 Laptop"]
            router["🔲 Travel Router<br/>(e.g. GL.iNet Beryl AX)"]
            dev_a1 -.->|WiFi| router
            dev_a2 -.->|WiFi| router
        end

        subgraph option_b["Option B — WireGuard app"]
            direction TB
            phone_b["📱 Phone<br/>(Android / iOS)"]
            laptop_b["💻 Laptop<br/>(Windows / macOS / Linux)"]
        end
    end

    subgraph home["🏠 Home"]
        direction TB
        isp["🌐 ISP Router<br/>NAT: 51820/UDP → RPi"]
        rpi["🍓 Raspberry Pi<br/>WireGuard Server<br/>+ DuckDNS"]
        rpi -->|eth0| isp
    end

    router ==>|"WireGuard tunnel<br/>(UDP 51820)"| isp
    phone_b ==>|"WireGuard tunnel<br/>(UDP 51820)"| isp
    laptop_b ==>|"WireGuard tunnel<br/>(UDP 51820)"| isp
    isp -->|"port forward"| rpi
    isp ==>|"🌍 Internet<br/>(home IP)"| web["☁️ Web"]

    style abroad fill:#1a1a2e,color:#fff
    style option_a fill:#0d1b2a,color:#fff
    style option_b fill:#0d1b2a,color:#fff
    style home fill:#162447,color:#fff
    style router fill:#e43f5a,color:#fff
    style rpi fill:#1f4068,color:#fff
    style isp fill:#1b1b2f,color:#fff
    style web fill:#0f3460,color:#fff
Loading

How it works:

  1. The RPi sits at home, connected to your ISP router via ethernet
  2. DuckDNS keeps a public DNS name pointing to your home IP (updated every 5 min)
  3. Your ISP router forwards port 51820/UDP to the RPi
  4. Abroad, your devices connect to the RPi through a WireGuard tunnel — either:
    • Option A: via a travel router (e.g. GL.iNet Beryl AX) — all devices behind it are tunnelled automatically
    • Option B: via the WireGuard app installed directly on each device (Android, iOS, Windows, macOS, Linux)
  5. All traffic exits through your home IP → you're subject to your home country's internet rules, not the local ones

Prerequisites

What Details
Raspberry Pi 3B or newer, with Raspberry Pi OS Lite 64-bit (fresh install)
Network RPi connected via ethernet to your ISP router
DuckDNS account Free — sign up at duckdns.org, create a subdomain, grab your token
Router access Ability to set up a DHCP static lease + port forwarding (NAT/PAT)
WireGuard client Any device or app: travel router, phone, laptop...

Quick Start

# 1. Clone the repo
git clone https://github.com/synthetiqgroup/rpi-wireguard-exit-node.git
cd rpi-wireguard-exit-node

# 2. Edit the configuration section at the top of the script
#    → Set your DuckDNS token, subdomain, and client profile name(s)
nano setup.sh

# 3. Run it (must be root, must be bash)
sudo bash setup.sh

The script is fully unattended — zero interactive prompts. It will:

  • Install & configure everything (WireGuard, DuckDNS, iptables, unattended-upgrades...)
  • Print each client .conf to the terminal — paste it into your WireGuard client

After the Script

Two manual steps remain (the script reminds you):

  1. Router port forwarding — forward 51820/UDP to the RPi's local IP (router admin is often at 192.168.1.1 or 192.168.0.1 — check your router's docs)

  2. Client config — paste the generated .conf into your WireGuard client (e.g. GL.iNet → VPN → WireGuard Client → Add Profiles → Manually)

Managing the Server

After setup, use manage.sh to manage your WireGuard server from the command line:

# Health check: WireGuard, DuckDNS, public IP, disk...
sudo bash manage.sh status

# List all profiles and their connection status
sudo bash manage.sh list

# Display a profile's .conf + QR code (interactive picker if no name given)
sudo bash manage.sh show
sudo bash manage.sh show phone

# Add one or more profiles
sudo bash manage.sh add phone laptop

# Remove profiles (will ask for confirmation)
sudo bash manage.sh remove phone laptop

# Remove without confirmation
sudo bash manage.sh remove phone -y

# Replace the DuckDNS token and verify it works
sudo bash manage.sh duckdns-token a1b2c3d4-e5f6-7890-abcd-ef1234567890

Aliases: ls for list, rm or del for remove.

What the Script Does

Step Description
1 Refresh package lists (apt update)
2 Create dedicated VPN user with sudo (no password login)
3 Install dependencies (curl, iptables-persistent, unattended-upgrades)
4 Configure DuckDNS update script (dynamic DNS)
5 Enable IP forwarding
6 Install PiVPN / WireGuard (unattended)
7 Fix iptables NAT rule (PiVPN bug workaround) + persist rules
8 Create WireGuard client profile(s)
9 Configure logrotate + cap journald at 100 MB
10 Schedule DuckDNS cron job (every 5 min, optional)

All configuration is in the top section of setup.sh — see the inline comments for details.

Coexistence with Other Services

If another service on this machine already updates the same DuckDNS domain, set DUCKDNS_CRON=false in setup.sh. The DuckDNS update script is still created (for manual testing via manage.sh), but the cron job is skipped.

If you use a different DuckDNS domain for this service, leave DUCKDNS_CRON=true.

Performance

Your actual speed is limited by the slowest link in the chain:

  1. Home ISP upload speed (e.g. 400 Mbps fiber upload)
  2. Raspberry Pi WireGuard throughput (see table below)
  3. Remote ISP speed where you're connecting from
  4. Client device WireGuard capability (e.g. GL.iNet Beryl AX caps at ~300 Mbps over ethernet)
RPi model Max WireGuard throughput
RPi 3B ~85 Mbps (USB2 shared bus)
RPi 4 ~500 Mbps
RPi 5 ~900 Mbps

Troubleshooting

Problem Cause Fix
syntax error near unexpected token ( Script was run with sh instead of bash Use sudo bash setup.sh — arrays require bash
DuckDNS returns KO Wrong token or subdomain Double-check DUCKDNS_TOKEN and DUCKDNS_DOMAIN at duckdns.org
PiVPN install fails RPi hostname is too long Keep hostname under 20 characters (sudo hostnamectl set-hostname short-name)
pivpn add refuses the profile name Name exceeds 15 characters WireGuard interface names are limited to 15 chars — use shorter names
Two devices share the same .conf Only the last connected device works Each profile has a unique key pair — 1 profile = 1 device at a time. Create a separate profile per device.
Slow download, fast upload ISP or mobile carrier throttling UDP Test on a different network — this is not a tunnel issue
WireGuard app says "endpoint domain could not be resolved" but WireGuard is running fine on the RPi The client's ISP does not resolve *.duckdns.org subdomains (common with some mobile carriers abroad) Replace the domain with the server's public IP in the client config Endpoint field. Find the IP with: https://dns.google/resolve?name=YOUR-SUBDOMAIN.duckdns.org&type=A. For a permanent fix, set Private DNS on the phone to one.one.one.one (Android: Settings → Network → Private DNS) so the domain works directly.

Disclaimer

This project is provided for educational and legitimate personal use only. You are solely responsible for how you use it. Make sure you comply with the laws and regulations of your country and any country you connect from. The author assumes no liability for misuse.

License

MIT

About

Turn a Raspberry Pi into a WireGuard VPN exit node at home. Travel abroad and route all your traffic through your home IP — bypassing local internet restrictions. Single fully-unattended bash script: PiVPN, DuckDNS, fail2ban, iptables, cron. Zero interactive prompts. Works with any WireGuard client (travel router, phone, laptop).

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages