How to Self-Host WireGuard VPN: Access Your Homelab From Anywhere

How to Self-Host WireGuard VPN: Access Your Homelab From Anywhere

Set up your own WireGuard VPN server in Docker. Connect to your homelab securely from your phone, laptop, or anywhere — no paid VPN needed.

I got tired of paying for VPN services. Not because they’re bad — some are great — but because I already have a perfectly good server sitting at home running 24/7. Why pay someone else when I can run my own?

That’s when I switched to WireGuard. And honestly? Best networking decision I’ve made in years.

Why WireGuard?

Look, I tried OpenVPN first. It works, sure, but the config files are a nightmare. Hundreds of lines of XML-like gibberish that make you question your life choices. WireGuard’s config? Maybe 10 lines. Clean, simple, readable.

Plus it’s fast. Like, noticeably faster. OpenVPN would slow my connection to a crawl sometimes. WireGuard? I barely notice the overhead.

The protocol is built into the Linux kernel now (since 5.6), which means it’s battle-tested and efficient. Mobile clients are solid too — the iOS and Android apps just work.

What You’ll Need

  • A server with Docker (could be a VPS, a Raspberry Pi, your homelab box)
  • A domain name (optional but recommended for dynamic DNS)
  • Port 51820 open on your firewall/router
  • 10 minutes of your time

I’m running this on a tiny VPS with 1GB RAM. WireGuard barely uses any resources — it’s hilariously lightweight.

The Docker Setup

I tried installing WireGuard manually once. Once. Between kernel modules, iptables rules, and peer key management, I wanted to throw my laptop out the window.

Docker makes this painless. We’ll use the linuxserver/wireguard image — it handles all the annoying stuff automatically.

Create a directory for your WireGuard setup:

mkdir -p ~/wireguard/config
cd ~/wireguard

Now create a docker-compose.yml:

version: "3.8"

services:
  wireguard:
    image: lscr.io/linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Paris
      - SERVERURL=vpn.yourdomain.com  # or your server IP
      - SERVERPORT=51820
      - PEERS=laptop,phone,tablet  # client names
      - PEERDNS=auto
      - INTERNAL_SUBNET=10.13.13.0
      - ALLOWEDIPS=0.0.0.0/0
      - LOG_CONFS=true
    volumes:
      - ./config:/config
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped

Let me break down the important bits:

SERVERURL: This is how your clients will find the server. Use your domain name if you have one, or your server’s public IP. If you’re on a home connection with a dynamic IP, set up dynamic DNS first (I use Cloudflare for this).

PEERS: List your client devices here, separated by commas. The container will generate a config file for each one. I named mine descriptively — laptop, phone, etc. You’ll thank yourself later when you’re trying to figure out which peer is which.

INTERNAL_SUBNET: The internal VPN network range. I use 10.13.13.0 because… I like the number 13. Pick whatever private subnet you want that doesn’t conflict with your local network.

ALLOWEDIPS: Setting this to 0.0.0.0/0 routes ALL traffic through the VPN. If you only want to access your homelab and route everything else normally, set this to your homelab subnet instead (like 192.168.1.0/24).

NET_ADMIN and SYS_MODULE: These capabilities let the container configure network interfaces and load kernel modules. WireGuard needs these to work.

Fire It Up

docker-compose up -d

The first run takes a bit longer because it’s generating keys for all your peers. Watch the logs:

docker logs -f wireguard

You’ll see it creating configs, generating QR codes, the whole deal. When it’s done, you should see something like:

**** Peer laptop QR code ****
[QR CODE ASCII ART]

Those QR codes are gold. You can scan them directly with the WireGuard mobile apps.

Getting Your Client Configs

All the configs are in ./config/peer_laptop/, ./config/peer_phone/, etc.

For mobile devices, just scan the QR code:

docker exec wireguard /app/show-peer phone

For laptops/desktops, grab the config file:

cat ./config/peer_laptop/peer_laptop.conf

It’ll look something like this:

[Interface]
Address = 10.13.13.2/32
PrivateKey = <generated_private_key>
DNS = 10.13.13.1

[Peer]
PublicKey = <server_public_key>
Endpoint = vpn.yourdomain.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Clean, right? That’s the entire config.

Client Installation

Linux

sudo apt install wireguard  # Debian/Ubuntu
sudo dnf install wireguard-tools  # Fedora

Copy your peer config to /etc/wireguard/wg0.conf, then:

sudo wg-quick up wg0

To make it start on boot:

sudo systemctl enable wg-quick@wg0

macOS

Download the official WireGuard app from the App Store. Import your config file or paste the contents directly.

Windows

Same deal — download from wireguard.com. Import the config.

iOS/Android

Install the WireGuard app, tap the + button, scan the QR code. Done.

Testing the Connection

Once connected, check your IP:

curl ifconfig.me

You should see your server’s public IP, not your local ISP’s IP.

Test access to your homelab services. Can you reach your Jellyfin server? Your Nextcloud? If you can, you’re golden.

Common Issues (and How I Fixed Them)

Can’t Connect At All

First, check if the port is actually open:

sudo netstat -tulpn | grep 51820

If you don’t see anything listening on 51820, the container might not be running properly. Check docker logs wireguard.

On your router, make sure UDP port 51820 is forwarded to your server. TCP won’t work — WireGuard is UDP-only.

Connected But Can’t Access Anything

This one got me the first time. The server needs IP forwarding enabled. Check:

sysctl net.ipv4.ip_forward

If it returns 0, enable it:

echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Restart the container:

docker-compose restart

Slow Speeds

Try lowering the MTU. Edit your client config and add this under [Interface]:

MTU = 1420

Or go even lower (1380) if you’re still having issues. This usually happens with certain ISPs that do weird packet stuff.

Handshake Fails

Check your system time. Seriously. WireGuard is picky about time sync. If your server’s clock is off by more than a few minutes, handshakes will fail.

sudo timedatectl set-ntp true

Also double-check your SERVERURL matches what the client is trying to connect to. If you changed your server IP and forgot to update the client configs, that’s your problem.

Split Tunneling (The Smart Way)

Routing all traffic through your VPN is cool, but sometimes you don’t need it. Maybe you only want to access your homelab and keep Netflix traffic local (some streaming services block VPN IPs).

Edit your client config’s AllowedIPs:

AllowedIPs = 10.13.13.0/24, 192.168.1.0/24

This routes only your VPN subnet and your homelab subnet through WireGuard. Everything else uses your local connection.

I do this on my phone — I only need VPN access when I’m checking my services, not when I’m doom-scrolling Twitter.

Adding More Peers Later

Need to add a new device? Easy. Edit your docker-compose.yml and add the new peer name to the PEERS list:

- PEERS=laptop,phone,tablet,new-device

Restart:

docker-compose up -d

Grab the new config:

docker exec wireguard /app/show-peer new-device

Security Thoughts

WireGuard uses state-of-the-art cryptography (Curve25519, ChaCha20, Poly1305). The protocol has been audited multiple times. It’s solid.

That said, a few things to keep in mind:

Don’t share your private keys. Each peer has its own private key. Keep it secret. If someone gets your key, they can impersonate your device.

Firewall your server. Just because WireGuard is secure doesn’t mean you should expose everything. Use a firewall (ufw, iptables, whatever) to restrict access to only what you need.

Rotate keys periodically. I regenerate my configs every 6 months or so. Paranoid? Maybe. But it’s easy enough to do.

Monitor connected peers. Check who’s connected:

docker exec wireguard wg show

If you see an unknown peer, revoke it immediately.

Dynamic DNS (If Your IP Changes)

Most home ISPs give you a dynamic IP that changes occasionally. If that’s you, set up dynamic DNS so your VPN clients can always find your server.

I use Cloudflare with ddclient:

sudo apt install ddclient

Configure it to update your DNS record whenever your IP changes. Or use your router’s built-in DDNS support if it has one.

Then use your domain name (like vpn.yourdomain.com) in your WireGuard configs instead of an IP.

What About IPv6?

WireGuard supports IPv6 out of the box. If your server and ISP both support it, you can add IPv6 addresses to your configs.

I haven’t bothered. IPv4 works fine for my use case and adding IPv6 would just be more complexity I don’t need right now.

If you want to enable it, set INTERNAL_SUBNET to include an IPv6 range:

- INTERNAL_SUBNET=10.13.13.0,fd42:42:42::0/64

Performance Tweaks

WireGuard is already fast, but if you’re a nerd like me, you can squeeze out a bit more:

Use a faster DNS resolver. The default might be slow. Set PEERDNS to Cloudflare (1.1.1.1) or Quad9 (9.9.9.9).

Disable logging in production. Once everything works, set LOG_CONFS=false to reduce disk writes.

Run on a server geographically close to you. The speed of light is still a thing. A VPS in your region will be faster than one across the ocean.

Why I Don’t Use Commercial VPNs Anymore

Nothing against NordVPN or Mullvad or whoever. They’re great if you need anonymity or want to bypass geo-restrictions.

But for accessing my own stuff? Self-hosting a VPN is cheaper, faster, and way more flexible. I control the logs (there are none), I control the routes, and I’m not sharing an exit IP with thousands of other users who might be doing shady stuff.

Plus, I trust my own server more than I trust a random VPN company. Call me paranoid.

Monitoring and Logs

Keep an eye on things:

docker exec wireguard wg show

This shows active connections, transferred data, and last handshake times.

If a peer shows a really old handshake time (like hours ago) but you think it’s connected, something’s wrong. Probably NAT issues or a firewall blocking keepalive packets.

You can also check Docker logs for connection events:

docker logs wireguard --tail 50

I set up Uptime Kuma to ping my VPN endpoint every minute. If it goes down, I get a notification. Overkill? Probably. But I like knowing my setup is solid.

Backup Your Configs

Seriously. Back up that ./config directory. It contains all your private keys and peer configs.

If you lose it, you’ll have to regenerate everything and reconfigure all your devices. Ask me how I know.

I rsync mine to a separate backup server:

rsync -avz ~/wireguard/config user@backup-server:/backups/wireguard/

The Bottom Line

WireGuard is stupid simple to set up, crazy fast, and incredibly reliable. I’ve been running mine for over a year with zero downtime (except when I accidentally rebooted the server without checking).

If you’re self-hosting anything at home and want secure remote access, stop paying for VPN subscriptions. Spend an hour setting this up instead.

Your homelab will thank you.


FAQ

Q: Can I use WireGuard to bypass geo-restrictions on streaming services?

Technically yes, but many streaming services actively block VPN traffic. If that’s your goal, you might have better luck with a commercial VPN that plays cat-and-mouse with Netflix’s blocklist.

Q: How many peers can I have?

As many as you want. I’ve seen people run 50+ peers on a single WireGuard server without issues. The protocol is lightweight enough that peer count isn’t usually the bottleneck — your server’s bandwidth is.

Q: Can I run WireGuard on a Raspberry Pi?

Absolutely. I ran mine on a Pi 4 for months before moving it to a VPS. Works great. Just make sure your Pi has a decent internet connection.

Q: What if I want to connect two networks together (site-to-site)?

WireGuard can do that too. Instead of individual devices, you’d set up WireGuard on routers/gateways at each location and route traffic between the networks. It’s more complex but totally doable.

Q: Is WireGuard legal everywhere?

In most places, yes. Some countries (China, Russia, UAE) restrict or block VPN usage. Check your local laws. I’m not a lawyer.

Q: Can I use WireGuard alongside other VPNs?

Technically yes, but it gets messy. Routing tables can conflict. I wouldn’t recommend it unless you really know what you’re doing with network namespaces and policy routing.

Q: How do I remove a peer?

Edit your docker-compose.yml, remove the peer from the PEERS list, and restart. The config will be cleaned up automatically. If you’re paranoid, manually delete the peer’s folder from ./config/.

Q: Why UDP instead of TCP?

UDP is faster and better suited for VPN traffic. TCP-over-TCP (if WireGuard used TCP) causes performance issues because both layers try to handle packet loss independently. It’s a whole thing. Trust me, UDP is the right choice.

Q: Can I run this without Docker?

Sure. Install WireGuard directly, write your configs manually, set up iptables rules, manage peer keys yourself. It’s not that hard, just more tedious. Docker handles all the boilerplate for you.

Q: What happens if my server IP changes and I’m using a static IP in my client configs?

Your VPN stops working until you update the configs. This is why I recommend using a domain name with dynamic DNS. Then your IP can change all it wants — DNS updates automatically and clients keep working.

Stay in the loop 📬

Get self-hosting tutorials, tool reviews, and infrastructure tips delivered to your inbox. No spam, unsubscribe anytime.

Join 0 self-hosters. Free forever.